diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/AdvancedNewFile.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/AdvancedNewFile.py index 164d7de..6632e99 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/AdvancedNewFile.py +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/AdvancedNewFile.py @@ -20,12 +20,19 @@ SETTINGS = [ "alias_path", "alias_folder_index", "debug", - "auto_refresh_sidebar" + "auto_refresh_sidebar", + "completion_type", + "complete_single_entry", + "use_folder_name", + "relative_from_current", + "default_extension" ] VIEW_NAME = "AdvancedNewFileCreation" WIN_ROOT_REGEX = r"[a-zA-Z]:(/|\\)" NIX_ROOT_REGEX = r"^/" HOME_REGEX = r"^~" +PLATFORM = sublime.platform().lower() +IS_ST3 = int(sublime.version()) > 3000 # Set up logger logging.basicConfig(format='[AdvancedNewFile] %(levelname)s %(message)s') @@ -33,8 +40,8 @@ logger = logging.getLogger() class AdvancedNewFileCommand(sublime_plugin.WindowCommand): - def run(self, is_python=False): - self.PLATFORM = sublime.platform().lower() + def run(self, is_python=False, initial_path=None): + PLATFORM = sublime.platform().lower() self.root = None self.alias_root = None self.top_level_split_char = ":" @@ -42,37 +49,34 @@ class AdvancedNewFileCommand(sublime_plugin.WindowCommand): self.view = self.window.active_view() # Settings will be based on the view - settings = get_settings(self.view) - self.aliases = self.get_aliases(settings) - self.show_path = settings.get("show_path") - self.auto_refresh_sidebar = settings.get("auto_refresh_sidebar") - self.default_folder_index = settings.get("default_folder_index") - self.alias_folder_index = settings.get("alias_folder_index") - default_root = self.get_default_root(settings.get("default_root")) + self.settings = get_settings(self.view) + self.aliases = self.get_aliases() + self.show_path = self.settings.get("show_path") + self.default_folder_index = self.settings.get("default_folder_index") + self.alias_folder_index = self.settings.get("alias_folder_index") + default_root = self.get_default_root(self.settings.get("default_root")) if default_root == "path": - self.root = os.path.expanduser(settings.get("default_path")) + self.root = os.path.expanduser(self.settings.get("default_path")) default_root = "" self.root, path = self.split_path(default_root) - # Set some default values for the auto complete - PathAutocomplete.set_show_files(settings.get("show_files")) - PathAutocomplete.set_aliases(self.aliases) - PathAutocomplete.set_ignore_case(settings.get("ignore_case")) - # Search for initial string - path = settings.get("default_initial", "") - if settings.get("use_cursor_text", False): - tmp = self.get_cursor_path() - if tmp != "": - path = tmp + if initial_path is not None: + path = initial_path + else: + path = self.settings.get("default_initial", "") + if self.settings.get("use_cursor_text", False): + tmp = self.get_cursor_path() + if tmp != "": + path = tmp - alias_root = self.get_default_root(settings.get("alias_root"), True) + alias_root = self.get_default_root(self.settings.get("alias_root"), True) if alias_root == "path": - self.alias_root = os.path.expanduser(settings.get("alias_path")) + self.alias_root = os.path.expanduser(self.settings.get("alias_path")) alias_root = "" self.alias_root, tmp = self.split_path(alias_root, True) - debug = settings.get("debug") or False + debug = self.settings.get("debug") or False if debug: logger.setLevel(logging.DEBUG) else: @@ -80,12 +84,12 @@ class AdvancedNewFileCommand(sublime_plugin.WindowCommand): # Get user input self.show_filename_input(path) - def get_aliases(self, settings): - aliases = settings.get("alias") - all_os_aliases = settings.get("os_specific_alias") + def get_aliases(self): + aliases = self.settings.get("alias") + all_os_aliases = self.settings.get("os_specific_alias") for key in all_os_aliases: - if self.PLATFORM in all_os_aliases.get(key): - aliases[key] = all_os_aliases.get(key).get(self.PLATFORM) + if PLATFORM in all_os_aliases.get(key): + aliases[key] = all_os_aliases.get(key).get(PLATFORM) return aliases @@ -122,7 +126,7 @@ class AdvancedNewFileCommand(sublime_plugin.WindowCommand): root = None try: # Parse windows root - if self.PLATFORM == "windows": + if PLATFORM == "windows": if re.match(WIN_ROOT_REGEX, path): root = path[0:3] path = path[3:] @@ -137,10 +141,30 @@ class AdvancedNewFileCommand(sublime_plugin.WindowCommand): if parts[1] != "": path_list.append(parts[1]) path = self.top_level_split_char.join(path_list) + elif re.match(r"^/", path): + path_offset = 1 + if PLATFORM == "windows": + match = re.match(r"^/([a-zA-Z])/", path) + if match: + root = "%s:\\" % match.group(1) + path_offset = 3 + else: + root, _ = os.path.splitdrive(self.view.file_name()) + root += "\\" + else: + root = "/" + path = path[path_offset:] # Parse if tilde used elif re.match(HOME_REGEX, path) and root == None: root = os.path.expanduser("~") path = path[2:] + elif re.match(r"^\.{1,2}[/\\]", path) and self.settings.get("relative_from_current", False): + path_index = 2 + root = os.path.dirname(self.view.file_name()) + if re.match(r"^\.{2}[/\\]", path): + root = os.path.dirname(root) + path_index = 3 + path = path[path_index:] # Default if root == None: @@ -153,6 +177,8 @@ class AdvancedNewFileCommand(sublime_plugin.WindowCommand): root = root or self.window.folders()[folder_index] except IndexError: root = os.path.expanduser("~") + + return root, path def translate_alias(self, path): @@ -169,9 +195,8 @@ class AdvancedNewFileCommand(sublime_plugin.WindowCommand): root_found = False while join_index >= 0 and not root_found: # Folder aliases - for folder in self.window.folders(): - basename = os.path.basename(folder) - if basename == target: + for name, folder in get_project_folder_data(self.settings.get("use_folder_name")): + if name == target: root = folder root_found = True break @@ -180,7 +205,7 @@ class AdvancedNewFileCommand(sublime_plugin.WindowCommand): if alias == target: alias_path = self.aliases.get(alias) if re.search(HOME_REGEX, alias_path) is None: - if self.PLATFORM == "windows": + if PLATFORM == "windows": if re.search(WIN_ROOT_REGEX, alias_path) is None: root = os.path.join(self.alias_root, alias_path) break @@ -208,51 +233,218 @@ class AdvancedNewFileCommand(sublime_plugin.WindowCommand): caption = 'Enter a path for a new file' if self.is_python: caption = '%s (creates __init__.py in new dirs)' % caption - view = self.window.show_input_panel( + self.input_panel_view = self.window.show_input_panel( caption, initial, self.entered_filename, self.update_filename_input, self.clear ) - view.set_name(VIEW_NAME) - temp = view.settings().get("word_separators") - temp = temp.replace(".", "") - view.settings().set("word_separators", temp) - view.settings().set("auto_complete_commit_on_tab", True) - view.settings().set("tab_completion", True) - - PathAutocomplete.set_view_id(view.id()) - PathAutocomplete.set_root(self.root, True) + self.input_panel_view.set_name(VIEW_NAME) + self.input_panel_view.settings().set("auto_complete_commit_on_tab", False) + self.input_panel_view.settings().set("tab_completion", False) + self.input_panel_view.settings().set("translate_tabs_to_spaces", False) + self.input_panel_view.settings().set("anf_panel", True) def update_filename_input(self, path_in): - base, path = self.split_path(path_in) - if self.top_level_split_char in path_in or re.match(r"^~[/\\]", path_in): - PathAutocomplete.set_root(base, False) - else: - PathAutocomplete.set_root(base, True) + if self.settings.get("completion_type") == "windows": + if "prev_text" in dir(self) and self.prev_text != path_in: + if self.view is not None: + self.view.erase_status("AdvancedNewFile2") + if path_in.endswith("\t"): + path_in = path_in.replace("\t", "") + if self.settings.get("completion_type") == "windows": + path_in = self.windows_completion(path_in) + elif self.settings.get("completion_type") == "nix": + path_in = self.nix_completion(path_in) - creation_path = self.generate_creation_path(base, path) + base, path = self.split_path(path_in) + + creation_path = self.generate_creation_path(base, path, True) if self.show_path: if self.view != None: self.view.set_status("AdvancedNewFile", "Creating file at %s " % \ creation_path) else: - sublime.status_message("Creating file at %s " % creation_path) + sublime.status_message("Creating file at %s" % creation_path) logger.debug("Creation path is '%s'" % creation_path) - PathAutocomplete.set_path(path) - def generate_creation_path(self, base, path): - if self.PLATFORM == "windows": + def generate_completion_list(self, path_in, each_list=False): + alias_list = [] + dir_list = [] + file_list = [] + self.suggestion_entries = [] + if self.top_level_split_char in path_in or re.match(r"^~[/\\]", path_in): + pass + else: + directory, filename = os.path.split(path_in) + if len(directory) == 0: + alias_list += self.generate_alias_auto_complete(filename) + alias_list += self.generate_project_auto_complete(filename) + base, path = self.split_path(path_in) + full_path = self.generate_creation_path(base, path) + + directory, filename = os.path.split(full_path) + + if os.path.isdir(directory): + for d in os.listdir(directory): + full_path = os.path.join(directory, d) + if os.path.isdir(full_path): + is_file = False + elif self.settings.get("show_files"): + is_file = True + else: + continue + + if self.compare_entries(d, filename): + if is_file: + file_list.append(d) + else: + dir_list.append(d) + + completion_list = alias_list + dir_list + file_list + + return sorted(completion_list), alias_list, dir_list, file_list + + def windows_completion(self, path_in): + pattern = r"(.*[/\\:])(.*)" + match = re.match(pattern, path_in) + if "prev_text" in dir(self) and self.prev_text == path_in: + self.offset = (self.offset + 1) % len(self.completion_list) + else: + # Generate new completion list + self.completion_list, self.alias_list, self.dir_list, self.file_list = self.generate_completion_list(path_in) + self.offset = 0 + + if len(self.completion_list) == 0: + if match: + self.completion_list = [match.group(2)] + else: + self.completion_list = [path_in] + match = re.match(pattern, path_in) + if match : + completion = self.completion_list[self.offset] + if self.settings.get("complete_single_entry"): + if len(self.completion_list) == 1: + if completion in self.alias_list: + completion += ":" + elif completion in self.dir_list: + completion += "/" + new_content = re.sub(pattern, r"\1" , path_in) + new_content += completion + first_token = False + else: + completion = self.completion_list[self.offset] + if self.settings.get("complete_single_entry"): + if len(self.completion_list) == 1: + if completion in self.alias_list: + completion += ":" + elif completion in self.dir_list: + completion += "/" + new_content = completion + first_token = True + + if len(self.completion_list) > 1: + if first_token: + if self.view is not None: + if self.completion_list[self.offset] in self.alias_list: + self.view.set_status("AdvancedNewFile2", "Alias Completion") + elif self.completion_list[self.offset] in self.dir_list: + self.view.set_status("AdvancedNewFile2", "Directory Completion") + self.prev_text = new_content + else: + self.prev_text = None + self.input_panel_view.run_command("anf_replace", {"content": new_content}) + return new_content + + def nix_completion(self, path_in): + pattern = r"(.*[/\\:])(.*)" + + completion_list, alias_list, dir_list, file_list = self.generate_completion_list(path_in) + new_content = path_in + if len(completion_list) > 0: + common = os.path.commonprefix(completion_list) + match = re.match(pattern, path_in) + if match : + new_content = re.sub(pattern, r"\1", path_in) + new_content += common + else: + new_content = common + if len(completion_list) > 1: + dir_list = map(lambda s: s + "/", dir_list) + alias_list = map(lambda s: s + ":", alias_list) + status_message_list = sorted(list(dir_list) + list(alias_list) + file_list) + sublime.status_message(", ".join(status_message_list)) + else: + if completion_list[0] in alias_list: + new_content += ":" + elif completion_list[0] in dir_list: + new_content += "/" + self.input_panel_view.run_command("anf_replace", {"content": new_content}) + return new_content + + def generate_project_auto_complete(self, base): + folder_data = get_project_folder_data(self.settings.get("use_folder_name")) + if len(folder_data) > 1: + folders = [x[0] for x in folder_data] + return self.generate_auto_complete(base, folders) + return [] + + def generate_alias_auto_complete(self, base): + return self.generate_auto_complete(base, self.aliases) + + def generate_auto_complete(self, base, iterable_var): + sugg = [] + for entry in iterable_var: + if entry in self.suggestion_entries: + continue + self.suggestion_entries.append(entry) + compare_entry = entry + compare_base = base + if self.settings.get("ignore_case"): + compare_entry = compare_entry.lower() + compare_base = compare_base.lower() + + if self.compare_entries(compare_entry, compare_base): + sugg.append(entry) + + return sugg + + def compare_entries(self, compare_entry, compare_base): + if self.settings.get("ignore_case"): + compare_entry = compare_entry.lower() + compare_base = compare_base.lower() + + return compare_entry.startswith(compare_base) + + + def generate_creation_path(self, base, path, append_extension=False): + if PLATFORM == "windows": if not re.match(WIN_ROOT_REGEX, base): return base + self.top_level_split_char + path else: if not re.match(NIX_ROOT_REGEX, base): return base + self.top_level_split_char + path - return os.path.abspath(os.path.join(base, path)) + tokens = re.split(r"[/\\]", base) + re.split(r"[/\\]", path) + if tokens[0] == "": + tokens[0] = "/" + if PLATFORM == "windows": + tokens[0] = base[0:3] + + full_path = os.path.abspath(os.path.join(*tokens)) + if re.search(r"[/\\]$", path) or len(path) == 0: + full_path += os.path.sep + elif re.search(r"\.", tokens[-1]): + if re.search(r"\.$", tokens[-1]): + full_path += "." + elif append_extension: + filename = os.path.basename(full_path) + if not os.path.exists(full_path): + full_path += self.settings.get("default_extension", "") + return full_path def entered_filename(self, filename): # Check if valid root specified for windows. - if self.PLATFORM == "windows": + if PLATFORM == "windows": if re.match(WIN_ROOT_REGEX, filename): root = filename[0:3] if not os.path.isdir(root): @@ -261,11 +453,11 @@ class AdvancedNewFileCommand(sublime_plugin.WindowCommand): return base, path = self.split_path(filename) - file_path = os.path.join(base, path) + file_path = self.generate_creation_path(base, path, True) # Check for invalid alias specified. if self.top_level_split_char in filename and \ - not (self.PLATFORM == "windows" and re.match(WIN_ROOT_REGEX, base)) and \ - not (self.PLATFORM != "windows" and re.match(NIX_ROOT_REGEX, base)): + not (PLATFORM == "windows" and re.match(WIN_ROOT_REGEX, base)) and \ + not (PLATFORM != "windows" and re.match(NIX_ROOT_REGEX, base)): if base == "": error_message = "Current file cannot be resolved." else: @@ -291,17 +483,16 @@ class AdvancedNewFileCommand(sublime_plugin.WindowCommand): self.refresh_sidebar() def refresh_sidebar(self): - if self.auto_refresh_sidebar: + if self.settings.get("auto_refresh_sidebar"): try: self.window.run_command("refresh_folder_list") except: pass - def clear(self): if self.view != None: self.view.erase_status("AdvancedNewFile") - PathAutocomplete.clear() + self.view.erase_status("AdvancedNewFile2") def create(self, filename): base, filename = os.path.split(filename) @@ -310,13 +501,21 @@ class AdvancedNewFileCommand(sublime_plugin.WindowCommand): open(os.path.join(base, filename), "a").close() def create_folder(self, path): + init_list = [] + if self.is_python: + temp_path = path + while not os.path.exists(temp_path): + init_list.append(temp_path) + temp_path = os.path.dirname(temp_path) try: - os.makedirs(path) + if not os.path.exists(path): + os.makedirs(path) except OSError as ex: if ex.errno != errno.EEXIST: raise - if self.is_python: - open(os.path.join(base, '__init__.py'), 'a').close() + + for entry in init_list: + open(os.path.join(entry, '__init__.py'), 'a').close() def get_cursor_path(self): if self.view == None: @@ -325,7 +524,7 @@ class AdvancedNewFileCommand(sublime_plugin.WindowCommand): view = self.view path = "" for region in view.sel(): - syntax = view.syntax_name(region.begin()) + syntax = view.scope_name(region.begin()) if region.begin() != region.end(): path = view.substr(region) break @@ -337,226 +536,22 @@ class AdvancedNewFileCommand(sublime_plugin.WindowCommand): return path -class PathAutocomplete(sublime_plugin.EventListener): - aliases = {} - show_files = False - ignore_case = False +class AnfReplaceCommand(sublime_plugin.TextCommand): + def run(self, edit, content): + self.view.replace(edit, sublime.Region(0, self.view.size()), content) - path = "" - root = "" - default_root = True - view_id = None - prev_suggestions = [] - prev_base = "" - prev_directory = "" - path_empty = True - prev_root = "" - prev_prefix = "" - prev_locations = [] +class AdvancedNewFileAtCommand(sublime_plugin.WindowCommand): + def run(self, dirs): + if len(dirs) != 1: + return + path = dirs[0] + self.window.run_command("advanced_new_file", {"initial_path": path + os.sep}) - def on_query_context(self, view, key, operator, operand, match_all): - if key == "advanced_new_file_completion" and PathAutocomplete.view_id != None and view.id() == PathAutocomplete.view_id: - return True - return None - def continue_previous_autocomplete(self): - pac = PathAutocomplete - sep = os.sep - root_path = pac.root + sep - prev_base = pac.prev_base - prev_directory = pac.prev_directory - prev_root = pac.prev_root - - base = os.path.basename(pac.path) - directory = os.path.dirname(pac.path) - - # If base is empty, we may be cycling through directory options - if base == "": - return True - - # Ensures the correct directory is used if the default root is specified - # using an alias. - if base == prev_base and \ - directory == prev_directory and \ - prev_root == root_path and \ - pac.default_root: - return True - # Continue completions if file names are completed. - if os.path.isfile(os.path.join(root_path, pac.path)): - return True - return False - - def on_query_completions(self, view, prefix, locations): - self.suggestion_entries = [] - pac = PathAutocomplete - if pac.view_id == None or view.id() != pac.view_id: - return [] - - auto_complete_prefix = "" - if self.continue_previous_autocomplete() and prefix != "": - logger.debug("(Prev) Suggestions") - logger.debug(pac.prev_suggestions) - if len(pac.prev_suggestions) > 1: - return (pac.prev_suggestions, sublime.INHIBIT_WORD_COMPLETIONS | sublime.INHIBIT_EXPLICIT_COMPLETIONS) - elif len(pac.prev_suggestions) == 1: - auto_complete_prefix = pac.prev_suggestions[0][1] - - suggestions = [] - suggestions_w_spaces = [] - root_path = pac.root + os.sep - directory, base = os.path.split(pac.path) - - if directory == "" and pac.default_root: - # Project folders - sugg, sugg_w_spaces = self.generate_project_auto_complete(base) - suggestions += sugg - suggestions_w_spaces += sugg_w_spaces - # Aliases - sugg, sugg_w_spaces = self.generate_alias_auto_complete(base) - suggestions += sugg - suggestions_w_spaces += sugg_w_spaces - - # Directories - path = os.path.join(root_path, directory) - if os.path.exists(path): - sugg, sugg_w_spaces = self.generate_relative_auto_complete(path, base, auto_complete_prefix) - suggestions += sugg - suggestions_w_spaces += sugg_w_spaces - # If suggestions exist, use complete name - # else remove base prefix - if len(suggestions) > 0: - for name in suggestions_w_spaces: - suggestions.append((" " + name, name)) - else: - for name in suggestions_w_spaces: - temp = name - name = name[len(base) - 1:] - suggestions.append((" " + temp, name)) - - if len(suggestions) == 0 and locations == pac.prev_locations: - return (pac.prev_suggestions, sublime.INHIBIT_WORD_COMPLETIONS | sublime.INHIBIT_EXPLICIT_COMPLETIONS) - # Previous used to determine cycling through entries. - pac.prev_directory = directory - pac.prev_base = base - pac.prev_suggestions = suggestions - pac.prev_root = root_path - pac.prev_prefix = prefix - pac.prev_locations = locations - logger.debug("Suggestions:") - logger.debug(suggestions) - return (suggestions, sublime.INHIBIT_WORD_COMPLETIONS | sublime.INHIBIT_EXPLICIT_COMPLETIONS) - - def generate_project_auto_complete(self, base): - folders = sublime.active_window().folders() - if len(folders) > 1: - folders = map(lambda f: os.path.basename(f), folders) - return self.generate_auto_complete(base, folders) - return [], [] - - def generate_alias_auto_complete(self, base): - return self.generate_auto_complete(base, PathAutocomplete.aliases) - - def generate_auto_complete(self, base, iterable_var): - sugg = [] - sugg_w_spaces = [] - - for entry in iterable_var: - if entry in self.suggestion_entries: - continue - self.suggestion_entries.append(entry) - compare_entry = entry - compare_base = base - if PathAutocomplete.ignore_case: - compare_entry = compare_entry.lower() - compare_base = compare_base.lower() - - if compare_entry.find(compare_base) == 0: - if " " in base: - sugg_w_spaces.append(entry + ":") - else: - sugg.append((entry + ":", entry + ":")) - return sugg, sugg_w_spaces - - def generate_relative_auto_complete(self, path, base, auto_complete_prefix): - sep = os.sep - sugg = [] - sugg_w_spaces = [] - - # Attempt to prevent searching the same path when a path has been specified - # Problems occur when using tab to complete entry with single completion - # followed by ctrl + space - if ":" in auto_complete_prefix: - compare_prefix = auto_complete_prefix.split(":", 1)[1] - else: - compare_prefix = auto_complete_prefix - - if re.search(r"[/\\]$", auto_complete_prefix) and not path.endswith(compare_prefix[0:-1]): - path = os.path.join(path, compare_prefix) - - for filename in os.listdir(path): - if PathAutocomplete.show_files or os.path.isdir(os.path.join(path, filename)): - compare_base = base - compare_filename = filename - if PathAutocomplete.ignore_case: - compare_base = compare_base.lower() - compare_filename = filename.lower() - - if compare_filename.find(compare_base) == 0: - # Need to find a better way to do the auto complete. - if " " in compare_base: - if os.path.isdir(os.path.join(path, filename)): - sugg_w_spaces.append(auto_complete_prefix + filename + sep) - else: - sugg_w_spaces.append(auto_complete_prefix + filename) - else: - if os.path.isdir(os.path.join(path, filename)): - sugg.append((" " + auto_complete_prefix + filename + sep, auto_complete_prefix + filename + sep)) - else: - sugg.append((" " + auto_complete_prefix + filename, auto_complete_prefix + filename)) - - return sugg, sugg_w_spaces - - @staticmethod - def set_path(path_input): - PathAutocomplete.path = path_input - - @staticmethod - def set_root(root_input, default_root): - PathAutocomplete.root = root_input - PathAutocomplete.default_root = default_root - - @staticmethod - def clear(): - PathAutocomplete.path = "" - PathAutocomplete.root = "" - PathAutocomplete.prev_suggestions = [] - PathAutocomplete.prev_base = "" - PathAutocomplete.prev_directory = "" - PathAutocomplete.aliases = {} - PathAutocomplete.path_empty = True - PathAutocomplete.prev_root = "" - PathAutocomplete.default_root = True - PathAutocomplete.show_files = False - PathAutocomplete.prev_prefix = "" - PathAutocomplete.prev_locations = [] - PathAutocomplete.view_id = None - - @staticmethod - def set_aliases(aliases): - PathAutocomplete.aliases = aliases - - @staticmethod - def set_show_files(show_files): - PathAutocomplete.show_files = show_files - - @staticmethod - def set_ignore_case(ignore_case): - PathAutocomplete.ignore_case = ignore_case - - @staticmethod - def set_view_id(view_id): - PathAutocomplete.view_id = view_id + def is_visible(self, dirs): + settings = sublime.load_settings("AdvancedNewFile.sublime-settings") + return settings.get("show_sidebar_menu", False) and len(dirs) == 1 def get_settings(view): @@ -579,3 +574,31 @@ def get_settings(view): logger.error("AdvancedNewFile[Warning]: Invalid key '%s' in project settings.", key) return local_settings + +def get_project_folder_data(use_folder_name): + folders = [] + folder_entries = [] + window = sublime.active_window() + project_folders = window.folders() + + if IS_ST3: + project_data = window.project_data() + + if project_data is not None: + if use_folder_name: + for folder in project_data.get("folders", []): + folder_entries.append({}) + else: + folder_entries = project_data.get("folders", []) + else: + for folder in project_folders: + folder_entries.append({}) + for index in range(len(folder_entries)): + folder_path = project_folders[index] + folder_entry = folder_entries[index] + if "name" in folder_entry: + folders.append((folder_entry["name"], folder_path)) + else: + folders.append((os.path.basename(folder_path), folder_path)) + + return folders diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/AdvancedNewFile.sublime-settings b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/AdvancedNewFile.sublime-settings index 76cdf33..af15f63 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/AdvancedNewFile.sublime-settings +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/AdvancedNewFile.sublime-settings @@ -19,7 +19,7 @@ // A boolean defining if cursor text should be used. Text bound by single or // double quotes or within a region will be used. If multiple cursors - // are used, the earliest selection containing a region or existing + // are used, the earliest selection containing a region or existing // within quotes will be used. // NOTE: A value read from cursor will override the default // initial string setting. @@ -55,7 +55,7 @@ // This value specifies the root that will be used when resolving relative paths // defined in aliases. For more information about valid values, see "default_root". - // Note that if "default_path" or "default_folder_index" is used, + // Note that if "default_path" or "default_folder_index" is used, // "alias_path" and "alias_folder_index" must be used for the respective entries. "alias_root": "current", @@ -76,5 +76,29 @@ // In some builds, the sidebar does not refresh when contents of project folder are updated. // This setting is required to refresh the sidebar in these circumstances. // false by default - "auto_refresh_sidebar": false -} \ No newline at end of file + "auto_refresh_sidebar": false, + + // A boolean specifying if an AdvancedNewFile option should be shown in the + // sidebar context menu. + "show_sidebar_menu": false, + + // A string specifying the type of auto completion to use. Valid values are + // "windows" or "nix" + "completion_type": "windows", + + // A boolean setting specifying if a separator should be inserted when + // there is only one completion and completion type is "windows" + "complete_single_entry": true, + + // A boolean setting specifying if the folder name should be used + // or the name specified in the project. This setting only applies to ST3. + "use_folder_name": false, + + // Boolean setting specifying if relative paths should be based on the + // current working directory. + "relative_from_current": true, + + // String containing the default file extension. Note the extension is only applied + // if the specified path does not contain a dot (.) character. + "default_extension": "" +} diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/CHANGELOG.md b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/CHANGELOG.md index 55b5f3a..1eefb08 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/CHANGELOG.md +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/CHANGELOG.md @@ -1,6 +1,33 @@ # Changelog for AdvancedNewFile +- 9 September 2013 + - Bug fix for folder creation. + - Bug fix for permission issue. + - Add default extension setting. + +- 2 September 2013 + - Add setting to begin all relative paths from current working directory if available. + +- 14 August 2013 + - Prompt completion type for first token when using Windows completion. + - Fix bug with path autocompletion. + - Fix bug for tab completion with no view. + +- 27 July 2013 + - Rewrite autocomplete functionality. + - Bug Fixes + - Snippets no longer appear when entering completions. + +- 22 April 2013 + - Add option to refresh sidebar after creating a file. + - Add side bar context menu. + - Bug Fixes + - Multiple autocomplete issues. + - Creation of __init__.py files. + - Filling text with cursor values. + - 2 February 2013 - Update to be compatible with Sublime Text 3. + - 14 January 2013 - Add `alias_root` setting, used with aliases with relative paths. - Add setting to allow user to specify which folder from the project should be used. diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Default (Linux).sublime-keymap b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Default (Linux).sublime-keymap index 2ea7bc1..3d9daa9 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Default (Linux).sublime-keymap +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Default (Linux).sublime-keymap @@ -3,13 +3,10 @@ { "keys": ["shift+super+alt+n"], "command": "advanced_new_file", "args": {"is_python": true}}, { "keys": ["tab"], - "command": "insert_best_completion", - "args": {"default": "", "exact": false}, - "context": [ - { "key": "advanced_new_file_completion"}, - { "key": "setting.tab_completion", "operator": "equal", "operand": true }, - { "key": "last_command", "operator": "not_equal", "operand": "insert_best_completion" }, - { "key": "auto_complete_visible" , "operator": "equal", "operand": false} - ] + "command": "insert", + "args": {"characters": "\t"}, + "context": [{ + "key": "setting.anf_panel" + }] } ] \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Default (OSX).sublime-keymap b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Default (OSX).sublime-keymap index 2ea7bc1..3d9daa9 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Default (OSX).sublime-keymap +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Default (OSX).sublime-keymap @@ -3,13 +3,10 @@ { "keys": ["shift+super+alt+n"], "command": "advanced_new_file", "args": {"is_python": true}}, { "keys": ["tab"], - "command": "insert_best_completion", - "args": {"default": "", "exact": false}, - "context": [ - { "key": "advanced_new_file_completion"}, - { "key": "setting.tab_completion", "operator": "equal", "operand": true }, - { "key": "last_command", "operator": "not_equal", "operand": "insert_best_completion" }, - { "key": "auto_complete_visible" , "operator": "equal", "operand": false} - ] + "command": "insert", + "args": {"characters": "\t"}, + "context": [{ + "key": "setting.anf_panel" + }] } ] \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Default (Windows).sublime-keymap b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Default (Windows).sublime-keymap index 11a180a..fe13333 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Default (Windows).sublime-keymap +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Default (Windows).sublime-keymap @@ -1,16 +1,12 @@ [ { "keys": ["ctrl+alt+n"], "command": "advanced_new_file"}, { "keys": ["shift+ctrl+alt+n"], "command": "advanced_new_file", "args": {"is_python": true}}, - // Forces insert best autocomplete to run for advanced new file { "keys": ["tab"], - "command": "insert_best_completion", - "args": {"default": "", "exact": false}, - "context": [ - { "key": "advanced_new_file_completion"}, - { "key": "setting.tab_completion", "operator": "equal", "operand": true }, - { "key": "last_command", "operator": "not_equal", "operand": "insert_best_completion" }, - { "key": "auto_complete_visible" , "operator": "equal", "operand": false} - ] + "command": "insert", + "args": {"characters": "\t"}, + "context": [{ + "key": "setting.anf_panel" + }] } ] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/LICENSE.txt b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/LICENSE.txt index b086861..90b631c 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/LICENSE.txt +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/LICENSE.txt @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2012 AdvancedNewFile authors +Copyright (c) 2013 AdvancedNewFile authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/README.md b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/README.md index d128cbf..19158a2 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/README.md +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/README.md @@ -22,6 +22,12 @@ Clone or copy this repository into the packages directory. By default, they are * Windows: %APPDATA%/Roaming/Sublime Text 2/Packages/ * Linux: ~/.config/sublime-text-2/Packages/ +or + +* OS X: ~/Library/Application Support/Sublime Text 3/Packages/ +* Windows: %APPDATA%/Roaming/Sublime Text 3/Packages/ +* Linux: ~/.config/sublime-text-3/Packages/ + ## Usage Simply bring up the AdvancedNewFile input through the appropriate [key binding](https://github.com/skuroda/Sublime-AdvancedNewFile). Then, enter the path, along with the file name into the input field. Upon pressing enter, the file will be created. In addition, if the directories specified do not yet exists, they will also be created. For more advanced usage of this plugin, be sure to look at [Advanced Path Usage](https://github.com/skuroda/Sublime-AdvancedNewFile#advanced-path-usage). By default, the path to the file being created will be filled shown in the status bar as you enter the path information. @@ -107,13 +113,35 @@ A boolean specifying if case should be ignored when building auto complete list. A boolean specifying if folders should automatically refresh and update the sidebar. In some builds, the sidebar does not refresh when contents of project folder are updated. This setting is required to refresh the sidebar in these circumstances. False by default. +`show_sidebar_menu`: + +A boolean specifying if an AdvancedNewFile option should be shown in the sidebar context menu. + +`completion_type`: + +A string specifying the type of auto completion to use. Valid values are "windows" or "nix". + +`complete_single_entry` + +A boolean setting specifying if a separator should be inserted when there is only one completion and completion type is "windows" + +`use_folder_name`: + +A boolean setting specifying if the folder name should be used or the name specified in the project. This setting only applies to ST3. + +`relative_from_current`: + +Boolean setting specifying if relative paths should be based on the current working directory. + +`default_extension`: + +String containing the default file extension. Note the extension is only applied if the specified path does not contain a dot (.) character. + ### Project Specific Settings All of the above settings can also be specified as part of the project specific settings. These values override any previous values set by higher level settings, with aliases being an exception. Alias settings will be merged with higher level configurations for alias. In addition, if the same alias exist for both default/user settings and project settings, the project setting will take precedence. - "settings": - { - "AdvancedNewFile": - { + "settings": { + "AdvancedNewFile": { "default_initial": "/project/specific/path" } } @@ -154,7 +182,7 @@ Sample OS Specific Aliases: { "os_specific_alias": { - "subl_packages" { + "subl_packages": { "windows": "~\\AppData\\Roaming\\Sublime Text 2\\Packages", "linux": "~/.config/sublime-text-2/Packages", "osx": "~/Library/Application Support/Sublime Text 2/Packages" diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Side Bar.sublime-menu b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Side Bar.sublime-menu new file mode 100644 index 0000000..dc104de --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Side Bar.sublime-menu @@ -0,0 +1,3 @@ +[ + { "caption": "Advanced New File", "command": "advanced_new_file_at", "args": {"dirs": []} } +] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages.json index 5fb2ced..9801895 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages.json +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages.json @@ -3,5 +3,8 @@ "2012.11.08.20.00.00": "messages/1.txt", "2012.11.12.11.00.00": "messages/2.txt", "2012.11.26.11.00.00": "messages/3.txt", - "2012.12.17.11.00.00": "messages/4.txt" -} \ No newline at end of file + "2012.12.17.11.00.00": "messages/4.txt", + "2013.07.29.11.00.00": "messages/5.txt", + "2013.08.05.11.00.00": "messages/6.txt", + "2013.09.03.11.00.00": "messages/7.txt" +} diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/5.txt b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/5.txt new file mode 100644 index 0000000..ac23414 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/5.txt @@ -0,0 +1,4 @@ +- Rewrite autocomplete functionality to support *nix or windows style completions. Please see the README for details about new settings. + +Bug Fixes: +- Snippets no longer appear when entering completions. diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/6.txt b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/6.txt new file mode 100644 index 0000000..66277ec --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/6.txt @@ -0,0 +1 @@ +- Merge ST2 and ST3 into a single version. diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/7.txt b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/7.txt new file mode 100644 index 0000000..c5eabd2 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/7.txt @@ -0,0 +1,6 @@ +Enhancements: +- Setting to allow relative paths to be based on the current working directory. Enabled by default. + +Bug Fixes: +- Various fixes for auto completion +- Prompt completion type when using Windows Completion diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/install.txt b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/install.txt index 871cba0..429cd7c 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/install.txt +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/install.txt @@ -2,7 +2,7 @@ Thank you for installing the AdvancedNewFile plugin. For more information please visit https://github.com/skuroda/Sublime-AdvancedNewFile. -Note you may need to restart Sublime Text 2 after installing this plugin. +Note you may need to restart Sublime Text after installing this plugin. If you have any questions, comments, or run into issues, please let me know! Hope you enjoy the plugin. diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/package-metadata.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/package-metadata.json index c8ced63..831c552 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/package-metadata.json +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/package-metadata.json @@ -1 +1 @@ -{"url": "https://github.com/skuroda/Sublime-AdvancedNewFile", "version": "2013.04.02.21.22.36", "description": "File creation plugin for Sublime Text 2 and Sublime Text 3."} \ No newline at end of file +{"url": "https://github.com/skuroda/Sublime-AdvancedNewFile", "version": "2013.09.09.06.50.47", "description": "File creation plugin for Sublime Text 2 and Sublime Text 3."} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/ConsoleExec/examples/flask_application_screenshot.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/ConsoleExec/examples/flask_application_screenshot.png new file mode 100644 index 0000000..874e7a4 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/ConsoleExec/examples/flask_application_screenshot.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/ConsoleExec/examples/hello_sublime_screenshot.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/ConsoleExec/examples/hello_sublime_screenshot.png new file mode 100644 index 0000000..9de2511 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/ConsoleExec/examples/hello_sublime_screenshot.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/EncodingHelper/readme.md b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/EncodingHelper/readme.md index 6b8e26e..19d58f8 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/EncodingHelper/readme.md +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/EncodingHelper/readme.md @@ -1,3 +1,5 @@ +For Sublime Text 3 See: https://github.com/SublimeText/EncodingHelper/tree/st3 + Description ------------------ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/KeymapManager/package-metadata.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/KeymapManager/package-metadata.json index 56a13b3..2aa11d6 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/KeymapManager/package-metadata.json +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/KeymapManager/package-metadata.json @@ -1 +1 @@ -{"url": "https://github.com/welefen/KeymapManager", "version": "2012.08.19.23.05.38", "description": "KeymapManager for sublime text 2"} \ No newline at end of file +{"url": "https://github.com/welefen/KeymapManager", "version": "2012.08.20.03.05.38", "description": "KeymapManager plugin for sublime text 2"} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/Default (Linux).sublime-keymap b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/Default (Linux).sublime-keymap index c84fbfb..3d93b6c 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/Default (Linux).sublime-keymap +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/Default (Linux).sublime-keymap @@ -1,14 +1,15 @@ [ - { - "keys": ["ctrl+shift+end"], - "command": "move_tab", - "args": { "position": "999" } - }, - { - "keys": ["ctrl+shift+home"], - "command": "move_tab", - "args": { "position": "0" } - }, + // Uncomment to enable. + // { + // "keys": ["ctrl+shift+end"], + // "command": "move_tab", + // "args": { "position": "999" } + // }, + // { + // "keys": ["ctrl+shift+home"], + // "command": "move_tab", + // "args": { "position": "0" } + // }, { "keys": ["ctrl+shift+pageup"], "command": "move_tab", diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/Default (OSX).sublime-keymap b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/Default (OSX).sublime-keymap index d6a47bf..74a889f 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/Default (OSX).sublime-keymap +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/Default (OSX).sublime-keymap @@ -1,14 +1,15 @@ [ - { - "keys": ["super+alt+shift+down"], - "command": "move_tab", - "args": { "position": "999" } - }, - { - "keys": ["super+alt+shift+up"], - "command": "move_tab", - "args": { "position": "0" } - }, + // Uncomment to enable. + // { + // "keys": ["super+alt+shift+down"], + // "command": "move_tab", + // "args": { "position": "999" } + // }, + // { + // "keys": ["super+alt+shift+up"], + // "command": "move_tab", + // "args": { "position": "0" } + // }, { "keys": ["super+alt+shift+left"], "command": "move_tab", diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/Default (Windows).sublime-keymap b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/Default (Windows).sublime-keymap index c84fbfb..3d93b6c 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/Default (Windows).sublime-keymap +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/Default (Windows).sublime-keymap @@ -1,14 +1,15 @@ [ - { - "keys": ["ctrl+shift+end"], - "command": "move_tab", - "args": { "position": "999" } - }, - { - "keys": ["ctrl+shift+home"], - "command": "move_tab", - "args": { "position": "0" } - }, + // Uncomment to enable. + // { + // "keys": ["ctrl+shift+end"], + // "command": "move_tab", + // "args": { "position": "999" } + // }, + // { + // "keys": ["ctrl+shift+home"], + // "command": "move_tab", + // "args": { "position": "0" } + // }, { "keys": ["ctrl+shift+pageup"], "command": "move_tab", diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/README.md b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/README.md index aee5e46..9244e8a 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/README.md +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/README.md @@ -1,7 +1,7 @@ Move Tab ======== -Plugin for Sublime Text 2 to move tabs around. +Plugin for Sublime Text to move tabs around. Usage ----- @@ -13,21 +13,30 @@ The following commands are accessible via the command palette: - Move Tab: To first position - Move Tab: To last position -The shortcuts are: +The default shortcuts are: -- Linux/Windows: CTRL + Shift + (Page up / Page down / Home / End) -- MacOS X: Command + Alt + Shift + (Left / Right / Up / Down) +- Linux/Windows: CTRL + Shift + (Page up / Page down) +- MacOS X: Command + Alt + Shift + (Left / Right) + +*Shortcuts to move to first or last position are disabled by default.* + +Requirements +------------ + +Sublime Text 2 or 3. Installation ------------ -Clone this repository into the Packages directory. If you don't know where it is, enter the following command in the console: +Use the Package Control. - print sublime.packages_path() + Command palette > Package Control: Install Package -_To access the console press CTRL + `_ +Alternatively, simply clone this repository into the Packages directory. + + Preferences > Browse Packages... License ------- -Licensed under the [MIT License](http://www.opensource.org/licenses/mit-license.php) \ No newline at end of file +Licensed under the [MIT License](http://www.opensource.org/licenses/mit-license.php) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/move_tab.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/move_tab.py index 104c84b..703d183 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/move_tab.py +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/move_tab.py @@ -3,7 +3,7 @@ """ Move Tab -Plugin for Sublime Text 2 to move tabs around +Plugin for Sublime Text to move tabs around Copyright (c) 2012 Frédéric Massart - FMCorz.net diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/package-metadata.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/package-metadata.json index 00de1ed..584be22 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/package-metadata.json +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/package-metadata.json @@ -1 +1 @@ -{"url": "https://github.com/SublimeText/MoveTab", "version": "2012.08.24.06.11.17", "description": "Plugin for Sublime Text 2 to move tabs around"} \ No newline at end of file +{"url": "https://github.com/SublimeText/MoveTab", "version": "2013.09.12.08.16.08", "description": "Plugin for Sublime Text to move tabs around"} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Navigation History/package-metadata.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Navigation History/package-metadata.json index 345fe4a..801a344 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Navigation History/package-metadata.json +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Navigation History/package-metadata.json @@ -1 +1 @@ -{"url": "https://github.com/timjrobinson/SublimeNavigationHistory", "version": "2013.03.17.15.51.45", "description": "SublimeText 2/3 Navigation History - jump forward and back around your code. "} \ No newline at end of file +{"url": "https://github.com/timjrobinson/SublimeNavigationHistory", "version": "2013.03.17.19.51.45", "description": "SublimeText 2/3 Navigation History - jump forward and back around your code. "} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Open Recent Files/package-metadata.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Open Recent Files/package-metadata.json index fdec3ca..563a603 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Open Recent Files/package-metadata.json +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Open Recent Files/package-metadata.json @@ -1 +1 @@ -{"url": "https://github.com/spadgos/sublime-OpenRecentFiles", "version": "2011.10.03.10.20.08", "description": "A package which adds Ctrl+Shift+T to open the most recent files."} \ No newline at end of file +{"url": "https://github.com/spadgos/sublime-OpenRecentFiles", "version": "2011.10.03.14.20.08", "description": "A package which adds Ctrl+Shift+T to open the most recent files."} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/Base File (OSX).sublime-settings b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/Base File (OSX).sublime-settings index 0ed3659..434d4f1 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/Base File (OSX).sublime-settings +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/Base File (OSX).sublime-settings @@ -1,5 +1,6 @@ { "highlight_search_icon": "bookmark", "highlight_search_scope": "entity.name.function", - "highlight_search_results": false -} \ No newline at end of file + "highlight_search_results": false, + "open_search_result_everywhere": false +} diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/Default.sublime-keymap b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/Default.sublime-keymap index 2018feb..8a69d3c 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/Default.sublime-keymap +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/Default.sublime-keymap @@ -3,5 +3,13 @@ "keys": ["g", "o"], "command": "open_search_result", "context": [{"key": "setting.command_mode"}] + }, + { + "keys": ["ctrl+enter"], + "command": "open_search_result", + "context": + [ + { "key": "selector", "operator": "equal", "operand": "text.find-in-files" } + ] } -] \ No newline at end of file +] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/README.md b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/README.md index ed2ce2a..40340cd 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/README.md +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/README.md @@ -24,3 +24,6 @@ openable paths or the icon. See your theme file for examples of colors. - 'highlight_search_icon': If you want an icon to show up in the gutter next to openable paths, include a valid icon name as a string (e.g., 'circle', 'dot' or 'bookmark') +- 'open_search_result_everywhere': Set to true to enable this plugin on all +files not just Find Results panes. You can use this for saving and reopening +your find results. diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/open_search_result.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/open_search_result.py index ccaa753..930ba2d 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/open_search_result.py +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/open_search_result.py @@ -1,31 +1,40 @@ import os import sublime, sublime_plugin -import util +# Load with Python3, fallback to load with Python2 +try: + from .util import parse_line_number, is_file_path +except ValueError: + from util import parse_line_number, is_file_path + + +class OpenSearchResultKeys: + HIGHLIGHT_ENABLED = 'highlight_search_results' + SCOPE_SETTINGS = 'highlight_search_scope' + ICON_SETTINGS = 'highlight_search_icon' + OPEN_EVERYWHERE = 'open_search_result_everywhere' class HighlightFilePaths(sublime_plugin.EventListener): HIGHLIGHT_REGION_NAME = 'HighlightFilePaths' - HIGHLIGHT_ENABLED_KEY = 'highlight_search_results' - SCOPE_SETTINGS_KEY = 'highlight_search_scope' - ICON_SETTINGS_KEY = 'highlight_search_icon' DEFAULT_SCOPE = 'search_result_highlight' DEFAULT_ICON = '' def show_highlight(self, view): valid_regions = [] - show_highlight = view.settings().get(self.HIGHLIGHT_ENABLED_KEY, False) - scope = view.settings().get(self.SCOPE_SETTINGS_KEY, self.DEFAULT_SCOPE) - icon = view.settings().get(self.ICON_SETTINGS_KEY, self.DEFAULT_ICON) + show_highlight = view.settings().get(OpenSearchResultKeys.HIGHLIGHT_ENABLED, False) + scope = view.settings().get(OpenSearchResultKeys.SCOPE_SETTINGS, self.DEFAULT_SCOPE) + icon = view.settings().get(OpenSearchResultKeys.ICON_SETTINGS, self.DEFAULT_ICON) + open_everywhere = view.settings().get(OpenSearchResultKeys.OPEN_EVERYWHERE, False) - if view.name() != 'Find Results': + if open_everywhere == False and view.name() != 'Find Results': return for s in view.sel(): line = view.line(s) line_str = view.substr(view.line(s)) - line_num = util.parse_line_number(line_str) + line_num = parse_line_number(line_str) - if util.is_file_path(line_str) or line_num: + if is_file_path(line_str) or line_num: valid_regions.append(line) if valid_regions: @@ -40,8 +49,8 @@ class HighlightFilePaths(sublime_plugin.EventListener): view.erase_regions(self.HIGHLIGHT_REGION_NAME) def on_selection_modified(self, view): - highlight_enabled = (view.settings().get(self.HIGHLIGHT_ENABLED_KEY) - or view.settings().get(self.ICON_SETTINGS_KEY)) + highlight_enabled = (view.settings().get(OpenSearchResultKeys.HIGHLIGHT_ENABLED) + or view.settings().get(OpenSearchResultKeys.ICON_SETTINGS)) if view.settings().get('is_widget') \ or not view.settings().get('command_mode') \ @@ -108,19 +117,20 @@ class OpenSearchResultCommand(sublime_plugin.TextCommand): break line = self.view.substr(prev).strip() - if util.is_file_path(line): + if is_file_path(line): return self.open_file_from_line(line, line_num) def run(self, edit): + open_everywhere = self.view.settings().get(OpenSearchResultKeys.OPEN_EVERYWHERE, False) for cursor in self.view.sel(): cur_line = self.view.line(cursor) line_str = self.view.substr(cur_line).strip() - line_num = util.parse_line_number(line_str) + line_num = parse_line_number(line_str) - if self.view.name() != 'Find Results': + if open_everywhere == False and self.view.name() != 'Find Results': return - if util.is_file_path(line_str): + if is_file_path(line_str): self.open_file_path(line_str) elif line_num: self.open_file_at_line_num(cur_line, line_num) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/package-metadata.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/package-metadata.json index 898c230..dcfeaf4 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/package-metadata.json +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/package-metadata.json @@ -1 +1 @@ -{"url": "https://github.com/abrookins/OpenSearchResult", "version": "2012.09.11.15.18.59", "description": "a Sublime Text 2 plugin that opens files listed in the Find in File output"} \ No newline at end of file +{"url": "https://github.com/abrookins/OpenSearchResult", "version": "2013.07.25.03.39.55", "description": "a Sublime Text 2 plugin that opens files listed in the Find in File output"} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/util.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/util.py index 1196191..25a26e4 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/util.py +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/util.py @@ -27,7 +27,7 @@ def is_file_path(line_str): >>> is_file_path('/Users/me/code/OpenSearchResult/open_search_result.py:') True - >>> is_file_path('C:\Users\me\\test.txt:') + >>> is_file_path('C:\\Users\\me\\test.txt:') True >>> is_file_path('5: def parse_line_number(line_str):') False diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Default.sublime-commands b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Default.sublime-commands index 7d823ee..dd5513c 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Default.sublime-commands +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Default.sublime-commands @@ -5,7 +5,7 @@ }, { "caption": "Package Control: Add Channel", - "command": "add_repository_channel" + "command": "add_channel" }, { "caption": "Package Control: Create Binary Package File", @@ -27,6 +27,10 @@ "caption": "Package Control: Enable Package", "command": "enable_package" }, + { + "caption": "Package Control: Grab CA Certs", + "command": "grab_certs" + }, { "caption": "Package Control: Install Package", "command": "install_package" diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.ca-bundle b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.ca-bundle new file mode 100644 index 0000000..b718caa --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.ca-bundle @@ -0,0 +1,43 @@ +----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg +R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 +9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq +fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv +iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU +1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ +bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW +MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA +ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l +uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn +Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS +tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF +PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un +hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV +5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm ++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW +PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM +xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB +Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 +hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg +EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA +FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec +nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z +eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF +hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 +Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep ++OkuE6N36B9K +-----END CERTIFICATE----- diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.ca-list b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.ca-list new file mode 100644 index 0000000..93aa232 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.ca-list @@ -0,0 +1,4 @@ +[ + "221e907bdfff70d71cea42361ae209d5", + "7d0986b90061d60c8c02aa3b1cf23850" +] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.py index 4b62978..9c47377 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.py +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.py @@ -1,4810 +1,101 @@ -# coding=utf-8 import sublime -import sublime_plugin -import os import sys -import subprocess -import zipfile -import urllib -import urllib2 -import json -from fnmatch import fnmatch -import re -import threading -import datetime -import time -import shutil -import tempfile -import httplib -import socket -import hashlib -import base64 +import os import locale -import urlparse -import gzip -import StringIO -import zlib -if os.name == 'nt': - from ctypes import windll, create_unicode_buffer +st_version = 2 + +# Warn about out-dated versions of ST3 +if sublime.version() == '': + st_version = 3 + print('Package Control: Please upgrade to Sublime Text 3 build 3012 or newer') + +elif int(sublime.version()) > 3000: + st_version = 3 -def add_to_path(path): - # Python 2.x on Windows can't properly import from non-ASCII paths, so - # this code added the DOC 8.3 version of the lib folder to the path in - # case the user's username includes non-ASCII characters - if os.name == 'nt': - buf = create_unicode_buffer(512) - if windll.kernel32.GetShortPathNameW(path, buf, len(buf)): - path = buf.value - - if path not in sys.path: - sys.path.append(path) +if st_version == 3: + installed_dir, _ = __name__.split('.') +elif st_version == 2: + installed_dir = os.path.basename(os.getcwd()) -lib_folder = os.path.join(sublime.packages_path(), 'Package Control', 'lib') -add_to_path(os.path.join(lib_folder, 'all')) +# Ensure the user has installed Package Control properly +if installed_dir != 'Package Control': + message = (u"Package Control\n\nThis package appears to be installed " + + u"incorrectly.\n\nIt should be installed as \"Package Control\", " + + u"but seems to be installed as \"%s\".\n\n" % installed_dir) + # If installed unpacked + if os.path.exists(os.path.join(sublime.packages_path(), installed_dir)): + message += (u"Please use the Preferences > Browse Packages... menu " + + u"entry to open the \"Packages/\" folder and rename" + + u"\"%s/\" to \"Package Control/\" " % installed_dir) + # If installed as a .sublime-package file + else: + message += (u"Please use the Preferences > Browse Packages... menu " + + u"entry to open the \"Packages/\" folder, then browse up a " + + u"folder and into the \"Installed Packages/\" folder.\n\n" + + u"Inside of \"Installed Packages/\", rename " + + u"\"%s.sublime-package\" to " % installed_dir + + u"\"Package Control.sublime-package\" ") + message += u"and restart Sublime Text." + sublime.error_message(message) + +# Normal execution will finish setting up the package +else: + reloader_name = 'package_control.reloader' + + # ST3 loads each package as a module, so it needs an extra prefix + if st_version == 3: + reloader_name = 'Package Control.' + reloader_name + from imp import reload + + # Make sure all dependencies are reloaded on upgrade + if reloader_name in sys.modules: + reload(sys.modules[reloader_name]) -import semver - - -if os.name == 'nt': - add_to_path(os.path.join(lib_folder, 'windows')) - from ntlm import ntlm - - -def unicode_from_os(e): - # This is needed as some exceptions coming from the OS are - # already encoded and so just calling unicode(e) will result - # in an UnicodeDecodeError as the string isn't in ascii form. try: - # Sublime Text on OS X does not seem to report the correct encoding - # so we hard-code that to UTF-8 - encoding = 'UTF-8' if os.name == 'darwin' else locale.getpreferredencoding() - return unicode(str(e), encoding) + # Python 3 + from .package_control import reloader - # If the "correct" encoding did not work, try some defaults, and then just - # obliterate characters that we can't seen to decode properly - except UnicodeDecodeError: - encodings = ['utf-8', 'cp1252'] - for encoding in encodings: - try: - return unicode(str(e), encoding, errors='strict') - except: - pass - return unicode(str(e), errors='replace') + from .package_control.commands import * + from .package_control.package_cleanup import PackageCleanup + + except (ValueError): + # Python 2 + from package_control import reloader + from package_control import sys_path + + from package_control.commands import * + from package_control.package_cleanup import PackageCleanup -def create_cmd(args, basename_binary=False): - if basename_binary: - args[0] = os.path.basename(args[0]) - - if os.name == 'nt': - return subprocess.list2cmdline(args) - else: - escaped_args = [] - for arg in args: - if re.search('^[a-zA-Z0-9/_^\\-\\.:=]+$', arg) == None: - arg = u"'" + arg.replace(u"'", u"'\\''") + u"'" - escaped_args.append(arg) - return u' '.join(escaped_args) - - -# Monkey patch AbstractBasicAuthHandler to prevent infinite recursion -def non_recursive_http_error_auth_reqed(self, authreq, host, req, headers): - authreq = headers.get(authreq, None) - - if not hasattr(self, 'retried'): - self.retried = 0 - - if self.retried > 5: - raise urllib2.HTTPError(req.get_full_url(), 401, "basic auth failed", - headers, None) - else: - self.retried += 1 - - if authreq: - mo = urllib2.AbstractBasicAuthHandler.rx.search(authreq) - if mo: - scheme, quote, realm = mo.groups() - if scheme.lower() == 'basic': - return self.retry_http_basic_auth(host, req, realm) - -urllib2.AbstractBasicAuthHandler.http_error_auth_reqed = non_recursive_http_error_auth_reqed - - -class DebuggableHTTPResponse(httplib.HTTPResponse): - """ - A custom HTTPResponse that formats debugging info for Sublime Text - """ - - _debug_protocol = 'HTTP' - - def __init__(self, sock, debuglevel=0, strict=0, method=None): - # We have to use a positive debuglevel to get it passed to here, - # however we don't want to use it because by default debugging prints - # to the stdout and we can't capture it, so we use a special -1 value - if debuglevel == 5: - debuglevel = -1 - httplib.HTTPResponse.__init__(self, sock, debuglevel, strict, method) - - def begin(self): - return_value = httplib.HTTPResponse.begin(self) - if self.debuglevel == -1: - print '%s: Urllib2 %s Debug Read' % (__name__, self._debug_protocol) - headers = self.msg.headers - versions = { - 9: 'HTTP/0.9', - 10: 'HTTP/1.0', - 11: 'HTTP/1.1' - } - status_line = versions[self.version] + ' ' + str(self.status) + ' ' + self.reason - headers.insert(0, status_line) - for line in headers: - print u" %s" % line.rstrip() - return return_value - - def read(self, *args): + def plugin_loaded(): + # Make sure the user's locale can handle non-ASCII. A whole bunch of + # work was done to try and make Package Control work even if the locale + # was poorly set, by manually encoding all file paths, but it ended up + # being a fool's errand since the package loading code built into + # Sublime Text is not written to work that way, and although packages + # could be installed, they could not be loaded properly. try: - return httplib.HTTPResponse.read(self, *args) - except (httplib.IncompleteRead) as (e): - return e.partial - - -class DebuggableHTTPSResponse(DebuggableHTTPResponse): - """ - A version of DebuggableHTTPResponse that sets the debug protocol to HTTPS - """ - - _debug_protocol = 'HTTPS' - - -class DebuggableHTTPConnection(httplib.HTTPConnection): - """ - A custom HTTPConnection that formats debugging info for Sublime Text - """ - - response_class = DebuggableHTTPResponse - _debug_protocol = 'HTTP' - - def __init__(self, host, port=None, strict=None, - timeout=socket._GLOBAL_DEFAULT_TIMEOUT, **kwargs): - self.passwd = kwargs.get('passwd') - - # Python 2.6.1 on OS X 10.6 does not include these - self._tunnel_host = None - self._tunnel_port = None - self._tunnel_headers = {} - - httplib.HTTPConnection.__init__(self, host, port, strict, timeout) - - def connect(self): - if self.debuglevel == -1: - print '%s: Urllib2 %s Debug General' % (__name__, self._debug_protocol) - print u" Connecting to %s on port %s" % (self.host, self.port) - httplib.HTTPConnection.connect(self) - - def send(self, string): - # We have to use a positive debuglevel to get it passed to the - # HTTPResponse object, however we don't want to use it because by - # default debugging prints to the stdout and we can't capture it, so - # we temporarily set it to -1 for the standard httplib code - reset_debug = False - if self.debuglevel == 5: - reset_debug = 5 - self.debuglevel = -1 - httplib.HTTPConnection.send(self, string) - if reset_debug or self.debuglevel == -1: - if len(string.strip()) > 0: - print '%s: Urllib2 %s Debug Write' % (__name__, self._debug_protocol) - for line in string.strip().splitlines(): - print ' ' + line - if reset_debug: - self.debuglevel = reset_debug - - def request(self, method, url, body=None, headers={}): - original_headers = headers.copy() - - # Handles the challenge request response cycle before the real request - proxy_auth = headers.get('Proxy-Authorization') - if os.name == 'nt' and proxy_auth and proxy_auth.lstrip()[0:4] == 'NTLM': - # The default urllib2.AbstractHTTPHandler automatically sets the - # Connection header to close because of urllib.addinfourl(), but in - # this case we are going to do some back and forth first for the NTLM - # proxy auth - headers['Connection'] = 'Keep-Alive' - self._send_request(method, url, body, headers) - - response = self.getresponse() - - content_length = int(response.getheader('content-length', 0)) - if content_length: - response._safe_read(content_length) - - proxy_authenticate = response.getheader('proxy-authenticate', None) - if not proxy_authenticate: - raise URLError('Invalid NTLM proxy authentication response') - ntlm_challenge = re.sub('^\s*NTLM\s+', '', proxy_authenticate) - - if self.host.find(':') != -1: - host_port = self.host - else: - host_port = "%s:%s" % (self.host, self.port) - username, password = self.passwd.find_user_password(None, host_port) - domain = '' - user = username - if username.find('\\') != -1: - domain, user = username.split('\\', 1) - - challenge, negotiate_flags = ntlm.parse_NTLM_CHALLENGE_MESSAGE(ntlm_challenge) - new_proxy_authorization = 'NTLM %s' % ntlm.create_NTLM_AUTHENTICATE_MESSAGE(challenge, user, - domain, password, negotiate_flags) - original_headers['Proxy-Authorization'] = new_proxy_authorization - response.close() - - httplib.HTTPConnection.request(self, method, url, body, original_headers) - - -class DebuggableHTTPHandler(urllib2.HTTPHandler): - """ - A custom HTTPHandler that formats debugging info for Sublime Text - """ - - def __init__(self, debuglevel=0, debug=False, **kwargs): - # This is a special value that will not trigger the standard debug - # functionality, but custom code where we can format the output - if debug: - self._debuglevel = 5 - else: - self._debuglevel = debuglevel - self.passwd = kwargs.get('passwd') - - def http_open(self, req): - def http_class_wrapper(host, **kwargs): - kwargs['passwd'] = self.passwd - return DebuggableHTTPConnection(host, **kwargs) - - return self.do_open(http_class_wrapper, req) - - -class RateLimitException(httplib.HTTPException, urllib2.URLError): - """ - An exception for when the rate limit of an API has been exceeded. - """ - - def __init__(self, host, limit): - httplib.HTTPException.__init__(self) - self.host = host - self.limit = limit - - def __str__(self): - return ('Rate limit of %s exceeded for %s' % (self.limit, self.host)) - - -if os.name == 'nt': - class ProxyNtlmAuthHandler(urllib2.BaseHandler): - - handler_order = 300 - auth_header = 'Proxy-Authorization' - - def __init__(self, password_manager=None): - if password_manager is None: - password_manager = HTTPPasswordMgr() - self.passwd = password_manager - self.retried = 0 - - def http_error_407(self, req, fp, code, msg, headers): - proxy_authenticate = headers.get('proxy-authenticate') - if os.name != 'nt' or proxy_authenticate[0:4] != 'NTLM': - return None - - type1_flags = ntlm.NTLM_TYPE1_FLAGS - - if req.host.find(':') != -1: - host_port = req.host - else: - host_port = "%s:%s" % (req.host, req.port) - username, password = self.passwd.find_user_password(None, host_port) - if not username: - return None - - if username.find('\\') == -1: - type1_flags &= ~ntlm.NTLM_NegotiateOemDomainSupplied - - negotiate_message = ntlm.create_NTLM_NEGOTIATE_MESSAGE(username, type1_flags) - auth = 'NTLM %s' % negotiate_message - if req.headers.get(self.auth_header, None) == auth: - return None - req.add_unredirected_header(self.auth_header, auth) - return self.parent.open(req, timeout=req.timeout) - - -# The following code is wrapped in a try because the Linux versions of Sublime -# Text do not include the ssl module due to the fact that different distros -# have different versions -try: - import ssl - - class InvalidCertificateException(httplib.HTTPException, urllib2.URLError): - """ - An exception for when an SSL certification is not valid for the URL - it was presented for. - """ - - def __init__(self, host, cert, reason): - httplib.HTTPException.__init__(self) - self.host = host - self.cert = cert - self.reason = reason - - def __str__(self): - return ('Host %s returned an invalid certificate (%s) %s\n' % - (self.host, self.reason, self.cert)) - - - class ValidatingHTTPSConnection(DebuggableHTTPConnection): - """ - A custom HTTPConnection class that validates SSL certificates, and - allows proxy authentication for HTTPS connections. - """ - - default_port = httplib.HTTPS_PORT - - response_class = DebuggableHTTPSResponse - _debug_protocol = 'HTTPS' - - def __init__(self, host, port=None, key_file=None, cert_file=None, - ca_certs=None, strict=None, **kwargs): - passed_args = {} - if 'timeout' in kwargs: - passed_args['timeout'] = kwargs['timeout'] - DebuggableHTTPConnection.__init__(self, host, port, strict, **passed_args) - - self.passwd = kwargs.get('passwd') - self.key_file = key_file - self.cert_file = cert_file - self.ca_certs = ca_certs - if 'user_agent' in kwargs: - self.user_agent = kwargs['user_agent'] - if self.ca_certs: - self.cert_reqs = ssl.CERT_REQUIRED - else: - self.cert_reqs = ssl.CERT_NONE - - def get_valid_hosts_for_cert(self, cert): - """ - Returns a list of valid hostnames for an SSL certificate - - :param cert: A dict from SSLSocket.getpeercert() - - :return: An array of hostnames - """ - - if 'subjectAltName' in cert: - return [x[1] for x in cert['subjectAltName'] - if x[0].lower() == 'dns'] - else: - return [x[0][1] for x in cert['subject'] - if x[0][0].lower() == 'commonname'] - - def validate_cert_host(self, cert, hostname): - """ - Checks if the cert is valid for the hostname - - :param cert: A dict from SSLSocket.getpeercert() - - :param hostname: A string hostname to check - - :return: A boolean if the cert is valid for the hostname - """ - - hosts = self.get_valid_hosts_for_cert(cert) - for host in hosts: - host_re = host.replace('.', '\.').replace('*', '[^.]*') - if re.search('^%s$' % (host_re,), hostname, re.I): - return True - return False - - def _tunnel(self, ntlm_follow_up=False): - """ - This custom _tunnel method allows us to read and print the debug - log for the whole response before throwing an error, and adds - support for proxy authentication - """ - - self._proxy_host = self.host - self._proxy_port = self.port - self._set_hostport(self._tunnel_host, self._tunnel_port) - - self._tunnel_headers['Host'] = u"%s:%s" % (self.host, self.port) - self._tunnel_headers['User-Agent'] = self.user_agent - self._tunnel_headers['Proxy-Connection'] = 'Keep-Alive' - - request = "CONNECT %s:%d HTTP/1.1\r\n" % (self.host, self.port) - for header, value in self._tunnel_headers.iteritems(): - request += "%s: %s\r\n" % (header, value) - self.send(request + "\r\n") - - response = self.response_class(self.sock, strict=self.strict, - method=self._method) - (version, code, message) = response._read_status() - - status_line = u"%s %s %s" % (version, code, message.rstrip()) - headers = [status_line] - - if self.debuglevel in [-1, 5]: - print '%s: Urllib2 %s Debug Read' % (__name__, self._debug_protocol) - print u" %s" % status_line - - content_length = 0 - close_connection = False - while True: - line = response.fp.readline() - if line == '\r\n': break - - headers.append(line.rstrip()) - - parts = line.rstrip().split(': ', 1) - name = parts[0].lower() - value = parts[1].lower().strip() - if name == 'content-length': - content_length = int(value) - - if name in ['connection', 'proxy-connection'] and value == 'close': - close_connection = True - - if self.debuglevel in [-1, 5]: - print u" %s" % line.rstrip() - - # Handle proxy auth for SSL connections since regular urllib2 punts on this - if code == 407 and self.passwd and ('Proxy-Authorization' not in self._tunnel_headers or ntlm_follow_up): - if content_length: - response._safe_read(content_length) - - supported_auth_methods = {} - for line in headers: - parts = line.split(': ', 1) - if parts[0].lower() != 'proxy-authenticate': - continue - details = parts[1].split(' ', 1) - supported_auth_methods[details[0].lower()] = details[1] if len(details) > 1 else '' - - username, password = self.passwd.find_user_password(None, "%s:%s" % ( - self._proxy_host, self._proxy_port)) - - do_ntlm_follow_up = False - - if 'digest' in supported_auth_methods: - response_value = self.build_digest_response( - supported_auth_methods['digest'], username, password) - if response_value: - self._tunnel_headers['Proxy-Authorization'] = u"Digest %s" % response_value - - elif 'basic' in supported_auth_methods: - response_value = u"%s:%s" % (username, password) - response_value = base64.b64encode(response_value).strip() - self._tunnel_headers['Proxy-Authorization'] = u"Basic %s" % response_value - - elif 'ntlm' in supported_auth_methods and os.name == 'nt': - ntlm_challenge = supported_auth_methods['ntlm'] - if not len(ntlm_challenge): - type1_flags = ntlm.NTLM_TYPE1_FLAGS - if username.find('\\') == -1: - type1_flags &= ~ntlm.NTLM_NegotiateOemDomainSupplied - - negotiate_message = ntlm.create_NTLM_NEGOTIATE_MESSAGE(username, type1_flags) - self._tunnel_headers['Proxy-Authorization'] = 'NTLM %s' % negotiate_message - do_ntlm_follow_up = True - else: - domain = '' - user = username - if username.find('\\') != -1: - domain, user = username.split('\\', 1) - - challenge, negotiate_flags = ntlm.parse_NTLM_CHALLENGE_MESSAGE(ntlm_challenge) - self._tunnel_headers['Proxy-Authorization'] = 'NTLM %s' % ntlm.create_NTLM_AUTHENTICATE_MESSAGE(challenge, user, - domain, password, negotiate_flags) - - if 'Proxy-Authorization' in self._tunnel_headers: - self.host = self._proxy_host - self.port = self._proxy_port - - # If the proxy wanted the connection closed, we need to make a new connection - if close_connection: - self.sock.close() - self.sock = socket.create_connection((self.host, self.port), self.timeout) - - return self._tunnel(do_ntlm_follow_up) - - if code != 200: - self.close() - raise socket.error("Tunnel connection failed: %d %s" % (code, - message.strip())) - - def build_digest_response(self, fields, username, password): - """ - Takes a Proxy-Authenticate: Digest header and creates a response - header - - :param fields: - The string portion of the Proxy-Authenticate header after - "Digest " - - :param username: - The username to use for the response - - :param password: - The password to use for the response - - :return: - None if invalid Proxy-Authenticate header, otherwise the - string of fields for the Proxy-Authorization: Digest header - """ - - fields = urllib2.parse_keqv_list(urllib2.parse_http_list(fields)) - - realm = fields.get('realm') - nonce = fields.get('nonce') - qop = fields.get('qop') - algorithm = fields.get('algorithm') - if algorithm: - algorithm = algorithm.lower() - opaque = fields.get('opaque') - - if algorithm in ['md5', None]: - def hash(string): - return hashlib.md5(string).hexdigest() - elif algorithm == 'sha': - def hash(string): - return hashlib.sha1(string).hexdigest() - else: - return None - - host_port = u"%s:%s" % (self.host, self.port) - - a1 = "%s:%s:%s" % (username, realm, password) - a2 = "CONNECT:%s" % host_port - ha1 = hash(a1) - ha2 = hash(a2) - - if qop == None: - response = hash(u"%s:%s:%s" % (ha1, nonce, ha2)) - elif qop == 'auth': - nc = '00000001' - cnonce = hash(urllib2.randombytes(8))[:8] - response = hash(u"%s:%s:%s:%s:%s:%s" % (ha1, nonce, nc, cnonce, qop, ha2)) - else: - return None - - response_fields = { - 'username': username, - 'realm': realm, - 'nonce': nonce, - 'response': response, - 'uri': host_port - } - if algorithm: - response_fields['algorithm'] = algorithm - if qop == 'auth': - response_fields['nc'] = nc - response_fields['cnonce'] = cnonce - response_fields['qop'] = qop - if opaque: - response_fields['opaque'] = opaque - - return ', '.join([u"%s=\"%s\"" % (field, response_fields[field]) for field in response_fields]) - - def connect(self): - """ - Adds debugging and SSL certification validation - """ - - if self.debuglevel == -1: - print '%s: Urllib2 HTTPS Debug General' % __name__ - print u" Connecting to %s on port %s" % (self.host, self.port) - - self.sock = socket.create_connection((self.host, self.port), self.timeout) - if self._tunnel_host: - self._tunnel() - - if self.debuglevel == -1: - print u"%s: Urllib2 HTTPS Debug General" % __name__ - print u" Connecting to %s on port %s" % (self.host, self.port) - print u" CA certs file at %s" % (self.ca_certs) - - self.sock = ssl.wrap_socket(self.sock, keyfile=self.key_file, - certfile=self.cert_file, cert_reqs=self.cert_reqs, - ca_certs=self.ca_certs) - - if self.debuglevel == -1: - print u" Successfully upgraded connection to %s:%s with SSL" % ( - self.host, self.port) - - # This debugs and validates the SSL certificate - if self.cert_reqs & ssl.CERT_REQUIRED: - cert = self.sock.getpeercert() - - if self.debuglevel == -1: - subjectMap = { - 'organizationName': 'O', - 'commonName': 'CN', - 'organizationalUnitName': 'OU', - 'countryName': 'C', - 'serialNumber': 'serialNumber', - 'commonName': 'CN', - 'localityName': 'L', - 'stateOrProvinceName': 'S' - } - subject_list = list(cert['subject']) - subject_list.reverse() - subject_parts = [] - for pair in subject_list: - if pair[0][0] in subjectMap: - field_name = subjectMap[pair[0][0]] - else: - field_name = pair[0][0] - subject_parts.append(field_name + '=' + pair[0][1]) - - print u" Server SSL certificate:" - print u" subject: " + ','.join(subject_parts) - if 'subjectAltName' in cert: - print u" common name: " + cert['subjectAltName'][0][1] - if 'notAfter' in cert: - print u" expire date: " + cert['notAfter'] - - hostname = self.host.split(':', 0)[0] - - if not self.validate_cert_host(cert, hostname): - if self.debuglevel == -1: - print u" Certificate INVALID" - - raise InvalidCertificateException(hostname, cert, - 'hostname mismatch') - - if self.debuglevel == -1: - print u" Certificate validated for %s" % hostname - - if hasattr(urllib2, 'HTTPSHandler'): - class ValidatingHTTPSHandler(urllib2.HTTPSHandler): - """ - A urllib2 handler that validates SSL certificates for HTTPS requests - """ - - def __init__(self, **kwargs): - # This is a special value that will not trigger the standard debug - # functionality, but custom code where we can format the output - self._debuglevel = 0 - if 'debug' in kwargs and kwargs['debug']: - self._debuglevel = 5 - elif 'debuglevel' in kwargs: - self._debuglevel = kwargs['debuglevel'] - self._connection_args = kwargs - - def https_open(self, req): - def http_class_wrapper(host, **kwargs): - full_kwargs = dict(self._connection_args) - full_kwargs.update(kwargs) - return ValidatingHTTPSConnection(host, **full_kwargs) - - try: - return self.do_open(http_class_wrapper, req) - except urllib2.URLError, e: - if type(e.reason) == ssl.SSLError and e.reason.args[0] == 1: - raise InvalidCertificateException(req.host, '', - e.reason.args[1]) - raise - - https_request = urllib2.AbstractHTTPHandler.do_request_ - -except (ImportError): - pass - - -def preferences_filename(): - """:return: The appropriate settings filename based on the version of Sublime Text""" - - if int(sublime.version()) >= 2174: - return 'Preferences.sublime-settings' - return 'Global.sublime-settings' - - -class ThreadProgress(): - """ - Animates an indicator, [= ], in the status area while a thread runs - - :param thread: - The thread to track for activity - - :param message: - The message to display next to the activity indicator - - :param success_message: - The message to display once the thread is complete - """ - - def __init__(self, thread, message, success_message): - self.thread = thread - self.message = message - self.success_message = success_message - self.addend = 1 - self.size = 8 - sublime.set_timeout(lambda: self.run(0), 100) - - def run(self, i): - if not self.thread.is_alive(): - if hasattr(self.thread, 'result') and not self.thread.result: - sublime.status_message('') - return - sublime.status_message(self.success_message) + os.path.exists(os.path.join(sublime.packages_path(), u"fran\u00e7ais")) + except (UnicodeEncodeError) as e: + message = (u"Package Control\n\nYour system's locale is set to a " + + u"value that can not handle non-ASCII characters. Package " + + u"Control can not properly work unless this is fixed.\n\n" + + u"On Linux, please reference your distribution's docs for " + + u"information on properly setting the LANG environmental " + + u"variable. As a temporary work-around, you can launch " + + u"Sublime Text from the terminal with:\n\n" + + u"LANG=en_US.UTF-8 sublime_text") + sublime.error_message(message) return - before = i % self.size - after = (self.size - 1) - before + # Start shortly after Sublime starts so package renames don't cause errors + # with keybindings, settings, etc disappearing in the middle of parsing + sublime.set_timeout(lambda: PackageCleanup().start(), 2000) - sublime.status_message('%s [%s=%s]' % \ - (self.message, ' ' * before, ' ' * after)) - - if not after: - self.addend = -1 - if not before: - self.addend = 1 - i += self.addend - - sublime.set_timeout(lambda: self.run(i), 100) - - -class PlatformComparator(): - def get_best_platform(self, platforms): - ids = [sublime.platform() + '-' + sublime.arch(), sublime.platform(), - '*'] - - for id in ids: - if id in platforms: - return id - - return None - - -class ChannelProvider(PlatformComparator): - """ - Retrieves a channel and provides an API into the information - - The current channel/repository infrastructure caches repository info into - the channel to improve the Package Control client performance. This also - has the side effect of lessening the load on the GitHub and BitBucket APIs - and getting around not-infrequent HTTP 503 errors from those APIs. - - :param channel: - The URL of the channel - - :param package_manager: - An instance of :class:`PackageManager` used to download the file - """ - - def __init__(self, channel, package_manager): - self.channel_info = None - self.channel = channel - self.package_manager = package_manager - self.unavailable_packages = [] - - def match_url(self): - """Indicates if this provider can handle the provided channel""" - - return True - - def fetch_channel(self): - """Retrieves and loads the JSON for other methods to use""" - - if self.channel_info != None: - return - - channel_json = self.package_manager.download_url(self.channel, - 'Error downloading channel.') - if channel_json == False: - self.channel_info = False - return - - try: - channel_info = json.loads(channel_json) - except (ValueError): - print '%s: Error parsing JSON from channel %s.' % (__name__, - self.channel) - channel_info = False - - self.channel_info = channel_info - - def get_name_map(self): - """:return: A dict of the mapping for URL slug -> package name""" - - self.fetch_channel() - if self.channel_info == False: - return False - return self.channel_info.get('package_name_map', {}) - - def get_renamed_packages(self): - """:return: A dict of the packages that have been renamed""" - - self.fetch_channel() - if self.channel_info == False: - return False - return self.channel_info.get('renamed_packages', {}) - - def get_repositories(self): - """:return: A list of the repository URLs""" - - self.fetch_channel() - if self.channel_info == False: - return False - return self.channel_info['repositories'] - - def get_certs(self): - """ - Provides a secure way for distribution of SSL CA certificates - - Unfortunately Python does not include a bundle of CA certs with urllib2 - to perform SSL certificate validation. To circumvent this issue, - Package Control acts as a distributor of the CA certs for all HTTPS - URLs of package downloads. - - The default channel scrapes and caches info about all packages - periodically, and in the process it checks the CA certs for all of - the HTTPS URLs listed in the repositories. The contents of the CA cert - files are then hashed, and the CA cert is stored in a filename with - that hash. This is a fingerprint to ensure that Package Control has - the appropriate CA cert for a domain name. - - Next, the default channel file serves up a JSON object of the domain - names and the hashes of their current CA cert files. If Package Control - does not have the appropriate hash for a domain, it may retrieve it - from the channel server. To ensure that Package Control is talking to - a trusted authority to get the CA certs from, the CA cert for - sublime.wbond.net is bundled with Package Control. Then when downloading - the channel file, Package Control can ensure that the channel file's - SSL certificate is valid, thus ensuring the resulting CA certs are - legitimate. - - As a matter of optimization, the distribution of Package Control also - includes the current CA certs for all known HTTPS domains that are - included in the channel, as of the time when Package Control was - last released. - - :return: A dict of {'Domain Name': ['cert_file_hash', 'cert_file_download_url']} - """ - - self.fetch_channel() - if self.channel_info == False: - return False - return self.channel_info.get('certs', {}) - - def get_packages(self, repo): - """ - Provides access to the repository info that is cached in a channel - - :param repo: - The URL of the repository to get the cached info of - - :return: - A dict in the format: - { - 'Package Name': { - # Package details - see example-packages.json for format - }, - ... - } - or False if there is an error - """ - - self.fetch_channel() - if self.channel_info == False: - return False - if self.channel_info.get('packages', False) == False: - return False - if self.channel_info['packages'].get(repo, False) == False: - return False - - output = {} - for package in self.channel_info['packages'][repo]: - copy = package.copy() - - platforms = copy['platforms'].keys() - best_platform = self.get_best_platform(platforms) - - if not best_platform: - self.unavailable_packages.append(copy['name']) - continue - - copy['downloads'] = copy['platforms'][best_platform] - - del copy['platforms'] - - copy['url'] = copy['homepage'] - del copy['homepage'] - - output[copy['name']] = copy - - return output - - def get_unavailable_packages(self): - """ - Provides a list of packages that are unavailable for the current - platform/architecture that Sublime Text is running on. - - This list will be empty unless get_packages() is called first. - - :return: A list of package names - """ - - return self.unavailable_packages - - -# The providers (in order) to check when trying to download a channel -_channel_providers = [ChannelProvider] - - -class PackageProvider(PlatformComparator): - """ - Generic repository downloader that fetches package info - - With the current channel/repository architecture where the channel file - caches info from all includes repositories, these package providers just - serve the purpose of downloading packages not in the default channel. - - The structure of the JSON a repository should contain is located in - example-packages.json. - - :param repo: - The URL of the package repository - - :param package_manager: - An instance of :class:`PackageManager` used to download the file - """ - def __init__(self, repo, package_manager): - self.repo_info = None - self.repo = repo - self.package_manager = package_manager - self.unavailable_packages = [] - - def match_url(self): - """Indicates if this provider can handle the provided repo""" - - return True - - def fetch_repo(self): - """Retrieves and loads the JSON for other methods to use""" - - if self.repo_info != None: - return - - repository_json = self.package_manager.download_url(self.repo, - 'Error downloading repository.') - if repository_json == False: - self.repo_info = False - return - - try: - self.repo_info = json.loads(repository_json) - except (ValueError): - print '%s: Error parsing JSON from repository %s.' % (__name__, - self.repo) - self.repo_info = False - - def get_packages(self): - """ - Provides access to the repository info that is cached in a channel - - :return: - A dict in the format: - { - 'Package Name': { - # Package details - see example-packages.json for format - }, - ... - } - or False if there is an error - """ - - self.fetch_repo() - if self.repo_info == False: - return False - - output = {} - - for package in self.repo_info['packages']: - - platforms = package['platforms'].keys() - best_platform = self.get_best_platform(platforms) - - if not best_platform: - self.unavailable_packages.append(package['name']) - continue - - # Rewrites the legacy "zipball" URLs to the new "zip" format - downloads = package['platforms'][best_platform] - rewritten_downloads = [] - for download in downloads: - download['url'] = re.sub( - '^(https://nodeload.github.com/[^/]+/[^/]+/)zipball(/.*)$', - '\\1zip\\2', download['url']) - rewritten_downloads.append(download) - - info = { - 'name': package['name'], - 'description': package.get('description'), - 'url': package.get('homepage', self.repo), - 'author': package.get('author'), - 'last_modified': package.get('last_modified'), - 'downloads': rewritten_downloads - } - - output[package['name']] = info - - return output - - def get_renamed_packages(self): - """:return: A dict of the packages that have been renamed""" - - return self.repo_info.get('renamed_packages', {}) - - def get_unavailable_packages(self): - """ - Provides a list of packages that are unavailable for the current - platform/architecture that Sublime Text is running on. - - This list will be empty unless get_packages() is called first. - - :return: A list of package names - """ - - return self.unavailable_packages - - -class NonCachingProvider(): - """ - Base for package providers that do not need to cache the JSON - """ - - def fetch_json(self, url): - """ - Retrieves and parses the JSON from a URL - - :return: A dict or list from the JSON, or False on error - """ - - repository_json = self.package_manager.download_url(url, - 'Error downloading repository.') - if repository_json == False: - return False - try: - return json.loads(repository_json) - except (ValueError): - print '%s: Error parsing JSON from repository %s.' % (__name__, - url) - return False - - def get_unavailable_packages(self): - """ - Method for compatibility with PackageProvider class. These providers - are based on API calls, and thus do not support different platform - downloads, making it impossible for there to be unavailable packages. - - :return: An empty list - """ - - return [] - - -class GitHubPackageProvider(NonCachingProvider): - """ - Allows using a public GitHub repository as the source for a single package - - :param repo: - The public web URL to the GitHub repository. Should be in the format - `https://github.com/user/package` for the master branch, or - `https://github.com/user/package/tree/{branch_name}` for any other - branch. - - :param package_manager: - An instance of :class:`PackageManager` used to access the API - """ - - def __init__(self, repo, package_manager): - # Clean off the trailing .git to be more forgiving - self.repo = re.sub('\.git$', '', repo) - self.package_manager = package_manager - - def match_url(self): - """Indicates if this provider can handle the provided repo""" - - master = re.search('^https?://github.com/[^/]+/[^/]+/?$', self.repo) - branch = re.search('^https?://github.com/[^/]+/[^/]+/tree/[^/]+/?$', - self.repo) - return master != None or branch != None - - def get_packages(self): - """Uses the GitHub API to construct necessary info for a package""" - - branch = 'master' - branch_match = re.search( - '^https?://github.com/[^/]+/[^/]+/tree/([^/]+)/?$', self.repo) - if branch_match != None: - branch = branch_match.group(1) - - api_url = re.sub('^https?://github.com/([^/]+)/([^/]+)($|/.*$)', - 'https://api.github.com/repos/\\1/\\2', self.repo) - - repo_info = self.fetch_json(api_url) - if repo_info == False: - return False - - # In addition to hitting the main API endpoint for this repo, we - # also have to list the commits to get the timestamp of the last - # commit since we use that to generate a version number - commit_api_url = api_url + '/commits?' + \ - urllib.urlencode({'sha': branch, 'per_page': 1}) - - commit_info = self.fetch_json(commit_api_url) - if commit_info == False: - return False - - # We specifically use nodeload.github.com here because the download - # URLs all redirect there, and some of the downloaders don't follow - # HTTP redirect headers - download_url = 'https://nodeload.github.com/' + \ - repo_info['owner']['login'] + '/' + \ - repo_info['name'] + '/zip/' + urllib.quote(branch) - - commit_date = commit_info[0]['commit']['committer']['date'] - timestamp = datetime.datetime.strptime(commit_date[0:19], - '%Y-%m-%dT%H:%M:%S') - utc_timestamp = timestamp.strftime( - '%Y.%m.%d.%H.%M.%S') - - homepage = repo_info['homepage'] - if not homepage: - homepage = repo_info['html_url'] - - package = { - 'name': repo_info['name'], - 'description': repo_info['description'] if \ - repo_info['description'] else 'No description provided', - 'url': homepage, - 'author': repo_info['owner']['login'], - 'last_modified': timestamp.strftime('%Y-%m-%d %H:%M:%S'), - 'downloads': [ - { - 'version': utc_timestamp, - 'url': download_url - } - ] - } - return {package['name']: package} - - def get_renamed_packages(self): - """For API-compatibility with :class:`PackageProvider`""" - - return {} - - -class GitHubUserProvider(NonCachingProvider): - """ - Allows using a GitHub user/organization as the source for multiple packages - - :param repo: - The public web URL to the GitHub user/org. Should be in the format - `https://github.com/user`. - - :param package_manager: - An instance of :class:`PackageManager` used to access the API - """ - - def __init__(self, repo, package_manager): - self.repo = repo - self.package_manager = package_manager - - def match_url(self): - """Indicates if this provider can handle the provided repo""" - - return re.search('^https?://github.com/[^/]+/?$', self.repo) != None - - def get_packages(self): - """Uses the GitHub API to construct necessary info for all packages""" - - user_match = re.search('^https?://github.com/([^/]+)/?$', self.repo) - user = user_match.group(1) - - api_url = 'https://api.github.com/users/%s/repos?per_page=100' % user - - repo_info = self.fetch_json(api_url) - if repo_info == False: - return False - - packages = {} - for package_info in repo_info: - # All packages for the user are made available, and always from - # the master branch. Anything else requires a custom packages.json - commit_api_url = ('https://api.github.com/repos/%s/%s/commits' + \ - '?sha=master&per_page=1') % (user, package_info['name']) - - commit_info = self.fetch_json(commit_api_url) - if commit_info == False: - return False - - commit_date = commit_info[0]['commit']['committer']['date'] - timestamp = datetime.datetime.strptime(commit_date[0:19], - '%Y-%m-%dT%H:%M:%S') - utc_timestamp = timestamp.strftime( - '%Y.%m.%d.%H.%M.%S') - - homepage = package_info['homepage'] - if not homepage: - homepage = package_info['html_url'] - - package = { - 'name': package_info['name'], - 'description': package_info['description'] if \ - package_info['description'] else 'No description provided', - 'url': homepage, - 'author': package_info['owner']['login'], - 'last_modified': timestamp.strftime('%Y-%m-%d %H:%M:%S'), - 'downloads': [ - { - 'version': utc_timestamp, - # We specifically use nodeload.github.com here because - # the download URLs all redirect there, and some of the - # downloaders don't follow HTTP redirect headers - 'url': 'https://nodeload.github.com/' + \ - package_info['owner']['login'] + '/' + \ - package_info['name'] + '/zip/master' - } - ] - } - packages[package['name']] = package - return packages - - def get_renamed_packages(self): - """For API-compatibility with :class:`PackageProvider`""" - - return {} - - -class BitBucketPackageProvider(NonCachingProvider): - """ - Allows using a public BitBucket repository as the source for a single package - - :param repo: - The public web URL to the BitBucket repository. Should be in the format - `https://bitbucket.org/user/package`. - - :param package_manager: - An instance of :class:`PackageManager` used to access the API - """ - - def __init__(self, repo, package_manager): - self.repo = repo - self.package_manager = package_manager - - def match_url(self): - """Indicates if this provider can handle the provided repo""" - - return re.search('^https?://bitbucket.org', self.repo) != None - - def get_packages(self): - """Uses the BitBucket API to construct necessary info for a package""" - - api_url = re.sub('^https?://bitbucket.org/', - 'https://api.bitbucket.org/1.0/repositories/', self.repo) - api_url = api_url.rstrip('/') - - repo_info = self.fetch_json(api_url) - if repo_info == False: - return False - - # Since HG allows for arbitrary main branch names, we have to hit - # this URL just to get that info - main_branch_url = api_url + '/main-branch/' - main_branch_info = self.fetch_json(main_branch_url) - if main_branch_info == False: - return False - - # Grabbing the changesets is necessary because we construct the - # version number from the last commit timestamp - changeset_url = api_url + '/changesets/' + main_branch_info['name'] - last_commit = self.fetch_json(changeset_url) - if last_commit == False: - return False - - commit_date = last_commit['timestamp'] - timestamp = datetime.datetime.strptime(commit_date[0:19], - '%Y-%m-%d %H:%M:%S') - utc_timestamp = timestamp.strftime( - '%Y.%m.%d.%H.%M.%S') - - homepage = repo_info['website'] - if not homepage: - homepage = self.repo - package = { - 'name': repo_info['name'], - 'description': repo_info['description'] if \ - repo_info['description'] else 'No description provided', - 'url': homepage, - 'author': repo_info['owner'], - 'last_modified': timestamp.strftime('%Y-%m-%d %H:%M:%S'), - 'downloads': [ - { - 'version': utc_timestamp, - 'url': self.repo + '/get/' + \ - last_commit['node'] + '.zip' - } - ] - } - return {package['name']: package} - - def get_renamed_packages(self): - """For API-compatibility with :class:`PackageProvider`""" - - return {} - - -# The providers (in order) to check when trying to download repository info -_package_providers = [BitBucketPackageProvider, GitHubPackageProvider, - GitHubUserProvider, PackageProvider] - - -class BinaryNotFoundError(Exception): - """If a necessary executable is not found in the PATH on the system""" - - pass - - -class NonCleanExitError(Exception): - """ - When an subprocess does not exit cleanly - - :param returncode: - The command line integer return code of the subprocess - """ - - def __init__(self, returncode): - self.returncode = returncode - - def __str__(self): - return repr(self.returncode) - - -class NonHttpError(Exception): - """If a downloader had a non-clean exit, but it was not due to an HTTP error""" - - pass - - -class Downloader(): - """ - A base downloader that actually performs downloading URLs - - The SSL module is not included with the bundled Python for Linux - users of Sublime Text, so Linux machines will fall back to using curl - or wget for HTTPS URLs. - """ - - def check_certs(self, domain, timeout): - """ - Ensures that the SSL CA cert for a domain is present on the machine - - :param domain: - The domain to ensure there is a CA cert for - - :param timeout: - The int timeout for downloading the CA cert from the channel - - :return: - The CA cert bundle path on success, or False on error - """ - - cert_match = False - - certs_list = self.settings.get('certs', {}) - certs_dir = os.path.join(sublime.packages_path(), 'Package Control', - 'certs') - ca_bundle_path = os.path.join(certs_dir, 'ca-bundle.crt') - - cert_info = certs_list.get(domain) - if cert_info: - cert_match = self.locate_cert(certs_dir, cert_info[0], cert_info[1]) - - wildcard_info = certs_list.get('*') - if wildcard_info: - cert_match = self.locate_cert(certs_dir, wildcard_info[0], wildcard_info[1]) or cert_match - - if not cert_match: - print '%s: No CA certs available for %s.' % (__name__, domain) - return False - - return ca_bundle_path - - def locate_cert(self, certs_dir, cert_id, location): - """ - Makes sure the SSL cert specified has been added to the CA cert - bundle that is present on the machine - - :param certs_dir: - The path of the folder that contains the cert files - - :param cert_id: - The identifier for CA cert(s). For those provided by the channel - system, this will be an md5 of the contents of the cert(s). For - user-provided certs, this is something they provide. - - :param location: - An http(s) URL, or absolute filesystem path to the CA cert(s) - - :return: - If the cert specified (by cert_id) is present on the machine and - part of the ca-bundle.crt file in the certs_dir - """ - - cert_path = os.path.join(certs_dir, cert_id) - if not os.path.exists(cert_path): - if str(location) != '': - if re.match('^https?://', location): - contents = self.download_cert(cert_id, location) - else: - contents = self.load_cert(cert_id, location) - if contents: - self.save_cert(certs_dir, cert_id, contents) - return True - return False - return True - - def download_cert(self, cert_id, url): - """ - Downloads CA cert(s) from a URL - - :param cert_id: - The identifier for CA cert(s). For those provided by the channel - system, this will be an md5 of the contents of the cert(s). For - user-provided certs, this is something they provide. - - :param url: - An http(s) URL to the CA cert(s) - - :return: - The contents of the CA cert(s) - """ - - cert_downloader = self.__class__(self.settings) - return cert_downloader.download(url, - 'Error downloading CA certs for %s.' % (domain), timeout, 1) - - def load_cert(self, cert_id, path): - """ - Copies CA cert(s) from a file path - - :param cert_id: - The identifier for CA cert(s). For those provided by the channel - system, this will be an md5 of the contents of the cert(s). For - user-provided certs, this is something they provide. - - :param path: - The absolute filesystem path to a file containing the CA cert(s) - - :return: - The contents of the CA cert(s) - """ - - if os.path.exists(path): - with open(path, 'rb') as f: - return f.read() - - def save_cert(self, certs_dir, cert_id, contents): - """ - Saves CA cert(s) to the certs_dir (and ca-bundle.crt file) - - :param certs_dir: - The path of the folder that contains the cert files - - :param cert_id: - The identifier for CA cert(s). For those provided by the channel - system, this will be an md5 of the contents of the cert(s). For - user-provided certs, this is something they provide. - - :param contents: - The contents of the CA cert(s) - """ - - ca_bundle_path = os.path.join(certs_dir, 'ca-bundle.crt') - cert_path = os.path.join(certs_dir, cert_id) - with open(cert_path, 'wb') as f: - f.write(contents) - with open(ca_bundle_path, 'ab') as f: - f.write("\n" + contents) - - def decode_response(self, encoding, response): - if encoding == 'gzip': - return gzip.GzipFile(fileobj=StringIO.StringIO(response)).read() - elif encoding == 'deflate': - decompresser = zlib.decompressobj(-zlib.MAX_WBITS) - return decompresser.decompress(response) + decompresser.flush() - return response - - -class CliDownloader(Downloader): - """ - Base for downloaders that use a command line program - - :param settings: - A dict of the various Package Control settings. The Sublime Text - Settings API is not used because this code is run in a thread. - """ - - def __init__(self, settings): - self.settings = settings - - def clean_tmp_file(self): - if os.path.exists(self.tmp_file): - os.remove(self.tmp_file) - - def find_binary(self, name): - """ - Finds the given executable name in the system PATH - - :param name: - The exact name of the executable to find - - :return: - The absolute path to the executable - - :raises: - BinaryNotFoundError when the executable can not be found - """ - - for dir in os.environ['PATH'].split(os.pathsep): - path = os.path.join(dir, name) - if os.path.exists(path): - return path - - raise BinaryNotFoundError('The binary %s could not be located' % name) - - def execute(self, args): - """ - Runs the executable and args and returns the result - - :param args: - A list of the executable path and all arguments to be passed to it - - :return: - The text output of the executable - - :raises: - NonCleanExitError when the executable exits with an error - """ - - if self.settings.get('debug'): - print u"%s: Trying to execute command %s" % ( - __name__, create_cmd(args)) - - proc = subprocess.Popen(args, stdin=subprocess.PIPE, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - - output = proc.stdout.read() - self.stderr = proc.stderr.read() - returncode = proc.wait() - if returncode != 0: - error = NonCleanExitError(returncode) - error.stderr = self.stderr - error.stdout = output - raise error - return output - - -class UrlLib2Downloader(Downloader): - """ - A downloader that uses the Python urllib2 module - - :param settings: - A dict of the various Package Control settings. The Sublime Text - Settings API is not used because this code is run in a thread. - """ - - def __init__(self, settings): - self.settings = settings - - def download(self, url, error_message, timeout, tries): - """ - Downloads a URL and returns the contents - - Uses the proxy settings from the Package Control.sublime-settings file, - however there seem to be a decent number of proxies that this code - does not work with. Patches welcome! - - :param url: - The URL to download - - :param error_message: - A string to include in the console error that is printed - when an error occurs - - :param timeout: - The int number of seconds to set the timeout to - - :param tries: - The int number of times to try and download the URL in the case of - a timeout or HTTP 503 error - - :return: - The string contents of the URL, or False on error - """ - - http_proxy = self.settings.get('http_proxy') - https_proxy = self.settings.get('https_proxy') - if http_proxy or https_proxy: - proxies = {} - if http_proxy: - proxies['http'] = http_proxy - if https_proxy: - proxies['https'] = https_proxy - proxy_handler = urllib2.ProxyHandler(proxies) - else: - proxy_handler = urllib2.ProxyHandler() - - password_manager = urllib2.HTTPPasswordMgrWithDefaultRealm() - proxy_username = self.settings.get('proxy_username') - proxy_password = self.settings.get('proxy_password') - if proxy_username and proxy_password: - if http_proxy: - password_manager.add_password(None, http_proxy, proxy_username, - proxy_password) - if https_proxy: - password_manager.add_password(None, https_proxy, proxy_username, - proxy_password) - - handlers = [proxy_handler] - if os.name == 'nt': - ntlm_auth_handler = ProxyNtlmAuthHandler(password_manager) - handlers.append(ntlm_auth_handler) - - basic_auth_handler = urllib2.ProxyBasicAuthHandler(password_manager) - digest_auth_handler = urllib2.ProxyDigestAuthHandler(password_manager) - handlers.extend([digest_auth_handler, basic_auth_handler]) - - debug = self.settings.get('debug') - - if debug: - print u"%s: Urllib2 Debug Proxy" % __name__ - print u" http_proxy: %s" % http_proxy - print u" https_proxy: %s" % https_proxy - print u" proxy_username: %s" % proxy_username - print u" proxy_password: %s" % proxy_password - - secure_url_match = re.match('^https://([^/]+)', url) - if secure_url_match != None: - secure_domain = secure_url_match.group(1) - bundle_path = self.check_certs(secure_domain, timeout) - if not bundle_path: - return False - bundle_path = bundle_path.encode(sys.getfilesystemencoding()) - handlers.append(ValidatingHTTPSHandler(ca_certs=bundle_path, - debug=debug, passwd=password_manager, - user_agent=self.settings.get('user_agent'))) - else: - handlers.append(DebuggableHTTPHandler(debug=debug, - passwd=password_manager)) - urllib2.install_opener(urllib2.build_opener(*handlers)) - - while tries > 0: - tries -= 1 - try: - request = urllib2.Request(url, headers={ - "User-Agent": self.settings.get('user_agent'), - # Don't be alarmed if the response from the server does not - # select one of these since the server runs a relatively new - # version of OpenSSL which supports compression on the SSL - # layer, and Apache will use that instead of HTTP-level - # encoding. - "Accept-Encoding": "gzip,deflate"}) - http_file = urllib2.urlopen(request, timeout=timeout) - self.handle_rate_limit(http_file, url) - result = http_file.read() - encoding = http_file.headers.get('Content-Encoding') - return self.decode_response(encoding, result) - - except (httplib.HTTPException) as (e): - print '%s: %s HTTP exception %s (%s) downloading %s.' % ( - __name__, error_message, e.__class__.__name__, - unicode_from_os(e), url) - - except (urllib2.HTTPError) as (e): - # Make sure we obey Github's rate limiting headers - self.handle_rate_limit(e, url) - - # Bitbucket and Github return 503 a decent amount - if unicode_from_os(e.code) == '503': - print ('%s: Downloading %s was rate limited, ' + - 'trying again') % (__name__, url) - continue - print '%s: %s HTTP error %s downloading %s.' % (__name__, - error_message, unicode_from_os(e.code), url) - - except (urllib2.URLError) as (e): - - # Bitbucket and Github timeout a decent amount - if unicode_from_os(e.reason) == 'The read operation timed out' \ - or unicode_from_os(e.reason) == 'timed out': - print (u'%s: Downloading %s timed out, trying ' + - u'again') % (__name__, url) - continue - print u'%s: %s URL error %s downloading %s.' % (__name__, - error_message, unicode_from_os(e.reason), url) - break - return False - - def handle_rate_limit(self, response, url): - """ - Checks the headers of a respone object to make sure we are obeying the - rate limit - - :param response: - The response object that has a headers dict - - :param url: - The URL that was requested - - :raises: - RateLimitException when the rate limit has been hit - """ - - limit_remaining = response.headers.get('X-RateLimit-Remaining', 1) - if str(limit_remaining) == '0': - hostname = urlparse.urlparse(url).hostname - limit = response.headers.get('X-RateLimit-Limit', 1) - raise RateLimitException(hostname, limit) - - -class WgetDownloader(CliDownloader): - """ - A downloader that uses the command line program wget - - :param settings: - A dict of the various Package Control settings. The Sublime Text - Settings API is not used because this code is run in a thread. - """ - - def __init__(self, settings): - self.settings = settings - self.debug = settings.get('debug') - self.wget = self.find_binary('wget') - - def download(self, url, error_message, timeout, tries): - """ - Downloads a URL and returns the contents - - :param url: - The URL to download - - :param error_message: - A string to include in the console error that is printed - when an error occurs - - :param timeout: - The int number of seconds to set the timeout to - - :param tries: - The int number of times to try and download the URL in the case of - a timeout or HTTP 503 error - - :return: - The string contents of the URL, or False on error - """ - - if not self.wget: - return False - - self.tmp_file = tempfile.NamedTemporaryFile().name - command = [self.wget, '--connect-timeout=' + str(int(timeout)), '-o', - self.tmp_file, '-O', '-', '-U', - self.settings.get('user_agent'), '--header', - # Don't be alarmed if the response from the server does not select - # one of these since the server runs a relatively new version of - # OpenSSL which supports compression on the SSL layer, and Apache - # will use that instead of HTTP-level encoding. - 'Accept-Encoding: gzip,deflate'] - - secure_url_match = re.match('^https://([^/]+)', url) - if secure_url_match != None: - secure_domain = secure_url_match.group(1) - bundle_path = self.check_certs(secure_domain, timeout) - if not bundle_path: - return False - command.append(u'--ca-certificate=' + bundle_path) - - if self.debug: - command.append('-d') - else: - command.append('-S') - - http_proxy = self.settings.get('http_proxy') - https_proxy = self.settings.get('https_proxy') - proxy_username = self.settings.get('proxy_username') - proxy_password = self.settings.get('proxy_password') - - if proxy_username: - command.append(u"--proxy-user=%s" % proxy_username) - if proxy_password: - command.append(u"--proxy-password=%s" % proxy_password) - - if self.debug: - print u"%s: Wget Debug Proxy" % __name__ - print u" http_proxy: %s" % http_proxy - print u" https_proxy: %s" % https_proxy - print u" proxy_username: %s" % proxy_username - print u" proxy_password: %s" % proxy_password - - command.append(url) - - if http_proxy: - os.putenv('http_proxy', http_proxy) - if https_proxy: - os.putenv('https_proxy', https_proxy) - - while tries > 0: - tries -= 1 - try: - result = self.execute(command) - - general, headers = self.parse_output() - encoding = headers.get('content-encoding') - if encoding: - result = self.decode_response(encoding, result) - - return result - - except (NonCleanExitError) as (e): - - try: - general, headers = self.parse_output() - self.handle_rate_limit(headers, url) - - if general['status'] == '503': - # GitHub and BitBucket seem to rate limit via 503 - print ('%s: Downloading %s was rate limited' + - ', trying again') % (__name__, url) - continue - - error_string = 'HTTP error %s %s' % (general['status'], - general['message']) - - except (NonHttpError) as (e): - - error_string = unicode_from_os(e) - - # GitHub and BitBucket seem to time out a lot - if error_string.find('timed out') != -1: - print ('%s: Downloading %s timed out, ' + - 'trying again') % (__name__, url) - continue - - print (u'%s: %s %s downloading %s.' % (__name__, error_message, - error_string, url)).encode('UTF-8') - - break - return False - - def parse_output(self): - with open(self.tmp_file, 'r') as f: - output = f.read().splitlines() - self.clean_tmp_file() - - error = None - header_lines = [] - if self.debug: - section = 'General' - last_section = None - for line in output: - if section == 'General': - if self.skippable_line(line): - continue - - # Skip blank lines - if line.strip() == '': - continue - - # Error lines - if line[0:5] == 'wget:': - error = line[5:].strip() - if line[0:7] == 'failed:': - error = line[7:].strip() - - if line == '---request begin---': - section = 'Write' - continue - elif line == '---request end---': - section = 'General' - continue - elif line == '---response begin---': - section = 'Read' - continue - elif line == '---response end---': - section = 'General' - continue - - if section != last_section: - print "%s: Wget HTTP Debug %s" % (__name__, section) - - if section == 'Read': - header_lines.append(line) - - print ' ' + line - last_section = section - - else: - for line in output: - if self.skippable_line(line): - continue - - # Check the resolving and connecting to lines for errors - if re.match('(Resolving |Connecting to )', line): - failed_match = re.search(' failed: (.*)$', line) - if failed_match: - error = failed_match.group(1).strip() - - # Error lines - if line[0:5] == 'wget:': - error = line[5:].strip() - if line[0:7] == 'failed:': - error = line[7:].strip() - - if line[0:2] == ' ': - header_lines.append(line.lstrip()) - - if error: - raise NonHttpError(error) - - return self.parse_headers(header_lines) - - def skippable_line(self, line): - # Skip date lines - if re.match('--\d{4}-\d{2}-\d{2}', line): - return True - if re.match('\d{4}-\d{2}-\d{2}', line): - return True - # Skip HTTP status code lines since we already have that info - if re.match('\d{3} ', line): - return True - # Skip Saving to and progress lines - if re.match('(Saving to:|\s*\d+K)', line): - return True - # Skip notice about ignoring body on HTTP error - if re.match('Skipping \d+ byte', line): - return True - - def parse_headers(self, output=None): - if not output: - with open(self.tmp_file, 'r') as f: - output = f.read().splitlines() - self.clean_tmp_file() - - general = { - 'version': '0.9', - 'status': '200', - 'message': 'OK' - } - headers = {} - for line in output: - # When using the -S option, headers have two spaces before them, - # additionally, valid headers won't have spaces, so this is always - # a safe operation to perform - line = line.lstrip() - if line.find('HTTP/') == 0: - match = re.match('HTTP/(\d\.\d)\s+(\d+)\s+(.*)$', line) - general['version'] = match.group(1) - general['status'] = match.group(2) - general['message'] = match.group(3) - else: - name, value = line.split(':', 1) - headers[name.lower()] = value.strip() - - return (general, headers) - - def handle_rate_limit(self, headers, url): - limit_remaining = headers.get('x-ratelimit-remaining', '1') - limit = headers.get('x-ratelimit-limit', '1') - - if str(limit_remaining) == '0': - hostname = urlparse.urlparse(url).hostname - raise RateLimitException(hostname, limit) - - -class CurlDownloader(CliDownloader): - """ - A downloader that uses the command line program curl - - :param settings: - A dict of the various Package Control settings. The Sublime Text - Settings API is not used because this code is run in a thread. - """ - - def __init__(self, settings): - self.settings = settings - self.curl = self.find_binary('curl') - - def download(self, url, error_message, timeout, tries): - """ - Downloads a URL and returns the contents - - :param url: - The URL to download - - :param error_message: - A string to include in the console error that is printed - when an error occurs - - :param timeout: - The int number of seconds to set the timeout to - - :param tries: - The int number of times to try and download the URL in the case of - a timeout or HTTP 503 error - - :return: - The string contents of the URL, or False on error - """ - - if not self.curl: - return False - - self.tmp_file = tempfile.NamedTemporaryFile().name - command = [self.curl, '--user-agent', self.settings.get('user_agent'), - '--connect-timeout', str(int(timeout)), '-sSL', - # Don't be alarmed if the response from the server does not select - # one of these since the server runs a relatively new version of - # OpenSSL which supports compression on the SSL layer, and Apache - # will use that instead of HTTP-level encoding. - '--compressed', - # We have to capture the headers to check for rate limit info - '--dump-header', self.tmp_file] - - secure_url_match = re.match('^https://([^/]+)', url) - if secure_url_match != None: - secure_domain = secure_url_match.group(1) - bundle_path = self.check_certs(secure_domain, timeout) - if not bundle_path: - return False - command.extend(['--cacert', bundle_path]) - - debug = self.settings.get('debug') - if debug: - command.append('-v') - - http_proxy = self.settings.get('http_proxy') - https_proxy = self.settings.get('https_proxy') - proxy_username = self.settings.get('proxy_username') - proxy_password = self.settings.get('proxy_password') - - if debug: - print u"%s: Curl Debug Proxy" % __name__ - print u" http_proxy: %s" % http_proxy - print u" https_proxy: %s" % https_proxy - print u" proxy_username: %s" % proxy_username - print u" proxy_password: %s" % proxy_password - - if http_proxy or https_proxy: - command.append('--proxy-anyauth') - - if proxy_username or proxy_password: - command.extend(['-U', u"%s:%s" % (proxy_username, proxy_password)]) - - if http_proxy: - os.putenv('http_proxy', http_proxy) - if https_proxy: - os.putenv('HTTPS_PROXY', https_proxy) - - command.append(url) - - while tries > 0: - tries -= 1 - try: - output = self.execute(command) - - with open(self.tmp_file, 'r') as f: - headers = f.read() - self.clean_tmp_file() - - limit = 1 - limit_remaining = 1 - status = '200 OK' - for header in headers.splitlines(): - if header[0:5] == 'HTTP/': - status = re.sub('^HTTP/\d\.\d\s+', '', header) - if header.lower()[0:22] == 'x-ratelimit-remaining:': - limit_remaining = header.lower()[22:].strip() - if header.lower()[0:18] == 'x-ratelimit-limit:': - limit = header.lower()[18:].strip() - - if debug: - self.print_debug(self.stderr) - - if str(limit_remaining) == '0': - hostname = urlparse.urlparse(url).hostname - raise RateLimitException(hostname, limit) - - if status != '200 OK': - e = NonCleanExitError(22) - e.stderr = status - raise e - - return output - - except (NonCleanExitError) as (e): - # Stderr is used for both the error message and the debug info - # so we need to process it to extra the debug info - if self.settings.get('debug'): - e.stderr = self.print_debug(e.stderr) - - self.clean_tmp_file() - - if e.returncode == 22: - code = re.sub('^.*?(\d+)([\w\s]+)?$', '\\1', e.stderr) - if code == '503': - # GitHub and BitBucket seem to rate limit via 503 - print ('%s: Downloading %s was rate limited' + - ', trying again') % (__name__, url) - continue - error_string = 'HTTP error ' + code - elif e.returncode == 6: - error_string = 'URL error host not found' - elif e.returncode == 28: - # GitHub and BitBucket seem to time out a lot - print ('%s: Downloading %s timed out, trying ' + - 'again') % (__name__, url) - continue - else: - error_string = e.stderr.rstrip() - - print '%s: %s %s downloading %s.' % (__name__, error_message, - error_string, url) - break - - return False - - def print_debug(self, string): - section = 'General' - last_section = None - - output = '' - - for line in string.splitlines(): - # Placeholder for body of request - if line and line[0:2] == '{ ': - continue - - if len(line) > 1: - subtract = 0 - if line[0:2] == '* ': - section = 'General' - subtract = 2 - elif line[0:2] == '> ': - section = 'Write' - subtract = 2 - elif line[0:2] == '< ': - section = 'Read' - subtract = 2 - line = line[subtract:] - - # If the line does not start with "* ", "< ", "> " or " " - # then it is a real stderr message - if subtract == 0 and line[0:2] != ' ': - output += line - continue - - if line.strip() == '': - continue - - if section != last_section: - print "%s: Curl HTTP Debug %s" % (__name__, section) - - print ' ' + line - last_section = section - - return output - - -# A cache of channel and repository info to allow users to install multiple -# packages without having to wait for the metadata to be downloaded more -# than once. The keys are managed locally by the utilizing code. -_channel_repository_cache = {} - - -class RepositoryDownloader(threading.Thread): - """ - Downloads information about a repository in the background - - :param package_manager: - An instance of :class:`PackageManager` used to download files - - :param name_map: - The dict of name mapping for URL slug -> package name - - :param repo: - The URL of the repository to download info about - """ - - def __init__(self, package_manager, name_map, repo): - self.package_manager = package_manager - self.repo = repo - self.packages = {} - self.name_map = name_map - threading.Thread.__init__(self) - - def run(self): - for provider_class in _package_providers: - provider = provider_class(self.repo, self.package_manager) - if provider.match_url(): - break - packages = provider.get_packages() - if packages == False: - self.packages = False - return - - mapped_packages = {} - for package in packages.keys(): - mapped_package = self.name_map.get(package, package) - mapped_packages[mapped_package] = packages[package] - mapped_packages[mapped_package]['name'] = mapped_package - packages = mapped_packages - - self.packages = packages - self.renamed_packages = provider.get_renamed_packages() - self.unavailable_packages = provider.get_unavailable_packages() - - -class VcsUpgrader(): - """ - Base class for updating packages that are a version control repository on local disk - - :param vcs_binary: - The full filesystem path to the executable for the version control - system. May be set to None to allow the code to try and find it. - - :param update_command: - The command to pass to the version control executable to update the - repository. - - :param working_copy: - The local path to the working copy/package directory - - :param cache_length: - The lenth of time to cache if incoming changesets are available - """ - - def __init__(self, vcs_binary, update_command, working_copy, cache_length, debug): - self.binary = vcs_binary - self.update_command = update_command - self.working_copy = working_copy - self.cache_length = cache_length - self.debug = debug - - def execute(self, args, dir): - """ - Creates a subprocess with the executable/args - - :param args: - A list of the executable path and all arguments to it - - :param dir: - The directory in which to run the executable - - :return: A string of the executable output - """ - - startupinfo = None - if os.name == 'nt': - startupinfo = subprocess.STARTUPINFO() - startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW - - if self.debug: - print u"%s: Trying to execute command %s" % ( - __name__, create_cmd(args)) - proc = subprocess.Popen(args, stdin=subprocess.PIPE, - stdout=subprocess.PIPE, stderr=subprocess.STDOUT, - startupinfo=startupinfo, cwd=dir) - - return proc.stdout.read().replace('\r\n', '\n').rstrip(' \n\r') - - def find_binary(self, name): - """ - Locates the executable by looking in the PATH and well-known directories - - :param name: - The string filename of the executable - - :return: The filesystem path to the executable, or None if not found - """ - - if self.binary: - if self.debug: - print u"%s: Using \"%s_binary\" from settings \"%s\"" % ( - __name__, self.vcs_type, self.binary) - return self.binary - - # Try the path first - for dir in os.environ['PATH'].split(os.pathsep): - path = os.path.join(dir, name) - if os.path.exists(path): - if self.debug: - print u"%s: Found %s at \"%s\"" % (__name__, self.vcs_type, - path) - return path - - # This is left in for backwards compatibility and for windows - # users who may have the binary, albeit in a common dir that may - # not be part of the PATH - if os.name == 'nt': - dirs = ['C:\\Program Files\\Git\\bin', - 'C:\\Program Files (x86)\\Git\\bin', - 'C:\\Program Files\\TortoiseGit\\bin', - 'C:\\Program Files\\Mercurial', - 'C:\\Program Files (x86)\\Mercurial', - 'C:\\Program Files (x86)\\TortoiseHg', - 'C:\\Program Files\\TortoiseHg', - 'C:\\cygwin\\bin'] - else: - dirs = ['/usr/local/git/bin'] - - for dir in dirs: - path = os.path.join(dir, name) - if os.path.exists(path): - if self.debug: - print u"%s: Found %s at \"%s\"" % (__name__, self.vcs_type, - path) - return path - - if self.debug: - print u"%s: Could not find %s on your machine" % (__name__, - self.vcs_type) - return None - - -class GitUpgrader(VcsUpgrader): - """ - Allows upgrading a local git-repository-based package - """ - - vcs_type = 'git' - - def retrieve_binary(self): - """ - Returns the path to the git executable - - :return: The string path to the executable or False on error - """ - - name = 'git' - if os.name == 'nt': - name += '.exe' - binary = self.find_binary(name) - if binary and os.path.isdir(binary): - full_path = os.path.join(binary, name) - if os.path.exists(full_path): - binary = full_path - if not binary: - sublime.error_message(('%s: Unable to find %s. ' + - 'Please set the git_binary setting by accessing the ' + - 'Preferences > Package Settings > %s > ' + - u'Settings – User menu entry. The Settings – Default entry ' + - 'can be used for reference, but changes to that will be ' + - 'overwritten upon next upgrade.') % (__name__, name, __name__)) - return False - - if os.name == 'nt': - tortoise_plink = self.find_binary('TortoisePlink.exe') - if tortoise_plink: - os.environ.setdefault('GIT_SSH', tortoise_plink) - return binary - - def run(self): - """ - Updates the repository with remote changes - - :return: False or error, or True on success - """ - - binary = self.retrieve_binary() - if not binary: - return False - args = [binary] - args.extend(self.update_command) - self.execute(args, self.working_copy) - return True - - def incoming(self): - """:return: bool if remote revisions are available""" - - cache_key = self.working_copy + '.incoming' - working_copy_cache = _channel_repository_cache.get(cache_key) - if working_copy_cache and working_copy_cache.get('time') > \ - time.time(): - return working_copy_cache.get('data') - - binary = self.retrieve_binary() - if not binary: - return False - self.execute([binary, 'fetch'], self.working_copy) - args = [binary, 'log'] - args.append('..' + '/'.join(self.update_command[-2:])) - output = self.execute(args, self.working_copy) - incoming = len(output) > 0 - - _channel_repository_cache[cache_key] = { - 'time': time.time() + self.cache_length, - 'data': incoming - } - return incoming - - -class HgUpgrader(VcsUpgrader): - """ - Allows upgrading a local mercurial-repository-based package - """ - - vcs_type = 'hg' - - def retrieve_binary(self): - """ - Returns the path to the hg executable - - :return: The string path to the executable or False on error - """ - - name = 'hg' - if os.name == 'nt': - name += '.exe' - binary = self.find_binary(name) - if binary and os.path.isdir(binary): - full_path = os.path.join(binary, name) - if os.path.exists(full_path): - binary = full_path - if not binary: - sublime.error_message(('%s: Unable to find %s. ' + - 'Please set the hg_binary setting by accessing the ' + - 'Preferences > Package Settings > %s > ' + - u'Settings – User menu entry. The Settings – Default entry ' + - 'can be used for reference, but changes to that will be ' + - 'overwritten upon next upgrade.') % (__name__, name, __name__)) - return False - return binary - - def run(self): - """ - Updates the repository with remote changes - - :return: False or error, or True on success - """ - - binary = self.retrieve_binary() - if not binary: - return False - args = [binary] - args.extend(self.update_command) - self.execute(args, self.working_copy) - return True - - def incoming(self): - """:return: bool if remote revisions are available""" - - cache_key = self.working_copy + '.incoming' - working_copy_cache = _channel_repository_cache.get(cache_key) - if working_copy_cache and working_copy_cache.get('time') > \ - time.time(): - return working_copy_cache.get('data') - - binary = self.retrieve_binary() - if not binary: - return False - args = [binary, 'in', '-q'] - args.append(self.update_command[-1]) - output = self.execute(args, self.working_copy) - incoming = len(output) > 0 - - _channel_repository_cache[cache_key] = { - 'time': time.time() + self.cache_length, - 'data': incoming - } - return incoming - - -def clear_directory(directory, ignore_paths=None): - was_exception = False - for root, dirs, files in os.walk(directory, topdown=False): - paths = [os.path.join(root, f) for f in files] - paths.extend([os.path.join(root, d) for d in dirs]) - - for path in paths: - try: - # Don't delete the metadata file, that way we have it - # when the reinstall happens, and the appropriate - # usage info can be sent back to the server - if ignore_paths and path in ignore_paths: - continue - if os.path.isdir(path): - os.rmdir(path) - else: - os.remove(path) - except (OSError, IOError) as (e): - was_exception = True - - return not was_exception - - - -class PackageManager(): - """ - Allows downloading, creating, installing, upgrading, and deleting packages - - Delegates metadata retrieval to the _channel_providers and - _package_providers classes. Uses VcsUpgrader-based classes for handling - git and hg repositories in the Packages folder. Downloader classes are - utilized to fetch contents of URLs. - - Also handles displaying package messaging, and sending usage information to - the usage server. - """ - - def __init__(self): - # Here we manually copy the settings since sublime doesn't like - # code accessing settings from threads - self.settings = {} - settings = sublime.load_settings(__name__ + '.sublime-settings') - for setting in ['timeout', 'repositories', 'repository_channels', - 'package_name_map', 'dirs_to_ignore', 'files_to_ignore', - 'package_destination', 'cache_length', 'auto_upgrade', - 'files_to_ignore_binary', 'files_to_keep', 'dirs_to_keep', - 'git_binary', 'git_update_command', 'hg_binary', - 'hg_update_command', 'http_proxy', 'https_proxy', - 'auto_upgrade_ignore', 'auto_upgrade_frequency', - 'submit_usage', 'submit_url', 'renamed_packages', - 'files_to_include', 'files_to_include_binary', 'certs', - 'ignore_vcs_packages', 'proxy_username', 'proxy_password', - 'debug', 'user_agent']: - if settings.get(setting) == None: - continue - self.settings[setting] = settings.get(setting) - - # https_proxy will inherit from http_proxy unless it is set to a - # string value or false - no_https_proxy = self.settings.get('https_proxy') in ["", None] - if no_https_proxy and self.settings.get('http_proxy'): - self.settings['https_proxy'] = self.settings.get('http_proxy') - if self.settings['https_proxy'] == False: - self.settings['https_proxy'] = '' - - self.settings['platform'] = sublime.platform() - self.settings['version'] = sublime.version() - - def compare_versions(self, version1, version2): - """ - Compares to version strings to see which is greater - - Date-based version numbers (used by GitHub and BitBucket providers) - are automatically pre-pended with a 0 so they are always less than - version 1.0. - - :return: - -1 if version1 is less than version2 - 0 if they are equal - 1 if version1 is greater than version2 - """ - - def date_compat(v): - # We prepend 0 to all date-based version numbers so that developers - # may switch to explicit versioning from GitHub/BitBucket - # versioning based on commit dates - date_match = re.match('(\d{4})\.(\d{2})\.(\d{2})\.(\d{2})\.(\d{2})\.(\d{2})$', v) - if date_match: - v = '0.%s.%s.%s.%s.%s.%s' % date_match.groups() - return v - - def semver_compat(v): - # When translating dates into semver, the way to get each date - # segment into the version is to treat the year and month as - # minor and patch, and then the rest as a numeric build version - # with four different parts. The result looks like: - # 0.2012.11+10.31.23.59 - date_match = re.match('(\d{4}(?:\.\d{2}){2})\.(\d{2}(?:\.\d{2}){3})$', v) - if date_match: - v = '%s+%s' % (date_match.group(1), date_match.group(2)) - - # Semver must have major, minor, patch - elif re.match('^\d+$', v): - v += '.0.0' - elif re.match('^\d+\.\d+$', v): - v += '.0' - return v - - def cmp_compat(v): - return [int(x) for x in re.sub(r'(\.0+)*$', '', v).split(".")] - - version1 = date_compat(version1) - version2 = date_compat(version2) - try: - return semver.compare(semver_compat(version1), semver_compat(version2)) - except (ValueError): - return cmp(cmp_compat(version1), cmp_compat(version2)) - - def download_url(self, url, error_message): - """ - Downloads a URL and returns the contents - - :param url: - The string URL to download - - :param error_message: - The error message to include if the download fails - - :return: - The string contents of the URL, or False on error - """ - - has_ssl = 'ssl' in sys.modules and hasattr(urllib2, 'HTTPSHandler') - is_ssl = re.search('^https://', url) != None - downloader = None - - if (is_ssl and has_ssl) or not is_ssl: - downloader = UrlLib2Downloader(self.settings) - else: - for downloader_class in [CurlDownloader, WgetDownloader]: - try: - downloader = downloader_class(self.settings) - break - except (BinaryNotFoundError): - pass - - if not downloader: - sublime.error_message(('%s: Unable to download %s due to no ' + - 'ssl module available and no capable program found. Please ' + - 'install curl or wget.') % (__name__, url)) - return False - - url = url.replace(' ', '%20') - hostname = urlparse.urlparse(url).hostname.lower() - timeout = self.settings.get('timeout', 3) - - rate_limited_cache = _channel_repository_cache.get('rate_limited_domains', {}) - if rate_limited_cache.get('time') and rate_limited_cache.get('time') > time.time(): - rate_limited_domains = rate_limited_cache.get('data', []) - else: - rate_limited_domains = [] - - if self.settings.get('debug'): - try: - ip = socket.gethostbyname(hostname) - except (socket.gaierror) as (e): - ip = unicode_from_os(e) - - print u"%s: Download Debug" % __name__ - print u" URL: %s" % url - print u" Resolved IP: %s" % ip - print u" Timeout: %s" % str(timeout) - - if hostname in rate_limited_domains: - if self.settings.get('debug'): - print u" Skipping due to hitting rate limit for %s" % hostname - return False - - try: - return downloader.download(url, error_message, timeout, 3) - except (RateLimitException) as (e): - - rate_limited_domains.append(hostname) - _channel_repository_cache['rate_limited_domains'] = { - 'data': rate_limited_domains, - 'time': time.time() + self.settings.get('cache_length', - 300) - } - - print ('%s: Hit rate limit of %s for %s, skipping all futher ' + - 'download requests for this domain') % (__name__, - e.limit, e.host) - return False - - def get_metadata(self, package): - """ - Returns the package metadata for an installed package - - :return: - A dict with the keys: - version - url - description - or an empty dict on error - """ - - metadata_filename = os.path.join(self.get_package_dir(package), - 'package-metadata.json') - if os.path.exists(metadata_filename): - with open(metadata_filename) as f: - try: - return json.load(f) - except (ValueError): - return {} - return {} - - def list_repositories(self): - """ - Returns a master list of all repositories pulled from all sources - - These repositories come from the channels specified in the - "repository_channels" setting, plus any repositories listed in the - "repositories" setting. - - :return: - A list of all available repositories - """ - - repositories = self.settings.get('repositories') - repository_channels = self.settings.get('repository_channels') - for channel in repository_channels: - channel = channel.strip() - - channel_repositories = None - - # Caches various info from channels for performance - cache_key = channel + '.repositories' - repositories_cache = _channel_repository_cache.get(cache_key) - if repositories_cache and repositories_cache.get('time') > \ - time.time(): - channel_repositories = repositories_cache.get('data') - - name_map_cache_key = channel + '.package_name_map' - name_map_cache = _channel_repository_cache.get( - name_map_cache_key) - if name_map_cache and name_map_cache.get('time') > \ - time.time(): - name_map = name_map_cache.get('data') - name_map.update(self.settings.get('package_name_map', {})) - self.settings['package_name_map'] = name_map - - renamed_cache_key = channel + '.renamed_packages' - renamed_cache = _channel_repository_cache.get( - renamed_cache_key) - if renamed_cache and renamed_cache.get('time') > \ - time.time(): - renamed_packages = renamed_cache.get('data') - renamed_packages.update(self.settings.get('renamed_packages', - {})) - self.settings['renamed_packages'] = renamed_packages - - unavailable_cache_key = channel + '.unavailable_packages' - unavailable_cache = _channel_repository_cache.get( - unavailable_cache_key) - if unavailable_cache and unavailable_cache.get('time') > \ - time.time(): - unavailable_packages = unavailable_cache.get('data') - unavailable_packages.extend(self.settings.get('unavailable_packages', - [])) - self.settings['unavailable_packages'] = unavailable_packages - - certs_cache_key = channel + '.certs' - certs_cache = _channel_repository_cache.get(certs_cache_key) - if certs_cache and certs_cache.get('time') > time.time(): - certs = self.settings.get('certs', {}) - certs.update(certs_cache.get('data')) - self.settings['certs'] = certs - - # If any of the info was not retrieved from the cache, we need to - # grab the channel to get it - if channel_repositories == None or \ - self.settings.get('package_name_map') == None or \ - self.settings.get('renamed_packages') == None: - - for provider_class in _channel_providers: - provider = provider_class(channel, self) - if provider.match_url(): - break - - channel_repositories = provider.get_repositories() - - if channel_repositories == False: - continue - _channel_repository_cache[cache_key] = { - 'time': time.time() + self.settings.get('cache_length', - 300), - 'data': channel_repositories - } - - for repo in channel_repositories: - if provider.get_packages(repo) == False: - continue - packages_cache_key = repo + '.packages' - _channel_repository_cache[packages_cache_key] = { - 'time': time.time() + self.settings.get('cache_length', - 300), - 'data': provider.get_packages(repo) - } - - # Have the local name map override the one from the channel - name_map = provider.get_name_map() - name_map.update(self.settings.get('package_name_map', {})) - self.settings['package_name_map'] = name_map - _channel_repository_cache[name_map_cache_key] = { - 'time': time.time() + self.settings.get('cache_length', - 300), - 'data': name_map - } - - renamed_packages = provider.get_renamed_packages() - _channel_repository_cache[renamed_cache_key] = { - 'time': time.time() + self.settings.get('cache_length', - 300), - 'data': renamed_packages - } - if renamed_packages: - self.settings['renamed_packages'] = self.settings.get( - 'renamed_packages', {}) - self.settings['renamed_packages'].update(renamed_packages) - - unavailable_packages = provider.get_unavailable_packages() - _channel_repository_cache[unavailable_cache_key] = { - 'time': time.time() + self.settings.get('cache_length', - 300), - 'data': unavailable_packages - } - if unavailable_packages: - self.settings['unavailable_packages'] = self.settings.get( - 'unavailable_packages', []) - self.settings['unavailable_packages'].extend(unavailable_packages) - - certs = provider.get_certs() - _channel_repository_cache[certs_cache_key] = { - 'time': time.time() + self.settings.get('cache_length', - 300), - 'data': certs - } - if certs: - self.settings['certs'] = self.settings.get('certs', {}) - self.settings['certs'].update(certs) - - repositories.extend(channel_repositories) - return [repo.strip() for repo in repositories] - - def list_available_packages(self): - """ - Returns a master list of every available package from all sources - - :return: - A dict in the format: - { - 'Package Name': { - # Package details - see example-packages.json for format - }, - ... - } - """ - - repositories = self.list_repositories() - packages = {} - downloaders = [] - grouped_downloaders = {} - - # Repositories are run in reverse order so that the ones first - # on the list will overwrite those last on the list - for repo in repositories[::-1]: - repository_packages = None - - cache_key = repo + '.packages' - packages_cache = _channel_repository_cache.get(cache_key) - if packages_cache and packages_cache.get('time') > \ - time.time(): - repository_packages = packages_cache.get('data') - packages.update(repository_packages) - - if repository_packages == None: - downloader = RepositoryDownloader(self, - self.settings.get('package_name_map', {}), repo) - domain = re.sub('^https?://[^/]*?(\w+\.\w+)($|/.*$)', '\\1', - repo) - - # downloaders are grouped by domain so that multiple can - # be run in parallel without running into API access limits - if not grouped_downloaders.get(domain): - grouped_downloaders[domain] = [] - grouped_downloaders[domain].append(downloader) - - # Allows creating a separate named function for each downloader - # delay. Not having this contained in a function causes all of the - # schedules to reference the same downloader.start() - def schedule(downloader, delay): - downloader.has_started = False - - def inner(): - downloader.start() - downloader.has_started = True - sublime.set_timeout(inner, delay) - - # Grabs every repo grouped by domain and delays the start - # of each download from that domain by a fixed amount - for domain_downloaders in grouped_downloaders.values(): - for i in range(len(domain_downloaders)): - downloader = domain_downloaders[i] - downloaders.append(downloader) - schedule(downloader, i * 150) - - complete = [] - - # Wait for all of the downloaders to finish - while downloaders: - downloader = downloaders.pop() - if downloader.has_started: - downloader.join() - complete.append(downloader) - else: - downloaders.insert(0, downloader) - - # Grabs the results and stuff if all in the cache - for downloader in complete: - repository_packages = downloader.packages - if repository_packages == False: - continue - cache_key = downloader.repo + '.packages' - _channel_repository_cache[cache_key] = { - 'time': time.time() + self.settings.get('cache_length', 300), - 'data': repository_packages - } - packages.update(repository_packages) - - renamed_packages = downloader.renamed_packages - if renamed_packages == False: - continue - renamed_cache_key = downloader.repo + '.renamed_packages' - _channel_repository_cache[renamed_cache_key] = { - 'time': time.time() + self.settings.get('cache_length', 300), - 'data': renamed_packages - } - if renamed_packages: - self.settings['renamed_packages'] = self.settings.get( - 'renamed_packages', {}) - self.settings['renamed_packages'].update(renamed_packages) - - unavailable_packages = downloader.unavailable_packages - unavailable_cache_key = downloader.repo + '.unavailable_packages' - _channel_repository_cache[unavailable_cache_key] = { - 'time': time.time() + self.settings.get('cache_length', 300), - 'data': unavailable_packages - } - if unavailable_packages: - self.settings['unavailable_packages'] = self.settings.get( - 'unavailable_packages', []) - self.settings['unavailable_packages'].extend(unavailable_packages) - - return packages - - def list_packages(self): - """ :return: A list of all installed, non-default, package names""" - - package_names = os.listdir(sublime.packages_path()) - package_names = [path for path in package_names if - os.path.isdir(os.path.join(sublime.packages_path(), path))] - - # Ignore things to be deleted - ignored = [] - for package in package_names: - cleanup_file = os.path.join(sublime.packages_path(), package, - 'package-control.cleanup') - if os.path.exists(cleanup_file): - ignored.append(package) - - packages = list(set(package_names) - set(ignored) - - set(self.list_default_packages())) - packages = sorted(packages, key=lambda s: s.lower()) - - return packages - - def list_all_packages(self): - """ :return: A list of all installed package names, including default packages""" - - packages = os.listdir(sublime.packages_path()) - packages = sorted(packages, key=lambda s: s.lower()) - return packages - - def list_default_packages(self): - """ :return: A list of all default package names""" - - files = os.listdir(os.path.join(os.path.dirname( - sublime.packages_path()), 'Pristine Packages')) - files = list(set(files) - set(os.listdir( - sublime.installed_packages_path()))) - packages = [file.replace('.sublime-package', '') for file in files] - packages = sorted(packages, key=lambda s: s.lower()) - return packages - - def get_package_dir(self, package): - """:return: The full filesystem path to the package directory""" - - return os.path.join(sublime.packages_path(), package) - - def get_mapped_name(self, package): - """:return: The name of the package after passing through mapping rules""" - - return self.settings.get('package_name_map', {}).get(package, package) - - def create_package(self, package_name, package_destination, - binary_package=False): - """ - Creates a .sublime-package file from the running Packages directory - - :param package_name: - The package to create a .sublime-package file for - - :param package_destination: - The full filesystem path of the directory to save the new - .sublime-package file in. - - :param binary_package: - If the created package should follow the binary package include/ - exclude patterns from the settings. These normally include a setup - to exclude .py files and include .pyc files, but that can be - changed via settings. - - :return: bool if the package file was successfully created - """ - - package_dir = self.get_package_dir(package_name) + '/' - - if not os.path.exists(package_dir): - sublime.error_message(('%s: The folder for the package name ' + - 'specified, %s, does not exist in %s') % - (__name__, package_name, sublime.packages_path())) - return False - - package_filename = package_name + '.sublime-package' - package_path = os.path.join(package_destination, - package_filename) - - if not os.path.exists(sublime.installed_packages_path()): - os.mkdir(sublime.installed_packages_path()) - - if os.path.exists(package_path): - os.remove(package_path) - - try: - package_file = zipfile.ZipFile(package_path, "w", - compression=zipfile.ZIP_DEFLATED) - except (OSError, IOError) as (exception): - sublime.error_message(('%s: An error occurred creating the ' + - 'package file %s in %s. %s') % (__name__, package_filename, - package_destination, unicode_from_os(exception))) - return False - - dirs_to_ignore = self.settings.get('dirs_to_ignore', []) - if not binary_package: - files_to_ignore = self.settings.get('files_to_ignore', []) - files_to_include = self.settings.get('files_to_include', []) - else: - files_to_ignore = self.settings.get('files_to_ignore_binary', []) - files_to_include = self.settings.get('files_to_include_binary', []) - - package_dir_regex = re.compile('^' + re.escape(package_dir)) - for root, dirs, files in os.walk(package_dir): - [dirs.remove(dir) for dir in dirs if dir in dirs_to_ignore] - paths = dirs - paths.extend(files) - for path in paths: - full_path = os.path.join(root, path) - relative_path = re.sub(package_dir_regex, '', full_path) - - ignore_matches = [fnmatch(relative_path, p) for p in files_to_ignore] - include_matches = [fnmatch(relative_path, p) for p in files_to_include] - if any(ignore_matches) and not any(include_matches): - continue - - if os.path.isdir(full_path): - continue - package_file.write(full_path, relative_path) - - package_file.close() - - return True - - def install_package(self, package_name): - """ - Downloads and installs (or upgrades) a package - - Uses the self.list_available_packages() method to determine where to - retrieve the package file from. - - The install process consists of: - - 1. Finding the package - 2. Downloading the .sublime-package/.zip file - 3. Extracting the package file - 4. Showing install/upgrade messaging - 5. Submitting usage info - 6. Recording that the package is installed - - :param package_name: - The package to download and install - - :return: bool if the package was successfully installed - """ - - packages = self.list_available_packages() - - if package_name in self.settings.get('unavailable_packages', []): - print ('%s: The package "%s" is not available ' + - 'on this platform.') % (__name__, package_name) - return False - - if package_name not in packages.keys(): - sublime.error_message(('%s: The package specified, %s, is ' + - 'not available.') % (__name__, package_name)) - return False - - download = packages[package_name]['downloads'][0] - url = download['url'] - - package_filename = package_name + \ - '.sublime-package' - package_path = os.path.join(sublime.installed_packages_path(), - package_filename) - pristine_package_path = os.path.join(os.path.dirname( - sublime.packages_path()), 'Pristine Packages', package_filename) - - package_dir = self.get_package_dir(package_name) - - package_metadata_file = os.path.join(package_dir, - 'package-metadata.json') - - if os.path.exists(os.path.join(package_dir, '.git')): - if self.settings.get('ignore_vcs_packages'): - sublime.error_message(('%s: Skipping git package %s since ' + - 'the setting ignore_vcs_packages is set to true') % - (__name__, package_name)) - return False - return GitUpgrader(self.settings['git_binary'], - self.settings['git_update_command'], package_dir, - self.settings['cache_length'], self.settings['debug']).run() - elif os.path.exists(os.path.join(package_dir, '.hg')): - if self.settings.get('ignore_vcs_packages'): - sublime.error_message(('%s: Skipping hg package %s since ' + - 'the setting ignore_vcs_packages is set to true') % - (__name__, package_name)) - return False - return HgUpgrader(self.settings['hg_binary'], - self.settings['hg_update_command'], package_dir, - self.settings['cache_length'], self.settings['debug']).run() - - is_upgrade = os.path.exists(package_metadata_file) - old_version = None - if is_upgrade: - old_version = self.get_metadata(package_name).get('version') - - package_bytes = self.download_url(url, 'Error downloading package.') - if package_bytes == False: - return False - with open(package_path, "wb") as package_file: - package_file.write(package_bytes) - - if not os.path.exists(package_dir): - os.mkdir(package_dir) - - # We create a backup copy incase something was edited - else: - try: - backup_dir = os.path.join(os.path.dirname( - sublime.packages_path()), 'Backup', - datetime.datetime.now().strftime('%Y%m%d%H%M%S')) - if not os.path.exists(backup_dir): - os.makedirs(backup_dir) - package_backup_dir = os.path.join(backup_dir, package_name) - shutil.copytree(package_dir, package_backup_dir) - except (OSError, IOError) as (exception): - sublime.error_message(('%s: An error occurred while trying ' + - 'to backup the package directory for %s. %s') % - (__name__, package_name, unicode_from_os(exception))) - shutil.rmtree(package_backup_dir) - return False - - try: - package_zip = zipfile.ZipFile(package_path, 'r') - except (zipfile.BadZipfile): - sublime.error_message(('%s: An error occurred while ' + - 'trying to unzip the package file for %s. Please try ' + - 'installing the package again.') % (__name__, package_name)) - return False - - root_level_paths = [] - last_path = None - for path in package_zip.namelist(): - last_path = path - if path.find('/') in [len(path) - 1, -1]: - root_level_paths.append(path) - if path[0] == '/' or path.find('../') != -1 or path.find('..\\') != -1: - sublime.error_message(('%s: The package specified, %s, ' + - 'contains files outside of the package dir and cannot ' + - 'be safely installed.') % (__name__, package_name)) - return False - - if last_path and len(root_level_paths) == 0: - root_level_paths.append(last_path[0:last_path.find('/') + 1]) - - os.chdir(package_dir) - - overwrite_failed = False - - # Here we don’t use .extractall() since it was having issues on OS X - skip_root_dir = len(root_level_paths) == 1 and \ - root_level_paths[0].endswith('/') - extracted_paths = [] - for path in package_zip.namelist(): - dest = path - try: - if not isinstance(dest, unicode): - dest = unicode(dest, 'utf-8', 'strict') - except (UnicodeDecodeError): - dest = unicode(dest, 'cp1252', 'replace') - - if os.name == 'nt': - regex = ':|\*|\?|"|<|>|\|' - if re.search(regex, dest) != None: - print ('%s: Skipping file from package named %s due to ' + - 'an invalid filename') % (__name__, path) - continue - - # If there was only a single directory in the package, we remove - # that folder name from the paths as we extract entries - if skip_root_dir: - dest = dest[len(root_level_paths[0]):] - - if os.name == 'nt': - dest = dest.replace('/', '\\') - else: - dest = dest.replace('\\', '/') - - dest = os.path.join(package_dir, dest) - - def add_extracted_dirs(dir): - while dir not in extracted_paths: - extracted_paths.append(dir) - dir = os.path.dirname(dir) - if dir == package_dir: - break - - if path.endswith('/'): - if not os.path.exists(dest): - os.makedirs(dest) - add_extracted_dirs(dest) - else: - dest_dir = os.path.dirname(dest) - if not os.path.exists(dest_dir): - os.makedirs(dest_dir) - add_extracted_dirs(dest_dir) - extracted_paths.append(dest) - try: - open(dest, 'wb').write(package_zip.read(path)) - except (IOError) as (e): - message = unicode_from_os(e) - if re.search('[Ee]rrno 13', message): - overwrite_failed = True - break - print ('%s: Skipping file from package named %s due to ' + - 'an invalid filename') % (__name__, path) - - except (UnicodeDecodeError): - print ('%s: Skipping file from package named %s due to ' + - 'an invalid filename') % (__name__, path) - package_zip.close() - - - # If upgrading failed, queue the package to upgrade upon next start - if overwrite_failed: - reinstall_file = os.path.join(package_dir, 'package-control.reinstall') - open(reinstall_file, 'w').close() - - # Don't delete the metadata file, that way we have it - # when the reinstall happens, and the appropriate - # usage info can be sent back to the server - clear_directory(package_dir, [reinstall_file, package_metadata_file]) - - sublime.error_message(('%s: An error occurred while trying to ' + - 'upgrade %s. Please restart Sublime Text to finish the ' + - 'upgrade.') % (__name__, package_name)) - return False - - - # Here we clean out any files that were not just overwritten. It is ok - # if there is an error removing a file. The next time there is an - # upgrade, it should be cleaned out successfully then. - clear_directory(package_dir, extracted_paths) - - - self.print_messages(package_name, package_dir, is_upgrade, old_version) - - with open(package_metadata_file, 'w') as f: - metadata = { - "version": packages[package_name]['downloads'][0]['version'], - "url": packages[package_name]['url'], - "description": packages[package_name]['description'] - } - json.dump(metadata, f) - - # Submit install and upgrade info - if is_upgrade: - params = { - 'package': package_name, - 'operation': 'upgrade', - 'version': packages[package_name]['downloads'][0]['version'], - 'old_version': old_version - } - else: - params = { - 'package': package_name, - 'operation': 'install', - 'version': packages[package_name]['downloads'][0]['version'] - } - self.record_usage(params) - - # Record the install in the settings file so that you can move - # settings across computers and have the same packages installed - def save_package(): - settings = sublime.load_settings(__name__ + '.sublime-settings') - installed_packages = settings.get('installed_packages', []) - if not installed_packages: - installed_packages = [] - installed_packages.append(package_name) - installed_packages = list(set(installed_packages)) - installed_packages = sorted(installed_packages, - key=lambda s: s.lower()) - settings.set('installed_packages', installed_packages) - sublime.save_settings(__name__ + '.sublime-settings') - sublime.set_timeout(save_package, 1) - - # Here we delete the package file from the installed packages directory - # since we don't want to accidentally overwrite user changes - os.remove(package_path) - # We have to remove the pristine package too or else Sublime Text 2 - # will silently delete the package - if os.path.exists(pristine_package_path): - os.remove(pristine_package_path) - - os.chdir(sublime.packages_path()) - return True - - def print_messages(self, package, package_dir, is_upgrade, old_version): - """ - Prints out package install and upgrade messages - - The functionality provided by this allows package maintainers to - show messages to the user when a package is installed, or when - certain version upgrade occur. - - :param package: - The name of the package the message is for - - :param package_dir: - The full filesystem path to the package directory - - :param is_upgrade: - If the install was actually an upgrade - - :param old_version: - The string version of the package before the upgrade occurred - """ - - messages_file = os.path.join(package_dir, 'messages.json') - if not os.path.exists(messages_file): - return - - messages_fp = open(messages_file, 'r') - try: - message_info = json.load(messages_fp) - except (ValueError): - print '%s: Error parsing messages.json for %s' % (__name__, package) - return - messages_fp.close() - - output = '' - if not is_upgrade and message_info.get('install'): - install_messages = os.path.join(package_dir, - message_info.get('install')) - message = '\n\n%s:\n%s\n\n ' % (package, - ('-' * len(package))) - with open(install_messages, 'r') as f: - message += unicode(f.read(), 'utf-8', errors='replace').replace('\n', '\n ') - output += message + '\n' - - elif is_upgrade and old_version: - upgrade_messages = list(set(message_info.keys()) - - set(['install'])) - upgrade_messages = sorted(upgrade_messages, - cmp=self.compare_versions, reverse=True) - for version in upgrade_messages: - if self.compare_versions(old_version, version) >= 0: - break - if not output: - message = '\n\n%s:\n%s\n' % (package, - ('-' * len(package))) - output += message - upgrade_messages = os.path.join(package_dir, - message_info.get(version)) - message = '\n ' - with open(upgrade_messages, 'r') as f: - message += unicode(f.read(), 'utf-8', errors='replace').replace('\n', '\n ') - output += message + '\n' - - if not output: - return - - def print_to_panel(): - window = sublime.active_window() - - views = window.views() - view = None - for _view in views: - if _view.name() == 'Package Control Messages': - view = _view - break - - if not view: - view = window.new_file() - view.set_name('Package Control Messages') - view.set_scratch(True) - - def write(string): - edit = view.begin_edit() - view.insert(edit, view.size(), string) - view.end_edit(edit) - - if not view.size(): - view.settings().set("word_wrap", True) - write('Package Control Messages\n' + - '========================') - - write(output) - sublime.set_timeout(print_to_panel, 1) - - def remove_package(self, package_name): - """ - Deletes a package - - The deletion process consists of: - - 1. Deleting the directory (or marking it for deletion if deletion fails) - 2. Submitting usage info - 3. Removing the package from the list of installed packages - - :param package_name: - The package to delete - - :return: bool if the package was successfully deleted - """ - - installed_packages = self.list_packages() - - if package_name not in installed_packages: - sublime.error_message(('%s: The package specified, %s, is not ' + - 'installed.') % (__name__, package_name)) - return False - - os.chdir(sublime.packages_path()) - - # Give Sublime Text some time to ignore the package - time.sleep(1) - - package_filename = package_name + '.sublime-package' - package_path = os.path.join(sublime.installed_packages_path(), - package_filename) - installed_package_path = os.path.join(os.path.dirname( - sublime.packages_path()), 'Installed Packages', package_filename) - pristine_package_path = os.path.join(os.path.dirname( - sublime.packages_path()), 'Pristine Packages', package_filename) - package_dir = self.get_package_dir(package_name) - - version = self.get_metadata(package_name).get('version') - - try: - if os.path.exists(package_path): - os.remove(package_path) - except (OSError, IOError) as (exception): - sublime.error_message(('%s: An error occurred while trying to ' + - 'remove the package file for %s. %s') % (__name__, - package_name, unicode_from_os(exception))) - return False - - try: - if os.path.exists(installed_package_path): - os.remove(installed_package_path) - except (OSError, IOError) as (exception): - sublime.error_message(('%s: An error occurred while trying to ' + - 'remove the installed package file for %s. %s') % (__name__, - package_name, unicode_from_os(exception))) - return False - - try: - if os.path.exists(pristine_package_path): - os.remove(pristine_package_path) - except (OSError, IOError) as (exception): - sublime.error_message(('%s: An error occurred while trying to ' + - 'remove the pristine package file for %s. %s') % (__name__, - package_name, unicode_from_os(exception))) - return False - - # We don't delete the actual package dir immediately due to a bug - # in sublime_plugin.py - can_delete_dir = True - if not clear_directory(package_dir): - # If there is an error deleting now, we will mark it for - # cleanup the next time Sublime Text starts - open(os.path.join(package_dir, 'package-control.cleanup'), - 'w').close() - can_delete_dir = False - - params = { - 'package': package_name, - 'operation': 'remove', - 'version': version - } - self.record_usage(params) - - # Remove the package from the installed packages list - def clear_package(): - settings = sublime.load_settings('%s.sublime-settings' % __name__) - installed_packages = settings.get('installed_packages', []) - if not installed_packages: - installed_packages = [] - installed_packages.remove(package_name) - settings.set('installed_packages', installed_packages) - sublime.save_settings('%s.sublime-settings' % __name__) - sublime.set_timeout(clear_package, 1) - - if can_delete_dir: - os.rmdir(package_dir) - - return True - - def record_usage(self, params): - """ - Submits install, upgrade and delete actions to a usage server - - The usage information is currently displayed on the Package Control - community package list at http://wbond.net/sublime_packages/community - - :param params: - A dict of the information to submit - """ - - if not self.settings.get('submit_usage'): - return - params['package_control_version'] = \ - self.get_metadata('Package Control').get('version') - params['sublime_platform'] = self.settings.get('platform') - params['sublime_version'] = self.settings.get('version') - url = self.settings.get('submit_url') + '?' + urllib.urlencode(params) - - result = self.download_url(url, 'Error submitting usage information.') - if result == False: - return - - try: - result = json.loads(result) - if result['result'] != 'success': - raise ValueError() - except (ValueError): - print '%s: Error submitting usage information for %s' % (__name__, - params['package']) - - -class PackageCreator(): - """ - Abstract class for commands that create .sublime-package files - """ - - def show_panel(self): - """ - Shows a list of packages that can be turned into a .sublime-package file - """ - - self.manager = PackageManager() - self.packages = self.manager.list_packages() - if not self.packages: - sublime.error_message(('%s: There are no packages available to ' + - 'be packaged.') % (__name__)) - return - self.window.show_quick_panel(self.packages, self.on_done) - - def get_package_destination(self): - """ - Retrieves the destination for .sublime-package files - - :return: - A string - the path to the folder to save .sublime-package files in - """ - - destination = self.manager.settings.get('package_destination') - - # We check destination via an if statement instead of using - # the dict.get() method since the key may be set, but to a blank value - if not destination: - destination = os.path.join(os.path.expanduser('~'), 'Desktop') - - return destination - - -class CreatePackageCommand(sublime_plugin.WindowCommand, PackageCreator): - """ - Command to create a regular .sublime-package file - """ - - def run(self): - self.show_panel() - - def on_done(self, picked): - """ - Quick panel user selection handler - processes the user package - selection and create the package file - - :param picked: - An integer of the 0-based package name index from the presented - list. -1 means the user cancelled. - """ - - if picked == -1: - return - package_name = self.packages[picked] - package_destination = self.get_package_destination() - - if self.manager.create_package(package_name, package_destination): - self.window.run_command('open_dir', {"dir": - package_destination, "file": package_name + - '.sublime-package'}) - - -class CreateBinaryPackageCommand(sublime_plugin.WindowCommand, PackageCreator): - """ - Command to create a binary .sublime-package file. Binary packages in - general exclude the .py source files and instead include the .pyc files. - Actual included and excluded files are controlled by settings. - """ - - def run(self): - self.show_panel() - - def on_done(self, picked): - """ - Quick panel user selection handler - processes the user package - selection and create the package file - - :param picked: - An integer of the 0-based package name index from the presented - list. -1 means the user cancelled. - """ - - if picked == -1: - return - package_name = self.packages[picked] - package_destination = self.get_package_destination() - - if self.manager.create_package(package_name, package_destination, - binary_package=True): - self.window.run_command('open_dir', {"dir": - package_destination, "file": package_name + - '.sublime-package'}) - - -class PackageRenamer(): - """ - Class to handle renaming packages via the renamed_packages setting - gathered from channels and repositories. - """ - - def load_settings(self): - """ - Loads the list of installed packages from the - Package Control.sublime-settings file. - """ - - self.settings_file = '%s.sublime-settings' % __name__ - self.settings = sublime.load_settings(self.settings_file) - self.installed_packages = self.settings.get('installed_packages', []) - if not isinstance(self.installed_packages, list): - self.installed_packages = [] - - def rename_packages(self, installer): - """ - Renames any installed packages that the user has installed. - - :param installer: - An instance of :class:`PackageInstaller` - """ - - # Fetch the packages since that will pull in the renamed packages list - installer.manager.list_available_packages() - renamed_packages = installer.manager.settings.get('renamed_packages', {}) - if not renamed_packages: - renamed_packages = {} - - # These are packages that have been tracked as installed - installed_pkgs = self.installed_packages - # There are the packages actually present on the filesystem - present_packages = installer.manager.list_packages() - - # Rename directories for packages that have changed names - for package_name in renamed_packages: - package_dir = os.path.join(sublime.packages_path(), package_name) - metadata_path = os.path.join(package_dir, 'package-metadata.json') - if not os.path.exists(metadata_path): - continue - - new_package_name = renamed_packages[package_name] - new_package_dir = os.path.join(sublime.packages_path(), - new_package_name) - - changing_case = package_name.lower() == new_package_name.lower() - case_insensitive_fs = sublime.platform() in ['windows', 'osx'] - - # Since Windows and OSX use case-insensitive filesystems, we have to - # scan through the list of installed packages if the rename of the - # package is just changing the case of it. If we don't find the old - # name for it, we continue the loop since os.path.exists() will return - # true due to the case-insensitive nature of the filesystems. - if case_insensitive_fs and changing_case: - has_old = False - for present_package_name in present_packages: - if present_package_name == package_name: - has_old = True - break - if not has_old: - continue - - if not os.path.exists(new_package_dir) or (case_insensitive_fs and changing_case): - - # Windows will not allow you to rename to the same name with - # a different case, so we work around that with a temporary name - if os.name == 'nt' and changing_case: - temp_package_name = '__' + new_package_name - temp_package_dir = os.path.join(sublime.packages_path(), - temp_package_name) - os.rename(package_dir, temp_package_dir) - package_dir = temp_package_dir - - os.rename(package_dir, new_package_dir) - installed_pkgs.append(new_package_name) - - print '%s: Renamed %s to %s' % (__name__, package_name, - new_package_name) - - else: - installer.manager.remove_package(package_name) - print ('%s: Removed %s since package with new name (%s) ' + - 'already exists') % (__name__, package_name, - new_package_name) - - try: - installed_pkgs.remove(package_name) - except (ValueError): - pass - - sublime.set_timeout(lambda: self.save_packages(installed_pkgs), 10) - - def save_packages(self, installed_packages): - """ - Saves the list of installed packages (after having been appropriately - renamed) - - :param installed_packages: - The new list of installed packages - """ - - installed_packages = list(set(installed_packages)) - installed_packages = sorted(installed_packages, - key=lambda s: s.lower()) - - if installed_packages != self.installed_packages: - self.settings.set('installed_packages', installed_packages) - sublime.save_settings(self.settings_file) - - -class PackageInstaller(): - """ - Provides helper functionality related to installing packages - """ - - def __init__(self): - self.manager = PackageManager() - - def make_package_list(self, ignore_actions=[], override_action=None, - ignore_packages=[]): - """ - Creates a list of packages and what operation would be performed for - each. Allows filtering by the applicable action or package name. - Returns the information in a format suitable for displaying in the - quick panel. - - :param ignore_actions: - A list of actions to ignore packages by. Valid actions include: - `install`, `upgrade`, `downgrade`, `reinstall`, `overwrite`, - `pull` and `none`. `pull` andd `none` are for Git and Hg - repositories. `pull` is present when incoming changes are detected, - where as `none` is selected if no commits are available. `overwrite` - is for packages that do not include version information via the - `package-metadata.json` file. - - :param override_action: - A string action name to override the displayed action for all listed - packages. - - :param ignore_packages: - A list of packages names that should not be returned in the list - - :return: - A list of lists, each containing three strings: - 0 - package name - 1 - package description - 2 - action; [extra info;] package url - """ - - packages = self.manager.list_available_packages() - installed_packages = self.manager.list_packages() - - package_list = [] - for package in sorted(packages.iterkeys(), key=lambda s: s.lower()): - if ignore_packages and package in ignore_packages: - continue - package_entry = [package] - info = packages[package] - download = info['downloads'][0] - - if package in installed_packages: - installed = True - metadata = self.manager.get_metadata(package) - if metadata.get('version'): - installed_version = metadata['version'] - else: - installed_version = None - else: - installed = False - - installed_version_name = 'v' + installed_version if \ - installed and installed_version else 'unknown version' - new_version = 'v' + download['version'] - - vcs = None - package_dir = self.manager.get_package_dir(package) - settings = self.manager.settings - - if override_action: - action = override_action - extra = '' - - else: - if os.path.exists(os.path.join(sublime.packages_path(), - package, '.git')): - if settings.get('ignore_vcs_packages'): - continue - vcs = 'git' - incoming = GitUpgrader(settings.get('git_binary'), - settings.get('git_update_command'), package_dir, - settings.get('cache_length'), settings.get('debug') - ).incoming() - elif os.path.exists(os.path.join(sublime.packages_path(), - package, '.hg')): - if settings.get('ignore_vcs_packages'): - continue - vcs = 'hg' - incoming = HgUpgrader(settings.get('hg_binary'), - settings.get('hg_update_command'), package_dir, - settings.get('cache_length'), settings.get('debug') - ).incoming() - - if installed: - if not installed_version: - if vcs: - if incoming: - action = 'pull' - extra = ' with ' + vcs - else: - action = 'none' - extra = '' - else: - action = 'overwrite' - extra = ' %s with %s' % (installed_version_name, - new_version) - else: - res = self.manager.compare_versions( - installed_version, download['version']) - if res < 0: - action = 'upgrade' - extra = ' to %s from %s' % (new_version, - installed_version_name) - elif res > 0: - action = 'downgrade' - extra = ' to %s from %s' % (new_version, - installed_version_name) - else: - action = 'reinstall' - extra = ' %s' % new_version - else: - action = 'install' - extra = ' %s' % new_version - extra += ';' - - if action in ignore_actions: - continue - - description = info.get('description') - if not description: - description = 'No description provided' - package_entry.append(description) - package_entry.append(action + extra + ' ' + - re.sub('^https?://', '', info['url'])) - package_list.append(package_entry) - return package_list - - def disable_package(self, package): - """ - Disables a package before installing or upgrading to prevent errors - where Sublime Text tries to read files that no longer exist, or read a - half-written file. - - :param package: The string package name - """ - - # Don't disable Package Control so it does not get stuck disabled - if package == 'Package Control': - return False - - settings = sublime.load_settings(preferences_filename()) - ignored = settings.get('ignored_packages') - if not ignored: - ignored = [] - if not package in ignored: - ignored.append(package) - settings.set('ignored_packages', ignored) - sublime.save_settings(preferences_filename()) - return True - return False - - def reenable_package(self, package): - """ - Re-enables a package after it has been installed or upgraded - - :param package: The string package name - """ - - settings = sublime.load_settings(preferences_filename()) - ignored = settings.get('ignored_packages') - if not ignored: - return - if package in ignored: - settings.set('ignored_packages', - list(set(ignored) - set([package]))) - sublime.save_settings(preferences_filename()) - - def on_done(self, picked): - """ - Quick panel user selection handler - disables a package, installs or - upgrades it, then re-enables the package - - :param picked: - An integer of the 0-based package name index from the presented - list. -1 means the user cancelled. - """ - - if picked == -1: - return - name = self.package_list[picked][0] - - if self.disable_package(name): - on_complete = lambda: self.reenable_package(name) - else: - on_complete = None - - thread = PackageInstallerThread(self.manager, name, on_complete) - thread.start() - ThreadProgress(thread, 'Installing package %s' % name, - 'Package %s successfully %s' % (name, self.completion_type)) - - -class PackageInstallerThread(threading.Thread): - """ - A thread to run package install/upgrade operations in so that the main - Sublime Text thread does not get blocked and freeze the UI - """ - - def __init__(self, manager, package, on_complete): - """ - :param manager: - An instance of :class:`PackageManager` - - :param package: - The string package name to install/upgrade - - :param on_complete: - A callback to run after installing/upgrading the package - """ - - self.package = package - self.manager = manager - self.on_complete = on_complete - threading.Thread.__init__(self) - - def run(self): - try: - self.result = self.manager.install_package(self.package) - finally: - if self.on_complete: - sublime.set_timeout(self.on_complete, 1) - - -class InstallPackageCommand(sublime_plugin.WindowCommand): - """ - A command that presents the list of available packages and allows the - user to pick one to install. - """ - - def run(self): - thread = InstallPackageThread(self.window) - thread.start() - ThreadProgress(thread, 'Loading repositories', '') - - -class InstallPackageThread(threading.Thread, PackageInstaller): - """ - A thread to run the action of retrieving available packages in. Uses the - default PackageInstaller.on_done quick panel handler. - """ - - def __init__(self, window): - """ - :param window: - An instance of :class:`sublime.Window` that represents the Sublime - Text window to show the available package list in. - """ - - self.window = window - self.completion_type = 'installed' - threading.Thread.__init__(self) - PackageInstaller.__init__(self) - - def run(self): - self.package_list = self.make_package_list(['upgrade', 'downgrade', - 'reinstall', 'pull', 'none']) - - def show_quick_panel(): - if not self.package_list: - sublime.error_message(('%s: There are no packages ' + - 'available for installation.') % __name__) - return - self.window.show_quick_panel(self.package_list, self.on_done) - sublime.set_timeout(show_quick_panel, 10) - - -class DiscoverPackagesCommand(sublime_plugin.WindowCommand): - """ - A command that opens the community package list webpage - """ - - def run(self): - self.window.run_command('open_url', - {'url': 'http://wbond.net/sublime_packages/community'}) - - -class UpgradePackageCommand(sublime_plugin.WindowCommand): - """ - A command that presents the list of installed packages that can be upgraded. - """ - - def run(self): - package_renamer = PackageRenamer() - package_renamer.load_settings() - - thread = UpgradePackageThread(self.window, package_renamer) - thread.start() - ThreadProgress(thread, 'Loading repositories', '') - - -class UpgradePackageThread(threading.Thread, PackageInstaller): - """ - A thread to run the action of retrieving upgradable packages in. - """ - - def __init__(self, window, package_renamer): - """ - :param window: - An instance of :class:`sublime.Window` that represents the Sublime - Text window to show the list of upgradable packages in. - - :param package_renamer: - An instance of :class:`PackageRenamer` - """ - self.window = window - self.package_renamer = package_renamer - self.completion_type = 'upgraded' - threading.Thread.__init__(self) - PackageInstaller.__init__(self) - - def run(self): - self.package_renamer.rename_packages(self) - - self.package_list = self.make_package_list(['install', 'reinstall', - 'none']) - - def show_quick_panel(): - if not self.package_list: - sublime.error_message(('%s: There are no packages ' + - 'ready for upgrade.') % __name__) - return - self.window.show_quick_panel(self.package_list, self.on_done) - sublime.set_timeout(show_quick_panel, 10) - - def on_done(self, picked): - """ - Quick panel user selection handler - disables a package, upgrades it, - then re-enables the package - - :param picked: - An integer of the 0-based package name index from the presented - list. -1 means the user cancelled. - """ - - if picked == -1: - return - name = self.package_list[picked][0] - - if self.disable_package(name): - on_complete = lambda: self.reenable_package(name) - else: - on_complete = None - - thread = PackageInstallerThread(self.manager, name, on_complete) - thread.start() - ThreadProgress(thread, 'Upgrading package %s' % name, - 'Package %s successfully %s' % (name, self.completion_type)) - - -class UpgradeAllPackagesCommand(sublime_plugin.WindowCommand): - """ - A command to automatically upgrade all installed packages that are - upgradable. - """ - - def run(self): - package_renamer = PackageRenamer() - package_renamer.load_settings() - - thread = UpgradeAllPackagesThread(self.window, package_renamer) - thread.start() - ThreadProgress(thread, 'Loading repositories', '') - - -class UpgradeAllPackagesThread(threading.Thread, PackageInstaller): - """ - A thread to run the action of retrieving upgradable packages in. - """ - - def __init__(self, window, package_renamer): - self.window = window - self.package_renamer = package_renamer - self.completion_type = 'upgraded' - threading.Thread.__init__(self) - PackageInstaller.__init__(self) - - def run(self): - self.package_renamer.rename_packages(self) - package_list = self.make_package_list(['install', 'reinstall', 'none']) - - disabled_packages = {} - - def do_upgrades(): - # Pause so packages can be disabled - time.sleep(0.5) - - # We use a function to generate the on-complete lambda because if - # we don't, the lambda will bind to info at the current scope, and - # thus use the last value of info from the loop - def make_on_complete(name): - return lambda: self.reenable_package(name) - - for info in package_list: - if disabled_packages.get(info[0]): - on_complete = make_on_complete(info[0]) - else: - on_complete = None - thread = PackageInstallerThread(self.manager, info[0], on_complete) - thread.start() - ThreadProgress(thread, 'Upgrading package %s' % info[0], - 'Package %s successfully %s' % (info[0], self.completion_type)) - - # Disabling a package means changing settings, which can only be done - # in the main thread. We then create a new background thread so that - # the upgrade process does not block the UI. - def disable_packages(): - for info in package_list: - disabled_packages[info[0]] = self.disable_package(info[0]) - threading.Thread(target=do_upgrades).start() - - sublime.set_timeout(disable_packages, 1) - - -class ExistingPackagesCommand(): - """ - Allows listing installed packages and their current version - """ - - def __init__(self): - self.manager = PackageManager() - - def make_package_list(self, action=''): - """ - Returns a list of installed packages suitable for displaying in the - quick panel. - - :param action: - An action to display at the beginning of the third element of the - list returned for each package - - :return: - A list of lists, each containing three strings: - 0 - package name - 1 - package description - 2 - [action] installed version; package url - """ - - packages = self.manager.list_packages() - - if action: - action += ' ' - - package_list = [] - for package in sorted(packages, key=lambda s: s.lower()): - package_entry = [package] - metadata = self.manager.get_metadata(package) - package_dir = os.path.join(sublime.packages_path(), package) - - description = metadata.get('description') - if not description: - description = 'No description provided' - package_entry.append(description) - - version = metadata.get('version') - if not version and os.path.exists(os.path.join(package_dir, - '.git')): - installed_version = 'git repository' - elif not version and os.path.exists(os.path.join(package_dir, - '.hg')): - installed_version = 'hg repository' - else: - installed_version = 'v' + version if version else \ - 'unknown version' - - url = metadata.get('url') - if url: - url = '; ' + re.sub('^https?://', '', url) - else: - url = '' - - package_entry.append(action + installed_version + url) - package_list.append(package_entry) - - return package_list - - -class ListPackagesCommand(sublime_plugin.WindowCommand): - """ - A command that shows a list of all installed packages in the quick panel - """ - - def run(self): - ListPackagesThread(self.window).start() - - -class ListPackagesThread(threading.Thread, ExistingPackagesCommand): - """ - A thread to prevent the listing of existing packages from freezing the UI - """ - - def __init__(self, window): - """ - :param window: - An instance of :class:`sublime.Window` that represents the Sublime - Text window to show the list of installed packages in. - """ - - self.window = window - threading.Thread.__init__(self) - ExistingPackagesCommand.__init__(self) - - def run(self): - self.package_list = self.make_package_list() - - def show_quick_panel(): - if not self.package_list: - sublime.error_message(('%s: There are no packages ' + - 'to list.') % __name__) - return - self.window.show_quick_panel(self.package_list, self.on_done) - sublime.set_timeout(show_quick_panel, 10) - - def on_done(self, picked): - """ - Quick panel user selection handler - opens the homepage for any - selected package in the user's browser - - :param picked: - An integer of the 0-based package name index from the presented - list. -1 means the user cancelled. - """ - - if picked == -1: - return - package_name = self.package_list[picked][0] - - def open_dir(): - self.window.run_command('open_dir', - {"dir": os.path.join(sublime.packages_path(), package_name)}) - sublime.set_timeout(open_dir, 10) - - -class RemovePackageCommand(sublime_plugin.WindowCommand, - ExistingPackagesCommand): - """ - A command that presents a list of installed packages, allowing the user to - select one to remove - """ - - def __init__(self, window): - """ - :param window: - An instance of :class:`sublime.Window` that represents the Sublime - Text window to show the list of installed packages in. - """ - - self.window = window - ExistingPackagesCommand.__init__(self) - - def run(self): - self.package_list = self.make_package_list('remove') - if not self.package_list: - sublime.error_message(('%s: There are no packages ' + - 'that can be removed.') % __name__) - return - self.window.show_quick_panel(self.package_list, self.on_done) - - def on_done(self, picked): - """ - Quick panel user selection handler - deletes the selected package - - :param picked: - An integer of the 0-based package name index from the presented - list. -1 means the user cancelled. - """ - - if picked == -1: - return - package = self.package_list[picked][0] - - # Don't disable Package Control so it does not get stuck disabled - if package != 'Package Control': - settings = sublime.load_settings(preferences_filename()) - ignored = settings.get('ignored_packages') - if not ignored: - ignored = [] - if not package in ignored: - ignored.append(package) - settings.set('ignored_packages', ignored) - sublime.save_settings(preferences_filename()) - - ignored.remove(package) - thread = RemovePackageThread(self.manager, package, - ignored) - thread.start() - ThreadProgress(thread, 'Removing package %s' % package, - 'Package %s successfully removed' % package) - - -class RemovePackageThread(threading.Thread): - """ - A thread to run the remove package operation in so that the Sublime Text - UI does not become frozen - """ - - def __init__(self, manager, package, ignored): - self.manager = manager - self.package = package - self.ignored = ignored - threading.Thread.__init__(self) - - def run(self): - self.result = self.manager.remove_package(self.package) - - def unignore_package(): - settings = sublime.load_settings(preferences_filename()) - settings.set('ignored_packages', self.ignored) - sublime.save_settings(preferences_filename()) - sublime.set_timeout(unignore_package, 10) - - -class AddRepositoryChannelCommand(sublime_plugin.WindowCommand): - """ - A command to add a new channel (list of repositories) to the user's machine - """ - - def run(self): - self.window.show_input_panel('Channel JSON URL', '', - self.on_done, self.on_change, self.on_cancel) - - def on_done(self, input): - """ - Input panel handler - adds the provided URL as a channel - - :param input: - A string of the URL to the new channel - """ - - settings = sublime.load_settings('%s.sublime-settings' % __name__) - repository_channels = settings.get('repository_channels', []) - if not repository_channels: - repository_channels = [] - repository_channels.append(input) - settings.set('repository_channels', repository_channels) - sublime.save_settings('%s.sublime-settings' % __name__) - sublime.status_message(('Channel %s successfully ' + - 'added') % input) - - def on_change(self, input): - pass - - def on_cancel(self): - pass - - -class AddRepositoryCommand(sublime_plugin.WindowCommand): - """ - A command to add a new repository to the user's machine - """ - - def run(self): - self.window.show_input_panel('GitHub or BitBucket Web URL, or Custom' + - ' JSON Repository URL', '', self.on_done, - self.on_change, self.on_cancel) - - def on_done(self, input): - """ - Input panel handler - adds the provided URL as a repository - - :param input: - A string of the URL to the new repository - """ - - settings = sublime.load_settings('%s.sublime-settings' % __name__) - repositories = settings.get('repositories', []) - if not repositories: - repositories = [] - repositories.append(input) - settings.set('repositories', repositories) - sublime.save_settings('%s.sublime-settings' % __name__) - sublime.status_message('Repository %s successfully added' % input) - - def on_change(self, input): - pass - - def on_cancel(self): - pass - - -class DisablePackageCommand(sublime_plugin.WindowCommand): - """ - A command that adds a package to Sublime Text's ignored packages list - """ - - def run(self): - manager = PackageManager() - packages = manager.list_all_packages() - self.settings = sublime.load_settings(preferences_filename()) - ignored = self.settings.get('ignored_packages') - if not ignored: - ignored = [] - self.package_list = list(set(packages) - set(ignored)) - self.package_list.sort() - if not self.package_list: - sublime.error_message(('%s: There are no enabled packages' + - 'to disable.') % __name__) - return - self.window.show_quick_panel(self.package_list, self.on_done) - - def on_done(self, picked): - """ - Quick panel user selection handler - disables the selected package - - :param picked: - An integer of the 0-based package name index from the presented - list. -1 means the user cancelled. - """ - - if picked == -1: - return - package = self.package_list[picked] - ignored = self.settings.get('ignored_packages') - if not ignored: - ignored = [] - ignored.append(package) - self.settings.set('ignored_packages', ignored) - sublime.save_settings(preferences_filename()) - sublime.status_message(('Package %s successfully added to list of ' + - 'disabled packages - restarting Sublime Text may be required') % - package) - - -class EnablePackageCommand(sublime_plugin.WindowCommand): - """ - A command that removes a package from Sublime Text's ignored packages list - """ - - def run(self): - self.settings = sublime.load_settings(preferences_filename()) - self.disabled_packages = self.settings.get('ignored_packages') - self.disabled_packages.sort() - if not self.disabled_packages: - sublime.error_message(('%s: There are no disabled packages ' + - 'to enable.') % __name__) - return - self.window.show_quick_panel(self.disabled_packages, self.on_done) - - def on_done(self, picked): - """ - Quick panel user selection handler - enables the selected package - - :param picked: - An integer of the 0-based package name index from the presented - list. -1 means the user cancelled. - """ - - if picked == -1: - return - package = self.disabled_packages[picked] - ignored = self.settings.get('ignored_packages') - self.settings.set('ignored_packages', - list(set(ignored) - set([package]))) - sublime.save_settings(preferences_filename()) - sublime.status_message(('Package %s successfully removed from list ' + - 'of disabled packages - restarting Sublime Text may be required') % - package) - - -class AutomaticUpgrader(threading.Thread): - """ - Automatically checks for updated packages and installs them. controlled - by the `auto_upgrade`, `auto_upgrade_ignore`, `auto_upgrade_frequency` and - `auto_upgrade_last_run` settings. - """ - - def __init__(self, found_packages): - """ - :param found_packages: - A list of package names for the packages that were found to be - installed on the machine. - """ - - self.installer = PackageInstaller() - self.manager = self.installer.manager - - self.load_settings() - - self.package_renamer = PackageRenamer() - self.package_renamer.load_settings() - - self.auto_upgrade = self.settings.get('auto_upgrade') - self.auto_upgrade_ignore = self.settings.get('auto_upgrade_ignore') - - self.next_run = int(time.time()) - self.last_run = None - last_run_file = os.path.join(sublime.packages_path(), 'User', - 'Package Control.last-run') - - if os.path.isfile(last_run_file): - with open(last_run_file) as fobj: - try: - self.last_run = int(fobj.read()) - except ValueError: - pass - - frequency = self.settings.get('auto_upgrade_frequency') - if frequency: - if self.last_run: - self.next_run = int(self.last_run) + (frequency * 60 * 60) - else: - self.next_run = time.time() - - # Detect if a package is missing that should be installed - self.missing_packages = list(set(self.installed_packages) - - set(found_packages)) - - if self.auto_upgrade and self.next_run <= time.time(): - with open(last_run_file, 'w') as fobj: - fobj.write(str(int(time.time()))) - - threading.Thread.__init__(self) - - def load_settings(self): - """ - Loads the list of installed packages from the - Package Control.sublime-settings file - """ - - self.settings_file = '%s.sublime-settings' % __name__ - self.settings = sublime.load_settings(self.settings_file) - self.installed_packages = self.settings.get('installed_packages', []) - self.should_install_missing = self.settings.get('install_missing') - if not isinstance(self.installed_packages, list): - self.installed_packages = [] - - def run(self): - self.install_missing() - - if self.next_run > time.time(): - self.print_skip() - return - - self.upgrade_packages() - - def install_missing(self): - """ - Installs all packages that were listed in the list of - `installed_packages` from Package Control.sublime-settings but were not - found on the filesystem and passed as `found_packages`. - """ - - if not self.missing_packages or not self.should_install_missing: - return - - print '%s: Installing %s missing packages' % \ - (__name__, len(self.missing_packages)) - for package in self.missing_packages: - if self.installer.manager.install_package(package): - print '%s: Installed missing package %s' % \ - (__name__, package) - - def print_skip(self): - """ - Prints a notice in the console if the automatic upgrade is skipped - due to already having been run in the last `auto_upgrade_frequency` - hours. - """ - - last_run = datetime.datetime.fromtimestamp(self.last_run) - next_run = datetime.datetime.fromtimestamp(self.next_run) - date_format = '%Y-%m-%d %H:%M:%S' - print ('%s: Skipping automatic upgrade, last run at ' + - '%s, next run at %s or after') % (__name__, - last_run.strftime(date_format), next_run.strftime(date_format)) - - def upgrade_packages(self): - """ - Upgrades all packages that are not currently upgraded to the lastest - version. Also renames any installed packages to their new names. - """ - - if not self.auto_upgrade: - return - - self.package_renamer.rename_packages(self.installer) - - packages = self.installer.make_package_list(['install', - 'reinstall', 'downgrade', 'overwrite', 'none'], - ignore_packages=self.auto_upgrade_ignore) - - # If Package Control is being upgraded, just do that and restart - for package in packages: - if package[0] != __name__: - continue - - def reset_last_run(): - settings = sublime.load_settings(self.settings_file) - settings.set('auto_upgrade_last_run', None) - sublime.save_settings(self.settings_file) - sublime.set_timeout(reset_last_run, 1) - packages = [package] - break - - if not packages: - print '%s: No updated packages' % __name__ - return - - print '%s: Installing %s upgrades' % (__name__, len(packages)) - for package in packages: - self.installer.manager.install_package(package[0]) - version = re.sub('^.*?(v[\d\.]+).*?$', '\\1', package[2]) - if version == package[2] and version.find('pull with') != -1: - vcs = re.sub('^pull with (\w+).*?$', '\\1', version) - version = 'latest %s commit' % vcs - print '%s: Upgraded %s to %s' % (__name__, package[0], version) - - -class PackageCleanup(threading.Thread, PackageRenamer): - """ - Cleans up folders for packages that were removed, but that still have files - in use. - """ - - def __init__(self): - self.manager = PackageManager() - self.load_settings() - threading.Thread.__init__(self) - - def run(self): - found_pkgs = [] - installed_pkgs = self.installed_packages - for package_name in os.listdir(sublime.packages_path()): - package_dir = os.path.join(sublime.packages_path(), package_name) - metadata_path = os.path.join(package_dir, 'package-metadata.json') - - # Cleanup packages that could not be removed due to in-use files - cleanup_file = os.path.join(package_dir, 'package-control.cleanup') - if os.path.exists(cleanup_file): - try: - shutil.rmtree(package_dir) - print '%s: Removed old directory for package %s' % \ - (__name__, package_name) - except (OSError) as (e): - if not os.path.exists(cleanup_file): - open(cleanup_file, 'w').close() - print ('%s: Unable to remove old directory for package ' + - '%s - deferring until next start: %s') % (__name__, - package_name, unicode_from_os(e)) - - # Finish reinstalling packages that could not be upgraded due to - # in-use files - reinstall = os.path.join(package_dir, 'package-control.reinstall') - if os.path.exists(reinstall): - if not clear_directory(package_dir, [metadata_path]): - if not os.path.exists(reinstall): - open(reinstall, 'w').close() - # Assigning this here prevents the callback from referencing the value - # of the "package_name" variable when it is executed - restart_message = ('%s: An error occurred while trying to ' + - 'finish the upgrade of %s. You will most likely need to ' + - 'restart your computer to complete the upgrade.') % ( - __name__, package_name) - def show_still_locked(): - sublime.error_message(restart_message) - sublime.set_timeout(show_still_locked, 10) - else: - self.manager.install_package(package_name) - - # This adds previously installed packages from old versions of PC - if os.path.exists(metadata_path) and \ - package_name not in self.installed_packages: - installed_pkgs.append(package_name) - params = { - 'package': package_name, - 'operation': 'install', - 'version': \ - self.manager.get_metadata(package_name).get('version') - } - self.manager.record_usage(params) - - found_pkgs.append(package_name) - - sublime.set_timeout(lambda: self.finish(installed_pkgs, found_pkgs), 10) - - def finish(self, installed_pkgs, found_pkgs): - """ - A callback that can be run the main UI thread to perform saving of the - Package Control.sublime-settings file. Also fires off the - :class:`AutomaticUpgrader`. - - :param installed_pkgs: - A list of the string package names of all "installed" packages, - even ones that do not appear to be in the filesystem. - - :param found_pkgs: - A list of the string package names of all packages that are - currently installed on the filesystem. - """ - - self.save_packages(installed_pkgs) - AutomaticUpgrader(found_pkgs).start() - - -# Start shortly after Sublime starts so package renames don't cause errors -# with keybindings, settings, etc disappearing in the middle of parsing -sublime.set_timeout(lambda: PackageCleanup().start(), 2000) + if st_version == 2: + plugin_loaded() diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.sublime-settings b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.sublime-settings index 2726c3f..03e1594 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.sublime-settings +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.sublime-settings @@ -2,18 +2,19 @@ // A list of URLs that each contain a JSON file with a list of repositories. // The repositories from these channels are placed in order after the // repositories from the "repositories" setting - "repository_channels": [ - "https://sublime.wbond.net/repositories.json" + "channels": [ + "https://sublime.wbond.net/channel.json" ], - + // A list of URLs that contain a packages JSON file. These repositories - // are placed in order before repositories from the "repository_channels" + // are placed in order before repositories from the "channels" // setting "repositories": [], // A list of CA certs needed for domains. The default channel provides a // list of domains and an identifier (the md5 hash) for the CA cert(s) - // necessary for each. + // necessary for each. Not used on Windows since the system CA cert list + // is automatically used via WinINet. // // If a custom cert is required for a proxy or for an alternate channel // or repository domain name, it should be added in one of the two forms: @@ -24,21 +25,29 @@ // In both cases the literal "*" means the cert will be checked to ensure // it is present for accessing any URL. This is necessary for proxy // connections, but also useful if you want to provide you own - // ca-bundle.crt file. + // Pckage Control.ca-bundle file. // // The "my_identifier" and "my_identifier_2" can be any unique string // that Package Control can use as a filename, and ensures that it has // merged the cert file with the ca-bundle.crt file in the certs/ directory // since that is what is passed to the downloaders. "certs": { - "api.bitbucket.org": ["d867a7b2aecc46f9c31afc4f2f50de05", ""], - "api.github.com": ["1c5282418e2cb4989cd6beddcdbab0b5", ""], - "bitbucket.org": ["897abe0b41fd2f64e9e2e351cbc36d76", ""], - "nodeload.github.com": ["1c5282418e2cb4989cd6beddcdbab0b5", ""], - "raw.github.com": ["1c5282418e2cb4989cd6beddcdbab0b5", ""], - "sublime.wbond.net": ["7f4f8622b4fd001c7f648e09aae7edaa", ""] + "api.bitbucket.org": ["7d0986b90061d60c8c02aa3b1cf23850", "https://sublime.wbond.net/certs/7d0986b90061d60c8c02aa3b1cf23850"], + "api.github.com": ["7d0986b90061d60c8c02aa3b1cf23850", "https://sublime.wbond.net/certs/7d0986b90061d60c8c02aa3b1cf23850"], + "bitbucket.org": ["7d0986b90061d60c8c02aa3b1cf23850", "https://sublime.wbond.net/certs/7d0986b90061d60c8c02aa3b1cf23850"], + "codeload.github.com": ["7d0986b90061d60c8c02aa3b1cf23850", "https://sublime.wbond.net/certs/7d0986b90061d60c8c02aa3b1cf23850"], + "downloads.sourceforge.net": ["221e907bdfff70d71cea42361ae209d5", "https://sublime.wbond.net/certs/221e907bdfff70d71cea42361ae209d5"], + "github.com": ["7d0986b90061d60c8c02aa3b1cf23850", "https://sublime.wbond.net/certs/7d0986b90061d60c8c02aa3b1cf23850"], + "nodeload.github.com": ["7d0986b90061d60c8c02aa3b1cf23850", "https://sublime.wbond.net/certs/7d0986b90061d60c8c02aa3b1cf23850"], + "raw.github.com": ["7d0986b90061d60c8c02aa3b1cf23850", "https://sublime.wbond.net/certs/7d0986b90061d60c8c02aa3b1cf23850"], + "sublime.wbond.net": ["221e907bdfff70d71cea42361ae209d5", "https://sublime.wbond.net/certs/221e907bdfff70d71cea42361ae209d5"] }, + // Install pre-release versions of packages. If this is false, versions + // under 1.0.0 will still be installed. Only packages using the SemVer + // -prerelease suffixes will be ignored. + "install_prereleases": false, + // If debugging information for HTTP/HTTPS connections should be printed // to the Sublime Text console "debug": false, @@ -53,7 +62,7 @@ // the channel for aggregated statistics "submit_usage": true, - // The URL to post install, upgrade and removal notices to + // The URL to post install, upgrade and removal notices to "submit_url": "https://sublime.wbond.net/submit", // If packages should be automatically upgraded when ST2 starts @@ -69,25 +78,36 @@ // Packages to not auto upgrade "auto_upgrade_ignore": [], - // Timeout for downloading channels, repositories and packages + // Timeout for downloading channels, repositories and packages. Doesn't + // have an effect on Windows due to a bug in WinINet. "timeout": 30, // The number of seconds to cache repository and package info for "cache_length": 300, - // An HTTP proxy server to use for requests + // An HTTP proxy server to use for requests. Not used on Windows since the + // system proxy configuration is utilized via WinINet. "http_proxy": "", // An HTTPS proxy server to use for requests - this will inherit from // http_proxy if it is set to "" or null and http_proxy has a value. You - // can set this to false to prevent inheriting from http_proxy. + // can set this to false to prevent inheriting from http_proxy. Not used on + // Windows since the system proxy configuration is utilized via WinINet. "https_proxy": "", - // Username and password for both http_proxy and https_proxy + // Username and password for both http_proxy and https_proxy. May be used + // with WinINet to set credentials for system-level proxy config. "proxy_username": "", "proxy_password": "", - // User agent for HTTP requests - "user_agent": "Sublime Package Control", + // If HTTP responses should be cached to disk + "http_cache": true, + + // Number of seconds to cache HTTP responses for, defaults to one week + "http_cache_length": 604800, + + // User agent for HTTP requests. If "%s" is present, will be replaced + // with the current version. + "user_agent": "Sublime Package Control v%s", // Setting this to true will cause Package Control to ignore all git // and hg repositories - this may help if trying to list packages to install @@ -97,12 +117,20 @@ // Custom paths to VCS binaries for when they can't be automatically // found on the system and a package includes a VCS metadata directory "git_binary": "", - "git_update_command": ["pull", "origin", "master", "--ff", "--commit"], - + + // This should NOT contain the name of the remote or branch - that will + // be automatically determined. + "git_update_command": ["pull", "--ff", "--commit"], + "hg_binary": "", - - // Be sure to keep the remote name as the last argument - "hg_update_command": ["pull", "--update", "default"], + + // For HG repositories, be sure to use "default" as the remote URL. + // This is the default behavior when cloning an HG repo. + "hg_update_command": ["pull", "--update"], + + // Full path to the openssl binary, if not found on your machine. This is + // only used when running the Grab CA Certs command. + "openssl_binary": "", // Directories to ignore when creating a package "dirs_to_ignore": [ @@ -133,6 +161,6 @@ "__init__.py" ], - // When a package is created, copy it to this folder - defaults to Desktop + // When a package is created, copy it to this folder - defaults to Desktop "package_destination": "" -} \ No newline at end of file +} diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/1c5282418e2cb4989cd6beddcdbab0b5 b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/1c5282418e2cb4989cd6beddcdbab0b5 deleted file mode 100644 index 432b087..0000000 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/1c5282418e2cb4989cd6beddcdbab0b5 +++ /dev/null @@ -1,113 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - 0a:5f:11:4d:03:5b:17:91:17:d2:ef:d4:03:8c:3f:3b - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA - Validity - Not Before: Apr 2 12:00:00 2008 GMT - Not After : Apr 3 00:00:00 2022 GMT - Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance CA-3 - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - Public-Key: (2048 bit) - Modulus: - 00:bf:61:0a:29:10:1f:5e:fe:34:37:51:08:f8:1e: - fb:22:ed:61:be:0b:0d:70:4c:50:63:26:75:15:b9: - 41:88:97:b6:f0:a0:15:bb:08:60:e0:42:e8:05:29: - 10:87:36:8a:28:65:a8:ef:31:07:74:6d:36:97:2f: - 28:46:66:04:c7:2a:79:26:7a:99:d5:8e:c3:6d:4f: - a0:5e:ad:bc:3d:91:c2:59:7b:5e:36:6c:c0:53:cf: - 00:08:32:3e:10:64:58:10:13:69:c7:0c:ee:9c:42: - 51:00:f9:05:44:ee:24:ce:7a:1f:ed:8c:11:bd:12: - a8:f3:15:f4:1c:7a:31:69:01:1b:a7:e6:5d:c0:9a: - 6c:7e:09:9e:e7:52:44:4a:10:3a:23:e4:9b:b6:03: - af:a8:9c:b4:5b:9f:d4:4b:ad:92:8c:ce:b5:11:2a: - aa:37:18:8d:b4:c2:b8:d8:5c:06:8c:f8:ff:23:bd: - 35:5e:d4:7c:3e:7e:83:0e:91:96:05:98:c3:b2:1f: - e3:c8:65:eb:a9:7b:5d:a0:2c:cc:fc:3c:d9:6d:ed: - cc:fa:4b:43:8c:c9:d4:b8:a5:61:1c:b2:40:b6:28: - 12:df:b9:f8:5f:fe:d3:b2:c9:ef:3d:b4:1e:4b:7c: - 1c:4c:99:36:9e:3d:eb:ec:a7:68:5e:1d:df:67:6e: - 5e:fb - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Key Usage: critical - Digital Signature, Certificate Sign, CRL Sign - X509v3 Certificate Policies: - Policy: 2.16.840.1.114412.1.3.0.2 - CPS: http://www.digicert.com/ssl-cps-repository.htm - User Notice: - Explicit Text: - - X509v3 Basic Constraints: critical - CA:TRUE, pathlen:0 - Authority Information Access: - OCSP - URI:http://ocsp.digicert.com - - X509v3 CRL Distribution Points: - - Full Name: - URI:http://crl3.digicert.com/DigiCertHighAssuranceEVRootCA.crl - - Full Name: - URI:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl - - X509v3 Authority Key Identifier: - keyid:B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3 - - X509v3 Subject Key Identifier: - 50:EA:73:89:DB:29:FB:10:8F:9E:E5:01:20:D4:DE:79:99:48:83:F7 - Signature Algorithm: sha1WithRSAEncryption - 1e:e2:a5:48:9e:6c:db:53:38:0f:ef:a6:1a:2a:ac:e2:03:43: - ed:9a:bc:3e:8e:75:1b:f0:fd:2e:22:59:ac:13:c0:61:e2:e7: - fa:e9:99:cd:87:09:75:54:28:bf:46:60:dc:be:51:2c:92:f3: - 1b:91:7c:31:08:70:e2:37:b9:c1:5b:a8:bd:a3:0b:00:fb:1a: - 15:fd:03:ad:58:6a:c5:c7:24:99:48:47:46:31:1e:92:ef:b4: - 5f:4e:34:c7:90:bf:31:c1:f8:b1:84:86:d0:9c:01:aa:df:8a: - 56:06:ce:3a:e9:0e:ae:97:74:5d:d7:71:9a:42:74:5f:de:8d: - 43:7c:de:e9:55:ed:69:00:cb:05:e0:7a:61:61:33:d1:19:4d: - f9:08:ee:a0:39:c5:25:35:b7:2b:c4:0f:b2:dd:f1:a5:b7:0e: - 24:c4:26:28:8d:79:77:f5:2f:f0:57:ba:7c:07:d4:e1:fc:cd: - 5a:30:57:7e:86:10:47:dd:31:1f:d7:fc:a2:c2:bf:30:7c:5d: - 24:aa:e8:f9:ae:5f:6a:74:c2:ce:6b:b3:46:d8:21:be:29:d4: - 8e:5e:15:d6:42:4a:e7:32:6f:a4:b1:6b:51:83:58:be:3f:6d: - c7:fb:da:03:21:cb:6a:16:19:4e:0a:f0:ad:84:ca:5d:94:b3: - 5a:76:f7:61 ------BEGIN CERTIFICATE----- -MIIGWDCCBUCgAwIBAgIQCl8RTQNbF5EX0u/UA4w/OzANBgkqhkiG9w0BAQUFADBs -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBFViBSb290IENBMB4XDTA4MDQwMjEyMDAwMFoXDTIyMDQwMzAwMDAwMFowZjEL -MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 -LmRpZ2ljZXJ0LmNvbTElMCMGA1UEAxMcRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug -Q0EtMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9hCikQH17+NDdR -CPge+yLtYb4LDXBMUGMmdRW5QYiXtvCgFbsIYOBC6AUpEIc2iihlqO8xB3RtNpcv -KEZmBMcqeSZ6mdWOw21PoF6tvD2Rwll7XjZswFPPAAgyPhBkWBATaccM7pxCUQD5 -BUTuJM56H+2MEb0SqPMV9Bx6MWkBG6fmXcCabH4JnudSREoQOiPkm7YDr6ictFuf -1EutkozOtREqqjcYjbTCuNhcBoz4/yO9NV7UfD5+gw6RlgWYw7If48hl66l7XaAs -zPw82W3tzPpLQ4zJ1LilYRyyQLYoEt+5+F/+07LJ7z20Hkt8HEyZNp496+ynaF4d -32duXvsCAwEAAaOCAvowggL2MA4GA1UdDwEB/wQEAwIBhjCCAcYGA1UdIASCAb0w -ggG5MIIBtQYLYIZIAYb9bAEDAAIwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3 -LmRpZ2ljZXJ0LmNvbS9zc2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUH -AgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQBy -AHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBj -AGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAg -AEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQ -AGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBt -AGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBj -AG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBl -AHIAZQBuAGMAZQAuMBIGA1UdEwEB/wQIMAYBAf8CAQAwNAYIKwYBBQUHAQEEKDAm -MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wgY8GA1UdHwSB -hzCBhDBAoD6gPIY6aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0SGln -aEFzc3VyYW5jZUVWUm9vdENBLmNybDBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNl -cnQuY29tL0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDAfBgNVHSME -GDAWgBSxPsNpA/i/RwHUmCYaCALvY2QrwzAdBgNVHQ4EFgQUUOpzidsp+xCPnuUB -INTeeZlIg/cwDQYJKoZIhvcNAQEFBQADggEBAB7ipUiebNtTOA/vphoqrOIDQ+2a -vD6OdRvw/S4iWawTwGHi5/rpmc2HCXVUKL9GYNy+USyS8xuRfDEIcOI3ucFbqL2j -CwD7GhX9A61YasXHJJlIR0YxHpLvtF9ONMeQvzHB+LGEhtCcAarfilYGzjrpDq6X -dF3XcZpCdF/ejUN83ulV7WkAywXgemFhM9EZTfkI7qA5xSU1tyvED7Ld8aW3DiTE -JiiNeXf1L/BXunwH1OH8zVowV36GEEfdMR/X/KLCvzB8XSSq6PmuX2p0ws5rs0bY -Ib4p1I5eFdZCSucyb6Sxa1GDWL4/bcf72gMhy2oWGU4K8K2Eyl2Us1p292E= ------END CERTIFICATE----- \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/7f4f8622b4fd001c7f648e09aae7edaa b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/7f4f8622b4fd001c7f648e09aae7edaa deleted file mode 100644 index cb680c1..0000000 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/7f4f8622b4fd001c7f648e09aae7edaa +++ /dev/null @@ -1,165 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 145105 (0x236d1) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA - Validity - Not Before: Feb 19 22:45:05 2010 GMT - Not After : Feb 18 22:45:05 2020 GMT - Subject: C=US, O=GeoTrust, Inc., CN=RapidSSL CA - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (2048 bit) - Modulus (2048 bit): - 00:c7:71:f8:56:c7:1e:d9:cc:b5:ad:f6:b4:97:a3: - fb:a1:e6:0b:50:5f:50:aa:3a:da:0f:fc:3d:29:24: - 43:c6:10:29:c1:fc:55:40:72:ee:bd:ea:df:9f:b6: - 41:f4:48:4b:c8:6e:fe:4f:57:12:8b:5b:fa:92:dd: - 5e:e8:ad:f3:f0:1b:b1:7b:4d:fb:cf:fd:d1:e5:f8: - e3:dc:e7:f5:73:7f:df:01:49:cf:8c:56:c1:bd:37: - e3:5b:be:b5:4f:8b:8b:f0:da:4f:c7:e3:dd:55:47: - 69:df:f2:5b:7b:07:4f:3d:e5:ac:21:c1:c8:1d:7a: - e8:e7:f6:0f:a1:aa:f5:6f:de:a8:65:4f:10:89:9c: - 03:f3:89:7a:a5:5e:01:72:33:ed:a9:e9:5a:1e:79: - f3:87:c8:df:c8:c5:fc:37:c8:9a:9a:d7:b8:76:cc: - b0:3e:e7:fd:e6:54:ea:df:5f:52:41:78:59:57:ad: - f1:12:d6:7f:bc:d5:9f:70:d3:05:6c:fa:a3:7d:67: - 58:dd:26:62:1d:31:92:0c:79:79:1c:8e:cf:ca:7b: - c1:66:af:a8:74:48:fb:8e:82:c2:9e:2c:99:5c:7b: - 2d:5d:9b:bc:5b:57:9e:7c:3a:7a:13:ad:f2:a3:18: - 5b:2b:59:0f:cd:5c:3a:eb:68:33:c6:28:1d:82:d1: - 50:8b - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Key Usage: critical - Certificate Sign, CRL Sign - X509v3 Subject Key Identifier: - 6B:69:3D:6A:18:42:4A:DD:8F:02:65:39:FD:35:24:86:78:91:16:30 - X509v3 Authority Key Identifier: - keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E - - X509v3 Basic Constraints: critical - CA:TRUE, pathlen:0 - X509v3 CRL Distribution Points: - URI:http://crl.geotrust.com/crls/gtglobal.crl - - Authority Information Access: - OCSP - URI:http://ocsp.geotrust.com - - Signature Algorithm: sha1WithRSAEncryption - ab:bc:bc:0a:5d:18:94:e3:c1:b1:c3:a8:4c:55:d6:be:b4:98: - f1:ee:3c:1c:cd:cf:f3:24:24:5c:96:03:27:58:fc:36:ae:a2: - 2f:8f:f1:fe:da:2b:02:c3:33:bd:c8:dd:48:22:2b:60:0f:a5: - 03:10:fd:77:f8:d0:ed:96:67:4f:fd:ea:47:20:70:54:dc:a9: - 0c:55:7e:e1:96:25:8a:d9:b5:da:57:4a:be:8d:8e:49:43:63: - a5:6c:4e:27:87:25:eb:5b:6d:fe:a2:7f:38:28:e0:36:ab:ad: - 39:a5:a5:62:c4:b7:5c:58:2c:aa:5d:01:60:a6:62:67:a3:c0: - c7:62:23:f4:e7:6c:46:ee:b5:d3:80:6a:22:13:d2:2d:3f:74: - 4f:ea:af:8c:5f:b4:38:9c:db:ae:ce:af:84:1e:a6:f6:34:51: - 59:79:d3:e3:75:dc:bc:d7:f3:73:df:92:ec:d2:20:59:6f:9c: - fb:95:f8:92:76:18:0a:7c:0f:2c:a6:ca:de:8a:62:7b:d8:f3: - ce:5f:68:bd:8f:3e:c1:74:bb:15:72:3a:16:83:a9:0b:e6:4d: - 99:9c:d8:57:ec:a8:01:51:c7:6f:57:34:5e:ab:4a:2c:42:f6: - 4f:1c:89:78:de:26:4e:f5:6f:93:4c:15:6b:27:56:4d:00:54: - 6c:7a:b7:b7 ------BEGIN CERTIFICATE----- -MIID1TCCAr2gAwIBAgIDAjbRMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT -MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i -YWwgQ0EwHhcNMTAwMjE5MjI0NTA1WhcNMjAwMjE4MjI0NTA1WjA8MQswCQYDVQQG -EwJVUzEXMBUGA1UEChMOR2VvVHJ1c3QsIEluYy4xFDASBgNVBAMTC1JhcGlkU1NM -IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx3H4Vsce2cy1rfa0 -l6P7oeYLUF9QqjraD/w9KSRDxhApwfxVQHLuverfn7ZB9EhLyG7+T1cSi1v6kt1e -6K3z8Buxe037z/3R5fjj3Of1c3/fAUnPjFbBvTfjW761T4uL8NpPx+PdVUdp3/Jb -ewdPPeWsIcHIHXro5/YPoar1b96oZU8QiZwD84l6pV4BcjPtqelaHnnzh8jfyMX8 -N8iamte4dsywPuf95lTq319SQXhZV63xEtZ/vNWfcNMFbPqjfWdY3SZiHTGSDHl5 -HI7PynvBZq+odEj7joLCniyZXHstXZu8W1eefDp6E63yoxhbK1kPzVw662gzxigd -gtFQiwIDAQABo4HZMIHWMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUa2k9ahhC -St2PAmU5/TUkhniRFjAwHwYDVR0jBBgwFoAUwHqYaI2J+6sFZAwRfap9ZbjKzE4w -EgYDVR0TAQH/BAgwBgEB/wIBADA6BgNVHR8EMzAxMC+gLaArhilodHRwOi8vY3Js -Lmdlb3RydXN0LmNvbS9jcmxzL2d0Z2xvYmFsLmNybDA0BggrBgEFBQcBAQQoMCYw -JAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmdlb3RydXN0LmNvbTANBgkqhkiG9w0B -AQUFAAOCAQEAq7y8Cl0YlOPBscOoTFXWvrSY8e48HM3P8yQkXJYDJ1j8Nq6iL4/x -/torAsMzvcjdSCIrYA+lAxD9d/jQ7ZZnT/3qRyBwVNypDFV+4ZYlitm12ldKvo2O -SUNjpWxOJ4cl61tt/qJ/OCjgNqutOaWlYsS3XFgsql0BYKZiZ6PAx2Ij9OdsRu61 -04BqIhPSLT90T+qvjF+0OJzbrs6vhB6m9jRRWXnT43XcvNfzc9+S7NIgWW+c+5X4 -knYYCnwPLKbK3opie9jzzl9ovY8+wXS7FXI6FoOpC+ZNmZzYV+yoAVHHb1c0XqtK -LEL2TxyJeN4mTvVvk0wVaydWTQBUbHq3tw== ------END CERTIFICATE----- -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 144470 (0x23456) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA - Validity - Not Before: May 21 04:00:00 2002 GMT - Not After : May 21 04:00:00 2022 GMT - Subject: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (2048 bit) - Modulus (2048 bit): - 00:da:cc:18:63:30:fd:f4:17:23:1a:56:7e:5b:df: - 3c:6c:38:e4:71:b7:78:91:d4:bc:a1:d8:4c:f8:a8: - 43:b6:03:e9:4d:21:07:08:88:da:58:2f:66:39:29: - bd:05:78:8b:9d:38:e8:05:b7:6a:7e:71:a4:e6:c4: - 60:a6:b0:ef:80:e4:89:28:0f:9e:25:d6:ed:83:f3: - ad:a6:91:c7:98:c9:42:18:35:14:9d:ad:98:46:92: - 2e:4f:ca:f1:87:43:c1:16:95:57:2d:50:ef:89:2d: - 80:7a:57:ad:f2:ee:5f:6b:d2:00:8d:b9:14:f8:14: - 15:35:d9:c0:46:a3:7b:72:c8:91:bf:c9:55:2b:cd: - d0:97:3e:9c:26:64:cc:df:ce:83:19:71:ca:4e:e6: - d4:d5:7b:a9:19:cd:55:de:c8:ec:d2:5e:38:53:e5: - 5c:4f:8c:2d:fe:50:23:36:fc:66:e6:cb:8e:a4:39: - 19:00:b7:95:02:39:91:0b:0e:fe:38:2e:d1:1d:05: - 9a:f6:4d:3e:6f:0f:07:1d:af:2c:1e:8f:60:39:e2: - fa:36:53:13:39:d4:5e:26:2b:db:3d:a8:14:bd:32: - eb:18:03:28:52:04:71:e5:ab:33:3d:e1:38:bb:07: - 36:84:62:9c:79:ea:16:30:f4:5f:c0:2b:e8:71:6b: - e4:f9 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: critical - CA:TRUE - X509v3 Subject Key Identifier: - C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E - X509v3 Authority Key Identifier: - keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E - - Signature Algorithm: sha1WithRSAEncryption - 35:e3:29:6a:e5:2f:5d:54:8e:29:50:94:9f:99:1a:14:e4:8f: - 78:2a:62:94:a2:27:67:9e:d0:cf:1a:5e:47:e9:c1:b2:a4:cf: - dd:41:1a:05:4e:9b:4b:ee:4a:6f:55:52:b3:24:a1:37:0a:eb: - 64:76:2a:2e:2c:f3:fd:3b:75:90:bf:fa:71:d8:c7:3d:37:d2: - b5:05:95:62:b9:a6:de:89:3d:36:7b:38:77:48:97:ac:a6:20: - 8f:2e:a6:c9:0c:c2:b2:99:45:00:c7:ce:11:51:22:22:e0:a5: - ea:b6:15:48:09:64:ea:5e:4f:74:f7:05:3e:c7:8a:52:0c:db: - 15:b4:bd:6d:9b:e5:c6:b1:54:68:a9:e3:69:90:b6:9a:a5:0f: - b8:b9:3f:20:7d:ae:4a:b5:b8:9c:e4:1d:b6:ab:e6:94:a5:c1: - c7:83:ad:db:f5:27:87:0e:04:6c:d5:ff:dd:a0:5d:ed:87:52: - b7:2b:15:02:ae:39:a6:6a:74:e9:da:c4:e7:bc:4d:34:1e:a9: - 5c:4d:33:5f:92:09:2f:88:66:5d:77:97:c7:1d:76:13:a9:d5: - e5:f1:16:09:11:35:d5:ac:db:24:71:70:2c:98:56:0b:d9:17: - b4:d1:e3:51:2b:5e:75:e8:d5:d0:dc:4f:34:ed:c2:05:66:80: - a1:cb:e6:33 ------BEGIN CERTIFICATE----- -MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT -MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i -YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG -EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg -R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 -9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq -fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv -iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU -1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ -bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW -MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA -ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l -uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn -Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS -tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF -PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un -hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV -5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== ------END CERTIFICATE----- \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/897abe0b41fd2f64e9e2e351cbc36d76 b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/897abe0b41fd2f64e9e2e351cbc36d76 deleted file mode 100644 index 591907f..0000000 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/897abe0b41fd2f64e9e2e351cbc36d76 +++ /dev/null @@ -1,285 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - 03:37:b9:28:34:7c:60:a6:ae:c5:ad:b1:21:7f:38:60 - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA - Validity - Not Before: Nov 9 12:00:00 2007 GMT - Not After : Nov 10 00:00:00 2021 GMT - Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV CA-1 - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (2048 bit) - Modulus (2048 bit): - 00:f3:96:62:d8:75:6e:19:ff:3f:34:7c:49:4f:31: - 7e:0d:04:4e:99:81:e2:b3:85:55:91:30:b1:c0:af: - 70:bb:2c:a8:e7:18:aa:3f:78:f7:90:68:52:86:01: - 88:97:e2:3b:06:65:90:aa:bd:65:76:c2:ec:be:10: - 5b:37:78:83:60:75:45:c6:bd:74:aa:b6:9f:a4:3a: - 01:50:17:c4:39:69:b9:f1:4f:ef:82:c1:ca:f3:4a: - db:cc:9e:50:4f:4d:40:a3:3a:90:e7:86:66:bc:f0: - 3e:76:28:4c:d1:75:80:9e:6a:35:14:35:03:9e:db: - 0c:8c:c2:28:ad:50:b2:ce:f6:91:a3:c3:a5:0a:58: - 49:f6:75:44:6c:ba:f9:ce:e9:ab:3a:02:e0:4d:f3: - ac:e2:7a:e0:60:22:05:3c:82:d3:52:e2:f3:9c:47: - f8:3b:d8:b2:4b:93:56:4a:bf:70:ab:3e:e9:68:c8: - 1d:8f:58:1d:2a:4d:5e:27:3d:ad:0a:59:2f:5a:11: - 20:40:d9:68:04:68:2d:f4:c0:84:0b:0a:1b:78:df: - ed:1a:58:dc:fb:41:5a:6d:6b:f2:ed:1c:ee:5c:32: - b6:5c:ec:d7:a6:03:32:a6:e8:de:b7:28:27:59:88: - 80:ff:7b:ad:89:58:d5:1e:14:a4:f2:b0:70:d4:a0: - 3e:a7 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Key Usage: critical - Digital Signature, Certificate Sign, CRL Sign - X509v3 Extended Key Usage: - TLS Web Server Authentication, TLS Web Client Authentication, Code Signing, E-mail Protection, Time Stamping - X509v3 Certificate Policies: - Policy: 2.16.840.1.114412.2.1 - CPS: http://www.digicert.com/ssl-cps-repository.htm - User Notice: - Explicit Text: - - X509v3 Basic Constraints: critical - CA:TRUE, pathlen:0 - Authority Information Access: - OCSP - URI:http://ocsp.digicert.com - CA Issuers - URI:http://www.digicert.com/CACerts/DigiCertHighAssuranceEVRootCA.crt - - X509v3 CRL Distribution Points: - URI:http://crl3.digicert.com/DigiCertHighAssuranceEVRootCA.crl - URI:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl - - X509v3 Subject Key Identifier: - 4C:58:CB:25:F0:41:4F:52:F4:28:C8:81:43:9B:A6:A8:A0:E6:92:E5 - X509v3 Authority Key Identifier: - keyid:B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3 - - Signature Algorithm: sha1WithRSAEncryption - 4c:7a:17:87:28:5d:17:bc:b2:32:73:bf:cd:2e:f5:58:31:1d: - f0:b1:71:54:9c:d6:9b:67:93:db:2f:03:3e:16:6f:1e:03:c9: - 53:84:a3:56:60:1e:78:94:1b:a2:a8:6f:a3:a4:8b:52:91:d7: - dd:5c:95:bb:ef:b5:16:49:e9:a5:42:4f:34:f2:47:ff:ae:81: - 7f:13:54:b7:20:c4:70:15:cb:81:0a:81:cb:74:57:dc:9c:df: - 24:a4:29:0c:18:f0:1c:e4:ae:07:33:ec:f1:49:3e:55:cf:6e: - 4f:0d:54:7b:d3:c9:e8:15:48:d4:c5:bb:dc:35:1c:77:45:07: - 48:45:85:bd:d7:7e:53:b8:c0:16:d9:95:cd:8b:8d:7d:c9:60: - 4f:d1:a2:9b:e3:d0:30:d6:b4:73:36:e6:d2:f9:03:b2:e3:a4: - f5:e5:b8:3e:04:49:00:ba:2e:a6:4a:72:83:72:9d:f7:0b:8c: - a9:89:e7:b3:d7:64:1f:d6:e3:60:cb:03:c4:dc:88:e9:9d:25: - 01:00:71:cb:03:b4:29:60:25:8f:f9:46:d1:7b:71:ae:cd:53: - 12:5b:84:8e:c2:0f:c7:ed:93:19:d9:c9:fa:8f:58:34:76:32: - 2f:ae:e1:50:14:61:d4:a8:58:a3:c8:30:13:23:ef:c6:25:8c: - 36:8f:1c:80 ------BEGIN CERTIFICATE----- -MIIG5jCCBc6gAwIBAgIQAze5KDR8YKauxa2xIX84YDANBgkqhkiG9w0BAQUFADBs -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBFViBSb290IENBMB4XDTA3MTEwOTEyMDAwMFoXDTIxMTExMDAwMDAwMFowaTEL -MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 -LmRpZ2ljZXJ0LmNvbTEoMCYGA1UEAxMfRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug -RVYgQ0EtMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPOWYth1bhn/ -PzR8SU8xfg0ETpmB4rOFVZEwscCvcLssqOcYqj9495BoUoYBiJfiOwZlkKq9ZXbC -7L4QWzd4g2B1Rca9dKq2n6Q6AVAXxDlpufFP74LByvNK28yeUE9NQKM6kOeGZrzw -PnYoTNF1gJ5qNRQ1A57bDIzCKK1Qss72kaPDpQpYSfZ1RGy6+c7pqzoC4E3zrOJ6 -4GAiBTyC01Li85xH+DvYskuTVkq/cKs+6WjIHY9YHSpNXic9rQpZL1oRIEDZaARo -LfTAhAsKG3jf7RpY3PtBWm1r8u0c7lwytlzs16YDMqbo3rcoJ1mIgP97rYlY1R4U -pPKwcNSgPqcCAwEAAaOCA4UwggOBMA4GA1UdDwEB/wQEAwIBhjA7BgNVHSUENDAy -BggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUH -AwgwggHEBgNVHSAEggG7MIIBtzCCAbMGCWCGSAGG/WwCATCCAaQwOgYIKwYBBQUH -AgEWLmh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5o -dG0wggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0 -AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1 -AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABp -AGcAaQBDAGUAcgB0ACAARQBWACAAQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBl -AGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBo -AGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAg -AGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAg -AGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wEgYDVR0TAQH/BAgwBgEB/wIBADCB -gwYIKwYBBQUHAQEEdzB1MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy -dC5jb20wTQYIKwYBBQUHMAKGQWh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NBQ2Vy -dHMvRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3J0MIGPBgNVHR8EgYcw -gYQwQKA+oDyGOmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEhpZ2hB -c3N1cmFuY2VFVlJvb3RDQS5jcmwwQKA+oDyGOmh0dHA6Ly9jcmw0LmRpZ2ljZXJ0 -LmNvbS9EaWdpQ2VydEhpZ2hBc3N1cmFuY2VFVlJvb3RDQS5jcmwwHQYDVR0OBBYE -FExYyyXwQU9S9CjIgUObpqig5pLlMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSYJhoI -Au9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQBMeheHKF0XvLIyc7/NLvVYMR3wsXFU -nNabZ5PbLwM+Fm8eA8lThKNWYB54lBuiqG+jpItSkdfdXJW777UWSemlQk808kf/ -roF/E1S3IMRwFcuBCoHLdFfcnN8kpCkMGPAc5K4HM+zxST5Vz25PDVR708noFUjU -xbvcNRx3RQdIRYW9135TuMAW2ZXNi419yWBP0aKb49Aw1rRzNubS+QOy46T15bg+ -BEkAui6mSnKDcp33C4ypieez12Qf1uNgywPE3IjpnSUBAHHLA7QpYCWP+UbRe3Gu -zVMSW4SOwg/H7ZMZ2cn6j1g0djIvruFQFGHUqFijyDATI+/GJYw2jxyA ------END CERTIFICATE----- -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 1116160165 (0x428740a5) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=Entrust.net, OU=www.entrust.net/CPS incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Secure Server Certification Authority - Validity - Not Before: Oct 1 05:00:00 2006 GMT - Not After : Jul 26 18:15:15 2014 GMT - Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (2048 bit) - Modulus (2048 bit): - 00:c6:cc:e5:73:e6:fb:d4:bb:e5:2d:2d:32:a6:df: - e5:81:3f:c9:cd:25:49:b6:71:2a:c3:d5:94:34:67: - a2:0a:1c:b0:5f:69:a6:40:b1:c4:b7:b2:8f:d0:98: - a4:a9:41:59:3a:d3:dc:94:d6:3c:db:74:38:a4:4a: - cc:4d:25:82:f7:4a:a5:53:12:38:ee:f3:49:6d:71: - 91:7e:63:b6:ab:a6:5f:c3:a4:84:f8:4f:62:51:be: - f8:c5:ec:db:38:92:e3:06:e5:08:91:0c:c4:28:41: - 55:fb:cb:5a:89:15:7e:71:e8:35:bf:4d:72:09:3d: - be:3a:38:50:5b:77:31:1b:8d:b3:c7:24:45:9a:a7: - ac:6d:00:14:5a:04:b7:ba:13:eb:51:0a:98:41:41: - 22:4e:65:61:87:81:41:50:a6:79:5c:89:de:19:4a: - 57:d5:2e:e6:5d:1c:53:2c:7e:98:cd:1a:06:16:a4: - 68:73:d0:34:04:13:5c:a1:71:d3:5a:7c:55:db:5e: - 64:e1:37:87:30:56:04:e5:11:b4:29:80:12:f1:79: - 39:88:a2:02:11:7c:27:66:b7:88:b7:78:f2:ca:0a: - a8:38:ab:0a:64:c2:bf:66:5d:95:84:c1:a1:25:1e: - 87:5d:1a:50:0b:20:12:cc:41:bb:6e:0b:51:38:b8: - 4b:cb - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: critical - CA:TRUE, pathlen:1 - X509v3 Extended Key Usage: - TLS Web Server Authentication, TLS Web Client Authentication, E-mail Protection - Authority Information Access: - OCSP - URI:http://ocsp.entrust.net - - X509v3 CRL Distribution Points: - URI:http://crl.entrust.net/server1.crl - - X509v3 Subject Key Identifier: - B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3 - X509v3 Key Usage: - Certificate Sign, CRL Sign - X509v3 Authority Key Identifier: - keyid:F0:17:62:13:55:3D:B3:FF:0A:00:6B:FB:50:84:97:F3:ED:62:D0:1A - - 1.2.840.113533.7.65.0: - 0 -..V7.1.... - Signature Algorithm: sha1WithRSAEncryption - 48:0e:2b:6f:20:62:4c:28:93:a3:24:3d:58:ab:21:cf:80:f8: - 9a:97:90:6a:22:ed:5a:7c:47:36:99:e7:79:84:75:ab:24:8f: - 92:0a:d5:61:04:ae:c3:6a:5c:b2:cc:d9:e4:44:87:6f:db:8f: - 38:62:f7:44:36:9d:ba:bc:6e:07:c4:d4:8d:e8:1f:d1:0b:60: - a3:b5:9c:ce:63:be:ed:67:dc:f8:ba:de:6e:c9:25:cb:5b:b5: - 9d:76:70:0b:df:42:72:f8:4f:41:11:64:a5:d2:ea:fc:d5:af: - 11:f4:15:38:67:9c:20:a8:4b:77:5a:91:32:42:32:e7:85:b3: - df:36 ------BEGIN CERTIFICATE----- -MIIEQjCCA6ugAwIBAgIEQodApTANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC -VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u -ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc -KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u -ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEw -MDEwNTAwMDBaFw0xNDA3MjYxODE1MTVaMGwxCzAJBgNVBAYTAlVTMRUwEwYDVQQK -EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xKzApBgNV -BAMTIkRpZ2lDZXJ0IEhpZ2ggQXNzdXJhbmNlIEVWIFJvb3QgQ0EwggEiMA0GCSqG -SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGzOVz5vvUu+UtLTKm3+WBP8nNJUm2cSrD -1ZQ0Z6IKHLBfaaZAscS3so/QmKSpQVk609yU1jzbdDikSsxNJYL3SqVTEjju80lt -cZF+Y7arpl/DpIT4T2JRvvjF7Ns4kuMG5QiRDMQoQVX7y1qJFX5x6DW/TXIJPb46 -OFBbdzEbjbPHJEWap6xtABRaBLe6E+tRCphBQSJOZWGHgUFQpnlcid4ZSlfVLuZd -HFMsfpjNGgYWpGhz0DQEE1yhcdNafFXbXmThN4cwVgTlEbQpgBLxeTmIogIRfCdm -t4i3ePLKCqg4qwpkwr9mXZWEwaElHoddGlALIBLMQbtuC1E4uEvLAgMBAAGjggET -MIIBDzASBgNVHRMBAf8ECDAGAQH/AgEBMCcGA1UdJQQgMB4GCCsGAQUFBwMBBggr -BgEFBQcDAgYIKwYBBQUHAwQwMwYIKwYBBQUHAQEEJzAlMCMGCCsGAQUFBzABhhdo -dHRwOi8vb2NzcC5lbnRydXN0Lm5ldDAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8v -Y3JsLmVudHJ1c3QubmV0L3NlcnZlcjEuY3JsMB0GA1UdDgQWBBSxPsNpA/i/RwHU -mCYaCALvY2QrwzALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7 -UISX8+1i0BowGQYJKoZIhvZ9B0EABAwwChsEVjcuMQMCAIEwDQYJKoZIhvcNAQEF -BQADgYEASA4rbyBiTCiToyQ9WKshz4D4mpeQaiLtWnxHNpnneYR1qySPkgrVYQSu -w2pcsszZ5ESHb9uPOGL3RDadurxuB8TUjegf0Qtgo7WczmO+7Wfc+Lrebskly1u1 -nXZwC99CcvhPQRFkpdLq/NWvEfQVOGecIKhLd1qRMkIy54Wz3zY= ------END CERTIFICATE----- -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 927650371 (0x374ad243) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=Entrust.net, OU=www.entrust.net/CPS incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Secure Server Certification Authority - Validity - Not Before: May 25 16:09:40 1999 GMT - Not After : May 25 16:39:40 2019 GMT - Subject: C=US, O=Entrust.net, OU=www.entrust.net/CPS incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Secure Server Certification Authority - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (1024 bit) - Modulus (1024 bit): - 00:cd:28:83:34:54:1b:89:f3:0f:af:37:91:31:ff: - af:31:60:c9:a8:e8:b2:10:68:ed:9f:e7:93:36:f1: - 0a:64:bb:47:f5:04:17:3f:23:47:4d:c5:27:19:81: - 26:0c:54:72:0d:88:2d:d9:1f:9a:12:9f:bc:b3:71: - d3:80:19:3f:47:66:7b:8c:35:28:d2:b9:0a:df:24: - da:9c:d6:50:79:81:7a:5a:d3:37:f7:c2:4a:d8:29: - 92:26:64:d1:e4:98:6c:3a:00:8a:f5:34:9b:65:f8: - ed:e3:10:ff:fd:b8:49:58:dc:a0:de:82:39:6b:81: - b1:16:19:61:b9:54:b6:e6:43 - Exponent: 3 (0x3) - X509v3 extensions: - Netscape Cert Type: - SSL CA, S/MIME CA, Object Signing CA - X509v3 CRL Distribution Points: - DirName:/C=US/O=Entrust.net/OU=www.entrust.net/CPS incorp. by ref. (limits liab.)/OU=(c) 1999 Entrust.net Limited/CN=Entrust.net Secure Server Certification Authority/CN=CRL1 - URI:http://www.entrust.net/CRL/net1.crl - - X509v3 Private Key Usage Period: - Not Before: May 25 16:09:40 1999 GMT, Not After: May 25 16:09:40 2019 GMT - X509v3 Key Usage: - Certificate Sign, CRL Sign - X509v3 Authority Key Identifier: - keyid:F0:17:62:13:55:3D:B3:FF:0A:00:6B:FB:50:84:97:F3:ED:62:D0:1A - - X509v3 Subject Key Identifier: - F0:17:62:13:55:3D:B3:FF:0A:00:6B:FB:50:84:97:F3:ED:62:D0:1A - X509v3 Basic Constraints: - CA:TRUE - 1.2.840.113533.7.65.0: - 0 -..V4.0.... - Signature Algorithm: sha1WithRSAEncryption - 90:dc:30:02:fa:64:74:c2:a7:0a:a5:7c:21:8d:34:17:a8:fb: - 47:0e:ff:25:7c:8d:13:0a:fb:e4:98:b5:ef:8c:f8:c5:10:0d: - f7:92:be:f1:c3:d5:d5:95:6a:04:bb:2c:ce:26:36:65:c8:31: - c6:e7:ee:3f:e3:57:75:84:7a:11:ef:46:4f:18:f4:d3:98:bb: - a8:87:32:ba:72:f6:3c:e2:3d:9f:d7:1d:d9:c3:60:43:8c:58: - 0e:22:96:2f:62:a3:2c:1f:ba:ad:05:ef:ab:32:78:87:a0:54: - 73:19:b5:5c:05:f9:52:3e:6d:2d:45:0b:f7:0a:93:ea:ed:06: - f9:b2 ------BEGIN CERTIFICATE----- -MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC -VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u -ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc -KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u -ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1 -MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE -ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j -b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF -bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg -U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA -A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/ -I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3 -wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC -AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb -oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5 -BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p -dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk -MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp -b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu -dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0 -MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi -E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa -MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI -hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN -95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd -2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI= ------END CERTIFICATE----- \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/ca-bundle.crt b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/ca-bundle.crt deleted file mode 100644 index 1681842..0000000 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/ca-bundle.crt +++ /dev/null @@ -1,563 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 145105 (0x236d1) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA - Validity - Not Before: Feb 19 22:45:05 2010 GMT - Not After : Feb 18 22:45:05 2020 GMT - Subject: C=US, O=GeoTrust, Inc., CN=RapidSSL CA - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (2048 bit) - Modulus (2048 bit): - 00:c7:71:f8:56:c7:1e:d9:cc:b5:ad:f6:b4:97:a3: - fb:a1:e6:0b:50:5f:50:aa:3a:da:0f:fc:3d:29:24: - 43:c6:10:29:c1:fc:55:40:72:ee:bd:ea:df:9f:b6: - 41:f4:48:4b:c8:6e:fe:4f:57:12:8b:5b:fa:92:dd: - 5e:e8:ad:f3:f0:1b:b1:7b:4d:fb:cf:fd:d1:e5:f8: - e3:dc:e7:f5:73:7f:df:01:49:cf:8c:56:c1:bd:37: - e3:5b:be:b5:4f:8b:8b:f0:da:4f:c7:e3:dd:55:47: - 69:df:f2:5b:7b:07:4f:3d:e5:ac:21:c1:c8:1d:7a: - e8:e7:f6:0f:a1:aa:f5:6f:de:a8:65:4f:10:89:9c: - 03:f3:89:7a:a5:5e:01:72:33:ed:a9:e9:5a:1e:79: - f3:87:c8:df:c8:c5:fc:37:c8:9a:9a:d7:b8:76:cc: - b0:3e:e7:fd:e6:54:ea:df:5f:52:41:78:59:57:ad: - f1:12:d6:7f:bc:d5:9f:70:d3:05:6c:fa:a3:7d:67: - 58:dd:26:62:1d:31:92:0c:79:79:1c:8e:cf:ca:7b: - c1:66:af:a8:74:48:fb:8e:82:c2:9e:2c:99:5c:7b: - 2d:5d:9b:bc:5b:57:9e:7c:3a:7a:13:ad:f2:a3:18: - 5b:2b:59:0f:cd:5c:3a:eb:68:33:c6:28:1d:82:d1: - 50:8b - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Key Usage: critical - Certificate Sign, CRL Sign - X509v3 Subject Key Identifier: - 6B:69:3D:6A:18:42:4A:DD:8F:02:65:39:FD:35:24:86:78:91:16:30 - X509v3 Authority Key Identifier: - keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E - - X509v3 Basic Constraints: critical - CA:TRUE, pathlen:0 - X509v3 CRL Distribution Points: - URI:http://crl.geotrust.com/crls/gtglobal.crl - - Authority Information Access: - OCSP - URI:http://ocsp.geotrust.com - - Signature Algorithm: sha1WithRSAEncryption - ab:bc:bc:0a:5d:18:94:e3:c1:b1:c3:a8:4c:55:d6:be:b4:98: - f1:ee:3c:1c:cd:cf:f3:24:24:5c:96:03:27:58:fc:36:ae:a2: - 2f:8f:f1:fe:da:2b:02:c3:33:bd:c8:dd:48:22:2b:60:0f:a5: - 03:10:fd:77:f8:d0:ed:96:67:4f:fd:ea:47:20:70:54:dc:a9: - 0c:55:7e:e1:96:25:8a:d9:b5:da:57:4a:be:8d:8e:49:43:63: - a5:6c:4e:27:87:25:eb:5b:6d:fe:a2:7f:38:28:e0:36:ab:ad: - 39:a5:a5:62:c4:b7:5c:58:2c:aa:5d:01:60:a6:62:67:a3:c0: - c7:62:23:f4:e7:6c:46:ee:b5:d3:80:6a:22:13:d2:2d:3f:74: - 4f:ea:af:8c:5f:b4:38:9c:db:ae:ce:af:84:1e:a6:f6:34:51: - 59:79:d3:e3:75:dc:bc:d7:f3:73:df:92:ec:d2:20:59:6f:9c: - fb:95:f8:92:76:18:0a:7c:0f:2c:a6:ca:de:8a:62:7b:d8:f3: - ce:5f:68:bd:8f:3e:c1:74:bb:15:72:3a:16:83:a9:0b:e6:4d: - 99:9c:d8:57:ec:a8:01:51:c7:6f:57:34:5e:ab:4a:2c:42:f6: - 4f:1c:89:78:de:26:4e:f5:6f:93:4c:15:6b:27:56:4d:00:54: - 6c:7a:b7:b7 ------BEGIN CERTIFICATE----- -MIID1TCCAr2gAwIBAgIDAjbRMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT -MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i -YWwgQ0EwHhcNMTAwMjE5MjI0NTA1WhcNMjAwMjE4MjI0NTA1WjA8MQswCQYDVQQG -EwJVUzEXMBUGA1UEChMOR2VvVHJ1c3QsIEluYy4xFDASBgNVBAMTC1JhcGlkU1NM -IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx3H4Vsce2cy1rfa0 -l6P7oeYLUF9QqjraD/w9KSRDxhApwfxVQHLuverfn7ZB9EhLyG7+T1cSi1v6kt1e -6K3z8Buxe037z/3R5fjj3Of1c3/fAUnPjFbBvTfjW761T4uL8NpPx+PdVUdp3/Jb -ewdPPeWsIcHIHXro5/YPoar1b96oZU8QiZwD84l6pV4BcjPtqelaHnnzh8jfyMX8 -N8iamte4dsywPuf95lTq319SQXhZV63xEtZ/vNWfcNMFbPqjfWdY3SZiHTGSDHl5 -HI7PynvBZq+odEj7joLCniyZXHstXZu8W1eefDp6E63yoxhbK1kPzVw662gzxigd -gtFQiwIDAQABo4HZMIHWMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUa2k9ahhC -St2PAmU5/TUkhniRFjAwHwYDVR0jBBgwFoAUwHqYaI2J+6sFZAwRfap9ZbjKzE4w -EgYDVR0TAQH/BAgwBgEB/wIBADA6BgNVHR8EMzAxMC+gLaArhilodHRwOi8vY3Js -Lmdlb3RydXN0LmNvbS9jcmxzL2d0Z2xvYmFsLmNybDA0BggrBgEFBQcBAQQoMCYw -JAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmdlb3RydXN0LmNvbTANBgkqhkiG9w0B -AQUFAAOCAQEAq7y8Cl0YlOPBscOoTFXWvrSY8e48HM3P8yQkXJYDJ1j8Nq6iL4/x -/torAsMzvcjdSCIrYA+lAxD9d/jQ7ZZnT/3qRyBwVNypDFV+4ZYlitm12ldKvo2O -SUNjpWxOJ4cl61tt/qJ/OCjgNqutOaWlYsS3XFgsql0BYKZiZ6PAx2Ij9OdsRu61 -04BqIhPSLT90T+qvjF+0OJzbrs6vhB6m9jRRWXnT43XcvNfzc9+S7NIgWW+c+5X4 -knYYCnwPLKbK3opie9jzzl9ovY8+wXS7FXI6FoOpC+ZNmZzYV+yoAVHHb1c0XqtK -LEL2TxyJeN4mTvVvk0wVaydWTQBUbHq3tw== ------END CERTIFICATE----- -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 144470 (0x23456) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA - Validity - Not Before: May 21 04:00:00 2002 GMT - Not After : May 21 04:00:00 2022 GMT - Subject: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (2048 bit) - Modulus (2048 bit): - 00:da:cc:18:63:30:fd:f4:17:23:1a:56:7e:5b:df: - 3c:6c:38:e4:71:b7:78:91:d4:bc:a1:d8:4c:f8:a8: - 43:b6:03:e9:4d:21:07:08:88:da:58:2f:66:39:29: - bd:05:78:8b:9d:38:e8:05:b7:6a:7e:71:a4:e6:c4: - 60:a6:b0:ef:80:e4:89:28:0f:9e:25:d6:ed:83:f3: - ad:a6:91:c7:98:c9:42:18:35:14:9d:ad:98:46:92: - 2e:4f:ca:f1:87:43:c1:16:95:57:2d:50:ef:89:2d: - 80:7a:57:ad:f2:ee:5f:6b:d2:00:8d:b9:14:f8:14: - 15:35:d9:c0:46:a3:7b:72:c8:91:bf:c9:55:2b:cd: - d0:97:3e:9c:26:64:cc:df:ce:83:19:71:ca:4e:e6: - d4:d5:7b:a9:19:cd:55:de:c8:ec:d2:5e:38:53:e5: - 5c:4f:8c:2d:fe:50:23:36:fc:66:e6:cb:8e:a4:39: - 19:00:b7:95:02:39:91:0b:0e:fe:38:2e:d1:1d:05: - 9a:f6:4d:3e:6f:0f:07:1d:af:2c:1e:8f:60:39:e2: - fa:36:53:13:39:d4:5e:26:2b:db:3d:a8:14:bd:32: - eb:18:03:28:52:04:71:e5:ab:33:3d:e1:38:bb:07: - 36:84:62:9c:79:ea:16:30:f4:5f:c0:2b:e8:71:6b: - e4:f9 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: critical - CA:TRUE - X509v3 Subject Key Identifier: - C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E - X509v3 Authority Key Identifier: - keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E - - Signature Algorithm: sha1WithRSAEncryption - 35:e3:29:6a:e5:2f:5d:54:8e:29:50:94:9f:99:1a:14:e4:8f: - 78:2a:62:94:a2:27:67:9e:d0:cf:1a:5e:47:e9:c1:b2:a4:cf: - dd:41:1a:05:4e:9b:4b:ee:4a:6f:55:52:b3:24:a1:37:0a:eb: - 64:76:2a:2e:2c:f3:fd:3b:75:90:bf:fa:71:d8:c7:3d:37:d2: - b5:05:95:62:b9:a6:de:89:3d:36:7b:38:77:48:97:ac:a6:20: - 8f:2e:a6:c9:0c:c2:b2:99:45:00:c7:ce:11:51:22:22:e0:a5: - ea:b6:15:48:09:64:ea:5e:4f:74:f7:05:3e:c7:8a:52:0c:db: - 15:b4:bd:6d:9b:e5:c6:b1:54:68:a9:e3:69:90:b6:9a:a5:0f: - b8:b9:3f:20:7d:ae:4a:b5:b8:9c:e4:1d:b6:ab:e6:94:a5:c1: - c7:83:ad:db:f5:27:87:0e:04:6c:d5:ff:dd:a0:5d:ed:87:52: - b7:2b:15:02:ae:39:a6:6a:74:e9:da:c4:e7:bc:4d:34:1e:a9: - 5c:4d:33:5f:92:09:2f:88:66:5d:77:97:c7:1d:76:13:a9:d5: - e5:f1:16:09:11:35:d5:ac:db:24:71:70:2c:98:56:0b:d9:17: - b4:d1:e3:51:2b:5e:75:e8:d5:d0:dc:4f:34:ed:c2:05:66:80: - a1:cb:e6:33 ------BEGIN CERTIFICATE----- -MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT -MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i -YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG -EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg -R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 -9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq -fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv -iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU -1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ -bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW -MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA -ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l -uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn -Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS -tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF -PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un -hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV -5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== ------END CERTIFICATE----- -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - 03:37:b9:28:34:7c:60:a6:ae:c5:ad:b1:21:7f:38:60 - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA - Validity - Not Before: Nov 9 12:00:00 2007 GMT - Not After : Nov 10 00:00:00 2021 GMT - Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV CA-1 - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (2048 bit) - Modulus (2048 bit): - 00:f3:96:62:d8:75:6e:19:ff:3f:34:7c:49:4f:31: - 7e:0d:04:4e:99:81:e2:b3:85:55:91:30:b1:c0:af: - 70:bb:2c:a8:e7:18:aa:3f:78:f7:90:68:52:86:01: - 88:97:e2:3b:06:65:90:aa:bd:65:76:c2:ec:be:10: - 5b:37:78:83:60:75:45:c6:bd:74:aa:b6:9f:a4:3a: - 01:50:17:c4:39:69:b9:f1:4f:ef:82:c1:ca:f3:4a: - db:cc:9e:50:4f:4d:40:a3:3a:90:e7:86:66:bc:f0: - 3e:76:28:4c:d1:75:80:9e:6a:35:14:35:03:9e:db: - 0c:8c:c2:28:ad:50:b2:ce:f6:91:a3:c3:a5:0a:58: - 49:f6:75:44:6c:ba:f9:ce:e9:ab:3a:02:e0:4d:f3: - ac:e2:7a:e0:60:22:05:3c:82:d3:52:e2:f3:9c:47: - f8:3b:d8:b2:4b:93:56:4a:bf:70:ab:3e:e9:68:c8: - 1d:8f:58:1d:2a:4d:5e:27:3d:ad:0a:59:2f:5a:11: - 20:40:d9:68:04:68:2d:f4:c0:84:0b:0a:1b:78:df: - ed:1a:58:dc:fb:41:5a:6d:6b:f2:ed:1c:ee:5c:32: - b6:5c:ec:d7:a6:03:32:a6:e8:de:b7:28:27:59:88: - 80:ff:7b:ad:89:58:d5:1e:14:a4:f2:b0:70:d4:a0: - 3e:a7 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Key Usage: critical - Digital Signature, Certificate Sign, CRL Sign - X509v3 Extended Key Usage: - TLS Web Server Authentication, TLS Web Client Authentication, Code Signing, E-mail Protection, Time Stamping - X509v3 Certificate Policies: - Policy: 2.16.840.1.114412.2.1 - CPS: http://www.digicert.com/ssl-cps-repository.htm - User Notice: - Explicit Text: - - X509v3 Basic Constraints: critical - CA:TRUE, pathlen:0 - Authority Information Access: - OCSP - URI:http://ocsp.digicert.com - CA Issuers - URI:http://www.digicert.com/CACerts/DigiCertHighAssuranceEVRootCA.crt - - X509v3 CRL Distribution Points: - URI:http://crl3.digicert.com/DigiCertHighAssuranceEVRootCA.crl - URI:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl - - X509v3 Subject Key Identifier: - 4C:58:CB:25:F0:41:4F:52:F4:28:C8:81:43:9B:A6:A8:A0:E6:92:E5 - X509v3 Authority Key Identifier: - keyid:B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3 - - Signature Algorithm: sha1WithRSAEncryption - 4c:7a:17:87:28:5d:17:bc:b2:32:73:bf:cd:2e:f5:58:31:1d: - f0:b1:71:54:9c:d6:9b:67:93:db:2f:03:3e:16:6f:1e:03:c9: - 53:84:a3:56:60:1e:78:94:1b:a2:a8:6f:a3:a4:8b:52:91:d7: - dd:5c:95:bb:ef:b5:16:49:e9:a5:42:4f:34:f2:47:ff:ae:81: - 7f:13:54:b7:20:c4:70:15:cb:81:0a:81:cb:74:57:dc:9c:df: - 24:a4:29:0c:18:f0:1c:e4:ae:07:33:ec:f1:49:3e:55:cf:6e: - 4f:0d:54:7b:d3:c9:e8:15:48:d4:c5:bb:dc:35:1c:77:45:07: - 48:45:85:bd:d7:7e:53:b8:c0:16:d9:95:cd:8b:8d:7d:c9:60: - 4f:d1:a2:9b:e3:d0:30:d6:b4:73:36:e6:d2:f9:03:b2:e3:a4: - f5:e5:b8:3e:04:49:00:ba:2e:a6:4a:72:83:72:9d:f7:0b:8c: - a9:89:e7:b3:d7:64:1f:d6:e3:60:cb:03:c4:dc:88:e9:9d:25: - 01:00:71:cb:03:b4:29:60:25:8f:f9:46:d1:7b:71:ae:cd:53: - 12:5b:84:8e:c2:0f:c7:ed:93:19:d9:c9:fa:8f:58:34:76:32: - 2f:ae:e1:50:14:61:d4:a8:58:a3:c8:30:13:23:ef:c6:25:8c: - 36:8f:1c:80 ------BEGIN CERTIFICATE----- -MIIG5jCCBc6gAwIBAgIQAze5KDR8YKauxa2xIX84YDANBgkqhkiG9w0BAQUFADBs -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBFViBSb290IENBMB4XDTA3MTEwOTEyMDAwMFoXDTIxMTExMDAwMDAwMFowaTEL -MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 -LmRpZ2ljZXJ0LmNvbTEoMCYGA1UEAxMfRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug -RVYgQ0EtMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPOWYth1bhn/ -PzR8SU8xfg0ETpmB4rOFVZEwscCvcLssqOcYqj9495BoUoYBiJfiOwZlkKq9ZXbC -7L4QWzd4g2B1Rca9dKq2n6Q6AVAXxDlpufFP74LByvNK28yeUE9NQKM6kOeGZrzw -PnYoTNF1gJ5qNRQ1A57bDIzCKK1Qss72kaPDpQpYSfZ1RGy6+c7pqzoC4E3zrOJ6 -4GAiBTyC01Li85xH+DvYskuTVkq/cKs+6WjIHY9YHSpNXic9rQpZL1oRIEDZaARo -LfTAhAsKG3jf7RpY3PtBWm1r8u0c7lwytlzs16YDMqbo3rcoJ1mIgP97rYlY1R4U -pPKwcNSgPqcCAwEAAaOCA4UwggOBMA4GA1UdDwEB/wQEAwIBhjA7BgNVHSUENDAy -BggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUH -AwgwggHEBgNVHSAEggG7MIIBtzCCAbMGCWCGSAGG/WwCATCCAaQwOgYIKwYBBQUH -AgEWLmh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5o -dG0wggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0 -AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1 -AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABp -AGcAaQBDAGUAcgB0ACAARQBWACAAQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBl -AGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBo -AGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAg -AGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAg -AGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wEgYDVR0TAQH/BAgwBgEB/wIBADCB -gwYIKwYBBQUHAQEEdzB1MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy -dC5jb20wTQYIKwYBBQUHMAKGQWh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NBQ2Vy -dHMvRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3J0MIGPBgNVHR8EgYcw -gYQwQKA+oDyGOmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEhpZ2hB -c3N1cmFuY2VFVlJvb3RDQS5jcmwwQKA+oDyGOmh0dHA6Ly9jcmw0LmRpZ2ljZXJ0 -LmNvbS9EaWdpQ2VydEhpZ2hBc3N1cmFuY2VFVlJvb3RDQS5jcmwwHQYDVR0OBBYE -FExYyyXwQU9S9CjIgUObpqig5pLlMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSYJhoI -Au9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQBMeheHKF0XvLIyc7/NLvVYMR3wsXFU -nNabZ5PbLwM+Fm8eA8lThKNWYB54lBuiqG+jpItSkdfdXJW777UWSemlQk808kf/ -roF/E1S3IMRwFcuBCoHLdFfcnN8kpCkMGPAc5K4HM+zxST5Vz25PDVR708noFUjU -xbvcNRx3RQdIRYW9135TuMAW2ZXNi419yWBP0aKb49Aw1rRzNubS+QOy46T15bg+ -BEkAui6mSnKDcp33C4ypieez12Qf1uNgywPE3IjpnSUBAHHLA7QpYCWP+UbRe3Gu -zVMSW4SOwg/H7ZMZ2cn6j1g0djIvruFQFGHUqFijyDATI+/GJYw2jxyA ------END CERTIFICATE----- -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 1116160165 (0x428740a5) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=Entrust.net, OU=www.entrust.net/CPS incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Secure Server Certification Authority - Validity - Not Before: Oct 1 05:00:00 2006 GMT - Not After : Jul 26 18:15:15 2014 GMT - Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (2048 bit) - Modulus (2048 bit): - 00:c6:cc:e5:73:e6:fb:d4:bb:e5:2d:2d:32:a6:df: - e5:81:3f:c9:cd:25:49:b6:71:2a:c3:d5:94:34:67: - a2:0a:1c:b0:5f:69:a6:40:b1:c4:b7:b2:8f:d0:98: - a4:a9:41:59:3a:d3:dc:94:d6:3c:db:74:38:a4:4a: - cc:4d:25:82:f7:4a:a5:53:12:38:ee:f3:49:6d:71: - 91:7e:63:b6:ab:a6:5f:c3:a4:84:f8:4f:62:51:be: - f8:c5:ec:db:38:92:e3:06:e5:08:91:0c:c4:28:41: - 55:fb:cb:5a:89:15:7e:71:e8:35:bf:4d:72:09:3d: - be:3a:38:50:5b:77:31:1b:8d:b3:c7:24:45:9a:a7: - ac:6d:00:14:5a:04:b7:ba:13:eb:51:0a:98:41:41: - 22:4e:65:61:87:81:41:50:a6:79:5c:89:de:19:4a: - 57:d5:2e:e6:5d:1c:53:2c:7e:98:cd:1a:06:16:a4: - 68:73:d0:34:04:13:5c:a1:71:d3:5a:7c:55:db:5e: - 64:e1:37:87:30:56:04:e5:11:b4:29:80:12:f1:79: - 39:88:a2:02:11:7c:27:66:b7:88:b7:78:f2:ca:0a: - a8:38:ab:0a:64:c2:bf:66:5d:95:84:c1:a1:25:1e: - 87:5d:1a:50:0b:20:12:cc:41:bb:6e:0b:51:38:b8: - 4b:cb - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: critical - CA:TRUE, pathlen:1 - X509v3 Extended Key Usage: - TLS Web Server Authentication, TLS Web Client Authentication, E-mail Protection - Authority Information Access: - OCSP - URI:http://ocsp.entrust.net - - X509v3 CRL Distribution Points: - URI:http://crl.entrust.net/server1.crl - - X509v3 Subject Key Identifier: - B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3 - X509v3 Key Usage: - Certificate Sign, CRL Sign - X509v3 Authority Key Identifier: - keyid:F0:17:62:13:55:3D:B3:FF:0A:00:6B:FB:50:84:97:F3:ED:62:D0:1A - - 1.2.840.113533.7.65.0: - 0 -..V7.1.... - Signature Algorithm: sha1WithRSAEncryption - 48:0e:2b:6f:20:62:4c:28:93:a3:24:3d:58:ab:21:cf:80:f8: - 9a:97:90:6a:22:ed:5a:7c:47:36:99:e7:79:84:75:ab:24:8f: - 92:0a:d5:61:04:ae:c3:6a:5c:b2:cc:d9:e4:44:87:6f:db:8f: - 38:62:f7:44:36:9d:ba:bc:6e:07:c4:d4:8d:e8:1f:d1:0b:60: - a3:b5:9c:ce:63:be:ed:67:dc:f8:ba:de:6e:c9:25:cb:5b:b5: - 9d:76:70:0b:df:42:72:f8:4f:41:11:64:a5:d2:ea:fc:d5:af: - 11:f4:15:38:67:9c:20:a8:4b:77:5a:91:32:42:32:e7:85:b3: - df:36 ------BEGIN CERTIFICATE----- -MIIEQjCCA6ugAwIBAgIEQodApTANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC -VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u -ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc -KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u -ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEw -MDEwNTAwMDBaFw0xNDA3MjYxODE1MTVaMGwxCzAJBgNVBAYTAlVTMRUwEwYDVQQK -EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xKzApBgNV -BAMTIkRpZ2lDZXJ0IEhpZ2ggQXNzdXJhbmNlIEVWIFJvb3QgQ0EwggEiMA0GCSqG -SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGzOVz5vvUu+UtLTKm3+WBP8nNJUm2cSrD -1ZQ0Z6IKHLBfaaZAscS3so/QmKSpQVk609yU1jzbdDikSsxNJYL3SqVTEjju80lt -cZF+Y7arpl/DpIT4T2JRvvjF7Ns4kuMG5QiRDMQoQVX7y1qJFX5x6DW/TXIJPb46 -OFBbdzEbjbPHJEWap6xtABRaBLe6E+tRCphBQSJOZWGHgUFQpnlcid4ZSlfVLuZd -HFMsfpjNGgYWpGhz0DQEE1yhcdNafFXbXmThN4cwVgTlEbQpgBLxeTmIogIRfCdm -t4i3ePLKCqg4qwpkwr9mXZWEwaElHoddGlALIBLMQbtuC1E4uEvLAgMBAAGjggET -MIIBDzASBgNVHRMBAf8ECDAGAQH/AgEBMCcGA1UdJQQgMB4GCCsGAQUFBwMBBggr -BgEFBQcDAgYIKwYBBQUHAwQwMwYIKwYBBQUHAQEEJzAlMCMGCCsGAQUFBzABhhdo -dHRwOi8vb2NzcC5lbnRydXN0Lm5ldDAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8v -Y3JsLmVudHJ1c3QubmV0L3NlcnZlcjEuY3JsMB0GA1UdDgQWBBSxPsNpA/i/RwHU -mCYaCALvY2QrwzALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7 -UISX8+1i0BowGQYJKoZIhvZ9B0EABAwwChsEVjcuMQMCAIEwDQYJKoZIhvcNAQEF -BQADgYEASA4rbyBiTCiToyQ9WKshz4D4mpeQaiLtWnxHNpnneYR1qySPkgrVYQSu -w2pcsszZ5ESHb9uPOGL3RDadurxuB8TUjegf0Qtgo7WczmO+7Wfc+Lrebskly1u1 -nXZwC99CcvhPQRFkpdLq/NWvEfQVOGecIKhLd1qRMkIy54Wz3zY= ------END CERTIFICATE----- -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 927650371 (0x374ad243) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=Entrust.net, OU=www.entrust.net/CPS incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Secure Server Certification Authority - Validity - Not Before: May 25 16:09:40 1999 GMT - Not After : May 25 16:39:40 2019 GMT - Subject: C=US, O=Entrust.net, OU=www.entrust.net/CPS incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Secure Server Certification Authority - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (1024 bit) - Modulus (1024 bit): - 00:cd:28:83:34:54:1b:89:f3:0f:af:37:91:31:ff: - af:31:60:c9:a8:e8:b2:10:68:ed:9f:e7:93:36:f1: - 0a:64:bb:47:f5:04:17:3f:23:47:4d:c5:27:19:81: - 26:0c:54:72:0d:88:2d:d9:1f:9a:12:9f:bc:b3:71: - d3:80:19:3f:47:66:7b:8c:35:28:d2:b9:0a:df:24: - da:9c:d6:50:79:81:7a:5a:d3:37:f7:c2:4a:d8:29: - 92:26:64:d1:e4:98:6c:3a:00:8a:f5:34:9b:65:f8: - ed:e3:10:ff:fd:b8:49:58:dc:a0:de:82:39:6b:81: - b1:16:19:61:b9:54:b6:e6:43 - Exponent: 3 (0x3) - X509v3 extensions: - Netscape Cert Type: - SSL CA, S/MIME CA, Object Signing CA - X509v3 CRL Distribution Points: - DirName:/C=US/O=Entrust.net/OU=www.entrust.net/CPS incorp. by ref. (limits liab.)/OU=(c) 1999 Entrust.net Limited/CN=Entrust.net Secure Server Certification Authority/CN=CRL1 - URI:http://www.entrust.net/CRL/net1.crl - - X509v3 Private Key Usage Period: - Not Before: May 25 16:09:40 1999 GMT, Not After: May 25 16:09:40 2019 GMT - X509v3 Key Usage: - Certificate Sign, CRL Sign - X509v3 Authority Key Identifier: - keyid:F0:17:62:13:55:3D:B3:FF:0A:00:6B:FB:50:84:97:F3:ED:62:D0:1A - - X509v3 Subject Key Identifier: - F0:17:62:13:55:3D:B3:FF:0A:00:6B:FB:50:84:97:F3:ED:62:D0:1A - X509v3 Basic Constraints: - CA:TRUE - 1.2.840.113533.7.65.0: - 0 -..V4.0.... - Signature Algorithm: sha1WithRSAEncryption - 90:dc:30:02:fa:64:74:c2:a7:0a:a5:7c:21:8d:34:17:a8:fb: - 47:0e:ff:25:7c:8d:13:0a:fb:e4:98:b5:ef:8c:f8:c5:10:0d: - f7:92:be:f1:c3:d5:d5:95:6a:04:bb:2c:ce:26:36:65:c8:31: - c6:e7:ee:3f:e3:57:75:84:7a:11:ef:46:4f:18:f4:d3:98:bb: - a8:87:32:ba:72:f6:3c:e2:3d:9f:d7:1d:d9:c3:60:43:8c:58: - 0e:22:96:2f:62:a3:2c:1f:ba:ad:05:ef:ab:32:78:87:a0:54: - 73:19:b5:5c:05:f9:52:3e:6d:2d:45:0b:f7:0a:93:ea:ed:06: - f9:b2 ------BEGIN CERTIFICATE----- -MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC -VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u -ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc -KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u -ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1 -MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE -ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j -b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF -bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg -U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA -A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/ -I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3 -wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC -AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb -oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5 -BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p -dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk -MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp -b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu -dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0 -MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi -E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa -MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI -hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN -95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd -2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI= ------END CERTIFICATE----- -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - 0a:5f:11:4d:03:5b:17:91:17:d2:ef:d4:03:8c:3f:3b - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA - Validity - Not Before: Apr 2 12:00:00 2008 GMT - Not After : Apr 3 00:00:00 2022 GMT - Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance CA-3 - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - Public-Key: (2048 bit) - Modulus: - 00:bf:61:0a:29:10:1f:5e:fe:34:37:51:08:f8:1e: - fb:22:ed:61:be:0b:0d:70:4c:50:63:26:75:15:b9: - 41:88:97:b6:f0:a0:15:bb:08:60:e0:42:e8:05:29: - 10:87:36:8a:28:65:a8:ef:31:07:74:6d:36:97:2f: - 28:46:66:04:c7:2a:79:26:7a:99:d5:8e:c3:6d:4f: - a0:5e:ad:bc:3d:91:c2:59:7b:5e:36:6c:c0:53:cf: - 00:08:32:3e:10:64:58:10:13:69:c7:0c:ee:9c:42: - 51:00:f9:05:44:ee:24:ce:7a:1f:ed:8c:11:bd:12: - a8:f3:15:f4:1c:7a:31:69:01:1b:a7:e6:5d:c0:9a: - 6c:7e:09:9e:e7:52:44:4a:10:3a:23:e4:9b:b6:03: - af:a8:9c:b4:5b:9f:d4:4b:ad:92:8c:ce:b5:11:2a: - aa:37:18:8d:b4:c2:b8:d8:5c:06:8c:f8:ff:23:bd: - 35:5e:d4:7c:3e:7e:83:0e:91:96:05:98:c3:b2:1f: - e3:c8:65:eb:a9:7b:5d:a0:2c:cc:fc:3c:d9:6d:ed: - cc:fa:4b:43:8c:c9:d4:b8:a5:61:1c:b2:40:b6:28: - 12:df:b9:f8:5f:fe:d3:b2:c9:ef:3d:b4:1e:4b:7c: - 1c:4c:99:36:9e:3d:eb:ec:a7:68:5e:1d:df:67:6e: - 5e:fb - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Key Usage: critical - Digital Signature, Certificate Sign, CRL Sign - X509v3 Certificate Policies: - Policy: 2.16.840.1.114412.1.3.0.2 - CPS: http://www.digicert.com/ssl-cps-repository.htm - User Notice: - Explicit Text: - - X509v3 Basic Constraints: critical - CA:TRUE, pathlen:0 - Authority Information Access: - OCSP - URI:http://ocsp.digicert.com - - X509v3 CRL Distribution Points: - - Full Name: - URI:http://crl3.digicert.com/DigiCertHighAssuranceEVRootCA.crl - - Full Name: - URI:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl - - X509v3 Authority Key Identifier: - keyid:B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3 - - X509v3 Subject Key Identifier: - 50:EA:73:89:DB:29:FB:10:8F:9E:E5:01:20:D4:DE:79:99:48:83:F7 - Signature Algorithm: sha1WithRSAEncryption - 1e:e2:a5:48:9e:6c:db:53:38:0f:ef:a6:1a:2a:ac:e2:03:43: - ed:9a:bc:3e:8e:75:1b:f0:fd:2e:22:59:ac:13:c0:61:e2:e7: - fa:e9:99:cd:87:09:75:54:28:bf:46:60:dc:be:51:2c:92:f3: - 1b:91:7c:31:08:70:e2:37:b9:c1:5b:a8:bd:a3:0b:00:fb:1a: - 15:fd:03:ad:58:6a:c5:c7:24:99:48:47:46:31:1e:92:ef:b4: - 5f:4e:34:c7:90:bf:31:c1:f8:b1:84:86:d0:9c:01:aa:df:8a: - 56:06:ce:3a:e9:0e:ae:97:74:5d:d7:71:9a:42:74:5f:de:8d: - 43:7c:de:e9:55:ed:69:00:cb:05:e0:7a:61:61:33:d1:19:4d: - f9:08:ee:a0:39:c5:25:35:b7:2b:c4:0f:b2:dd:f1:a5:b7:0e: - 24:c4:26:28:8d:79:77:f5:2f:f0:57:ba:7c:07:d4:e1:fc:cd: - 5a:30:57:7e:86:10:47:dd:31:1f:d7:fc:a2:c2:bf:30:7c:5d: - 24:aa:e8:f9:ae:5f:6a:74:c2:ce:6b:b3:46:d8:21:be:29:d4: - 8e:5e:15:d6:42:4a:e7:32:6f:a4:b1:6b:51:83:58:be:3f:6d: - c7:fb:da:03:21:cb:6a:16:19:4e:0a:f0:ad:84:ca:5d:94:b3: - 5a:76:f7:61 ------BEGIN CERTIFICATE----- -MIIGWDCCBUCgAwIBAgIQCl8RTQNbF5EX0u/UA4w/OzANBgkqhkiG9w0BAQUFADBs -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBFViBSb290IENBMB4XDTA4MDQwMjEyMDAwMFoXDTIyMDQwMzAwMDAwMFowZjEL -MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 -LmRpZ2ljZXJ0LmNvbTElMCMGA1UEAxMcRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug -Q0EtMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9hCikQH17+NDdR -CPge+yLtYb4LDXBMUGMmdRW5QYiXtvCgFbsIYOBC6AUpEIc2iihlqO8xB3RtNpcv -KEZmBMcqeSZ6mdWOw21PoF6tvD2Rwll7XjZswFPPAAgyPhBkWBATaccM7pxCUQD5 -BUTuJM56H+2MEb0SqPMV9Bx6MWkBG6fmXcCabH4JnudSREoQOiPkm7YDr6ictFuf -1EutkozOtREqqjcYjbTCuNhcBoz4/yO9NV7UfD5+gw6RlgWYw7If48hl66l7XaAs -zPw82W3tzPpLQ4zJ1LilYRyyQLYoEt+5+F/+07LJ7z20Hkt8HEyZNp496+ynaF4d -32duXvsCAwEAAaOCAvowggL2MA4GA1UdDwEB/wQEAwIBhjCCAcYGA1UdIASCAb0w -ggG5MIIBtQYLYIZIAYb9bAEDAAIwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3 -LmRpZ2ljZXJ0LmNvbS9zc2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUH -AgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQBy -AHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBj -AGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAg -AEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQ -AGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBt -AGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBj -AG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBl -AHIAZQBuAGMAZQAuMBIGA1UdEwEB/wQIMAYBAf8CAQAwNAYIKwYBBQUHAQEEKDAm -MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wgY8GA1UdHwSB -hzCBhDBAoD6gPIY6aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0SGln -aEFzc3VyYW5jZUVWUm9vdENBLmNybDBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNl -cnQuY29tL0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDAfBgNVHSME -GDAWgBSxPsNpA/i/RwHUmCYaCALvY2QrwzAdBgNVHQ4EFgQUUOpzidsp+xCPnuUB -INTeeZlIg/cwDQYJKoZIhvcNAQEFBQADggEBAB7ipUiebNtTOA/vphoqrOIDQ+2a -vD6OdRvw/S4iWawTwGHi5/rpmc2HCXVUKL9GYNy+USyS8xuRfDEIcOI3ucFbqL2j -CwD7GhX9A61YasXHJJlIR0YxHpLvtF9ONMeQvzHB+LGEhtCcAarfilYGzjrpDq6X -dF3XcZpCdF/ejUN83ulV7WkAywXgemFhM9EZTfkI7qA5xSU1tyvED7Ld8aW3DiTE -JiiNeXf1L/BXunwH1OH8zVowV36GEEfdMR/X/KLCvzB8XSSq6PmuX2p0ws5rs0bY -Ib4p1I5eFdZCSucyb6Sxa1GDWL4/bcf72gMhy2oWGU4K8K2Eyl2Us1p292E= ------END CERTIFICATE----- \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/d867a7b2aecc46f9c31afc4f2f50de05 b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/d867a7b2aecc46f9c31afc4f2f50de05 deleted file mode 100644 index 4ebe436..0000000 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/d867a7b2aecc46f9c31afc4f2f50de05 +++ /dev/null @@ -1,197 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - 0a:5f:11:4d:03:5b:17:91:17:d2:ef:d4:03:8c:3f:3b - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA - Validity - Not Before: Apr 2 12:00:00 2008 GMT - Not After : Apr 3 00:00:00 2022 GMT - Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance CA-3 - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (2048 bit) - Modulus (2048 bit): - 00:bf:61:0a:29:10:1f:5e:fe:34:37:51:08:f8:1e: - fb:22:ed:61:be:0b:0d:70:4c:50:63:26:75:15:b9: - 41:88:97:b6:f0:a0:15:bb:08:60:e0:42:e8:05:29: - 10:87:36:8a:28:65:a8:ef:31:07:74:6d:36:97:2f: - 28:46:66:04:c7:2a:79:26:7a:99:d5:8e:c3:6d:4f: - a0:5e:ad:bc:3d:91:c2:59:7b:5e:36:6c:c0:53:cf: - 00:08:32:3e:10:64:58:10:13:69:c7:0c:ee:9c:42: - 51:00:f9:05:44:ee:24:ce:7a:1f:ed:8c:11:bd:12: - a8:f3:15:f4:1c:7a:31:69:01:1b:a7:e6:5d:c0:9a: - 6c:7e:09:9e:e7:52:44:4a:10:3a:23:e4:9b:b6:03: - af:a8:9c:b4:5b:9f:d4:4b:ad:92:8c:ce:b5:11:2a: - aa:37:18:8d:b4:c2:b8:d8:5c:06:8c:f8:ff:23:bd: - 35:5e:d4:7c:3e:7e:83:0e:91:96:05:98:c3:b2:1f: - e3:c8:65:eb:a9:7b:5d:a0:2c:cc:fc:3c:d9:6d:ed: - cc:fa:4b:43:8c:c9:d4:b8:a5:61:1c:b2:40:b6:28: - 12:df:b9:f8:5f:fe:d3:b2:c9:ef:3d:b4:1e:4b:7c: - 1c:4c:99:36:9e:3d:eb:ec:a7:68:5e:1d:df:67:6e: - 5e:fb - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Key Usage: critical - Digital Signature, Certificate Sign, CRL Sign - X509v3 Certificate Policies: - Policy: 2.16.840.1.114412.1.3.0.2 - CPS: http://www.digicert.com/ssl-cps-repository.htm - User Notice: - Explicit Text: - - X509v3 Basic Constraints: critical - CA:TRUE, pathlen:0 - Authority Information Access: - OCSP - URI:http://ocsp.digicert.com - - X509v3 CRL Distribution Points: - URI:http://crl3.digicert.com/DigiCertHighAssuranceEVRootCA.crl - URI:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl - - X509v3 Authority Key Identifier: - keyid:B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3 - - X509v3 Subject Key Identifier: - 50:EA:73:89:DB:29:FB:10:8F:9E:E5:01:20:D4:DE:79:99:48:83:F7 - Signature Algorithm: sha1WithRSAEncryption - 1e:e2:a5:48:9e:6c:db:53:38:0f:ef:a6:1a:2a:ac:e2:03:43: - ed:9a:bc:3e:8e:75:1b:f0:fd:2e:22:59:ac:13:c0:61:e2:e7: - fa:e9:99:cd:87:09:75:54:28:bf:46:60:dc:be:51:2c:92:f3: - 1b:91:7c:31:08:70:e2:37:b9:c1:5b:a8:bd:a3:0b:00:fb:1a: - 15:fd:03:ad:58:6a:c5:c7:24:99:48:47:46:31:1e:92:ef:b4: - 5f:4e:34:c7:90:bf:31:c1:f8:b1:84:86:d0:9c:01:aa:df:8a: - 56:06:ce:3a:e9:0e:ae:97:74:5d:d7:71:9a:42:74:5f:de:8d: - 43:7c:de:e9:55:ed:69:00:cb:05:e0:7a:61:61:33:d1:19:4d: - f9:08:ee:a0:39:c5:25:35:b7:2b:c4:0f:b2:dd:f1:a5:b7:0e: - 24:c4:26:28:8d:79:77:f5:2f:f0:57:ba:7c:07:d4:e1:fc:cd: - 5a:30:57:7e:86:10:47:dd:31:1f:d7:fc:a2:c2:bf:30:7c:5d: - 24:aa:e8:f9:ae:5f:6a:74:c2:ce:6b:b3:46:d8:21:be:29:d4: - 8e:5e:15:d6:42:4a:e7:32:6f:a4:b1:6b:51:83:58:be:3f:6d: - c7:fb:da:03:21:cb:6a:16:19:4e:0a:f0:ad:84:ca:5d:94:b3: - 5a:76:f7:61 ------BEGIN CERTIFICATE----- -MIIGWDCCBUCgAwIBAgIQCl8RTQNbF5EX0u/UA4w/OzANBgkqhkiG9w0BAQUFADBs -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBFViBSb290IENBMB4XDTA4MDQwMjEyMDAwMFoXDTIyMDQwMzAwMDAwMFowZjEL -MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 -LmRpZ2ljZXJ0LmNvbTElMCMGA1UEAxMcRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug -Q0EtMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9hCikQH17+NDdR -CPge+yLtYb4LDXBMUGMmdRW5QYiXtvCgFbsIYOBC6AUpEIc2iihlqO8xB3RtNpcv -KEZmBMcqeSZ6mdWOw21PoF6tvD2Rwll7XjZswFPPAAgyPhBkWBATaccM7pxCUQD5 -BUTuJM56H+2MEb0SqPMV9Bx6MWkBG6fmXcCabH4JnudSREoQOiPkm7YDr6ictFuf -1EutkozOtREqqjcYjbTCuNhcBoz4/yO9NV7UfD5+gw6RlgWYw7If48hl66l7XaAs -zPw82W3tzPpLQ4zJ1LilYRyyQLYoEt+5+F/+07LJ7z20Hkt8HEyZNp496+ynaF4d -32duXvsCAwEAAaOCAvowggL2MA4GA1UdDwEB/wQEAwIBhjCCAcYGA1UdIASCAb0w -ggG5MIIBtQYLYIZIAYb9bAEDAAIwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3 -LmRpZ2ljZXJ0LmNvbS9zc2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUH -AgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQBy -AHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBj -AGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAg -AEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQ -AGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBt -AGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBj -AG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBl -AHIAZQBuAGMAZQAuMBIGA1UdEwEB/wQIMAYBAf8CAQAwNAYIKwYBBQUHAQEEKDAm -MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wgY8GA1UdHwSB -hzCBhDBAoD6gPIY6aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0SGln -aEFzc3VyYW5jZUVWUm9vdENBLmNybDBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNl -cnQuY29tL0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDAfBgNVHSME -GDAWgBSxPsNpA/i/RwHUmCYaCALvY2QrwzAdBgNVHQ4EFgQUUOpzidsp+xCPnuUB -INTeeZlIg/cwDQYJKoZIhvcNAQEFBQADggEBAB7ipUiebNtTOA/vphoqrOIDQ+2a -vD6OdRvw/S4iWawTwGHi5/rpmc2HCXVUKL9GYNy+USyS8xuRfDEIcOI3ucFbqL2j -CwD7GhX9A61YasXHJJlIR0YxHpLvtF9ONMeQvzHB+LGEhtCcAarfilYGzjrpDq6X -dF3XcZpCdF/ejUN83ulV7WkAywXgemFhM9EZTfkI7qA5xSU1tyvED7Ld8aW3DiTE -JiiNeXf1L/BXunwH1OH8zVowV36GEEfdMR/X/KLCvzB8XSSq6PmuX2p0ws5rs0bY -Ib4p1I5eFdZCSucyb6Sxa1GDWL4/bcf72gMhy2oWGU4K8K2Eyl2Us1p292E= ------END CERTIFICATE----- -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 1116160165 (0x428740a5) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=Entrust.net, OU=www.entrust.net/CPS incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Secure Server Certification Authority - Validity - Not Before: Oct 1 05:00:00 2006 GMT - Not After : Jul 26 18:15:15 2014 GMT - Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (2048 bit) - Modulus (2048 bit): - 00:c6:cc:e5:73:e6:fb:d4:bb:e5:2d:2d:32:a6:df: - e5:81:3f:c9:cd:25:49:b6:71:2a:c3:d5:94:34:67: - a2:0a:1c:b0:5f:69:a6:40:b1:c4:b7:b2:8f:d0:98: - a4:a9:41:59:3a:d3:dc:94:d6:3c:db:74:38:a4:4a: - cc:4d:25:82:f7:4a:a5:53:12:38:ee:f3:49:6d:71: - 91:7e:63:b6:ab:a6:5f:c3:a4:84:f8:4f:62:51:be: - f8:c5:ec:db:38:92:e3:06:e5:08:91:0c:c4:28:41: - 55:fb:cb:5a:89:15:7e:71:e8:35:bf:4d:72:09:3d: - be:3a:38:50:5b:77:31:1b:8d:b3:c7:24:45:9a:a7: - ac:6d:00:14:5a:04:b7:ba:13:eb:51:0a:98:41:41: - 22:4e:65:61:87:81:41:50:a6:79:5c:89:de:19:4a: - 57:d5:2e:e6:5d:1c:53:2c:7e:98:cd:1a:06:16:a4: - 68:73:d0:34:04:13:5c:a1:71:d3:5a:7c:55:db:5e: - 64:e1:37:87:30:56:04:e5:11:b4:29:80:12:f1:79: - 39:88:a2:02:11:7c:27:66:b7:88:b7:78:f2:ca:0a: - a8:38:ab:0a:64:c2:bf:66:5d:95:84:c1:a1:25:1e: - 87:5d:1a:50:0b:20:12:cc:41:bb:6e:0b:51:38:b8: - 4b:cb - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: critical - CA:TRUE, pathlen:1 - X509v3 Extended Key Usage: - TLS Web Server Authentication, TLS Web Client Authentication, E-mail Protection - Authority Information Access: - OCSP - URI:http://ocsp.entrust.net - - X509v3 CRL Distribution Points: - URI:http://crl.entrust.net/server1.crl - - X509v3 Subject Key Identifier: - B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3 - X509v3 Key Usage: - Certificate Sign, CRL Sign - X509v3 Authority Key Identifier: - keyid:F0:17:62:13:55:3D:B3:FF:0A:00:6B:FB:50:84:97:F3:ED:62:D0:1A - - 1.2.840.113533.7.65.0: - 0 -..V7.1.... - Signature Algorithm: sha1WithRSAEncryption - 48:0e:2b:6f:20:62:4c:28:93:a3:24:3d:58:ab:21:cf:80:f8: - 9a:97:90:6a:22:ed:5a:7c:47:36:99:e7:79:84:75:ab:24:8f: - 92:0a:d5:61:04:ae:c3:6a:5c:b2:cc:d9:e4:44:87:6f:db:8f: - 38:62:f7:44:36:9d:ba:bc:6e:07:c4:d4:8d:e8:1f:d1:0b:60: - a3:b5:9c:ce:63:be:ed:67:dc:f8:ba:de:6e:c9:25:cb:5b:b5: - 9d:76:70:0b:df:42:72:f8:4f:41:11:64:a5:d2:ea:fc:d5:af: - 11:f4:15:38:67:9c:20:a8:4b:77:5a:91:32:42:32:e7:85:b3: - df:36 ------BEGIN CERTIFICATE----- -MIIEQjCCA6ugAwIBAgIEQodApTANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC -VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u -ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc -KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u -ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEw -MDEwNTAwMDBaFw0xNDA3MjYxODE1MTVaMGwxCzAJBgNVBAYTAlVTMRUwEwYDVQQK -EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xKzApBgNV -BAMTIkRpZ2lDZXJ0IEhpZ2ggQXNzdXJhbmNlIEVWIFJvb3QgQ0EwggEiMA0GCSqG -SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGzOVz5vvUu+UtLTKm3+WBP8nNJUm2cSrD -1ZQ0Z6IKHLBfaaZAscS3so/QmKSpQVk609yU1jzbdDikSsxNJYL3SqVTEjju80lt -cZF+Y7arpl/DpIT4T2JRvvjF7Ns4kuMG5QiRDMQoQVX7y1qJFX5x6DW/TXIJPb46 -OFBbdzEbjbPHJEWap6xtABRaBLe6E+tRCphBQSJOZWGHgUFQpnlcid4ZSlfVLuZd -HFMsfpjNGgYWpGhz0DQEE1yhcdNafFXbXmThN4cwVgTlEbQpgBLxeTmIogIRfCdm -t4i3ePLKCqg4qwpkwr9mXZWEwaElHoddGlALIBLMQbtuC1E4uEvLAgMBAAGjggET -MIIBDzASBgNVHRMBAf8ECDAGAQH/AgEBMCcGA1UdJQQgMB4GCCsGAQUFBwMBBggr -BgEFBQcDAgYIKwYBBQUHAwQwMwYIKwYBBQUHAQEEJzAlMCMGCCsGAQUFBzABhhdo -dHRwOi8vb2NzcC5lbnRydXN0Lm5ldDAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8v -Y3JsLmVudHJ1c3QubmV0L3NlcnZlcjEuY3JsMB0GA1UdDgQWBBSxPsNpA/i/RwHU -mCYaCALvY2QrwzALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7 -UISX8+1i0BowGQYJKoZIhvZ9B0EABAwwChsEVjcuMQMCAIEwDQYJKoZIhvcNAQEF -BQADgYEASA4rbyBiTCiToyQ9WKshz4D4mpeQaiLtWnxHNpnneYR1qySPkgrVYQSu -w2pcsszZ5ESHb9uPOGL3RDadurxuB8TUjegf0Qtgo7WczmO+7Wfc+Lrebskly1u1 -nXZwC99CcvhPQRFkpdLq/NWvEfQVOGecIKhLd1qRMkIy54Wz3zY= ------END CERTIFICATE----- \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/example-channel.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/example-channel.json new file mode 100644 index 0000000..75aeac3 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/example-channel.json @@ -0,0 +1,64 @@ +{ + "schema_version": "2.0", + + // All repositories must be an HTTP or HTTPS URL. HTTPS is vastly superior + // since verification of the source server is performed on SSL certificates. + "repositories": [ + "http://sublime.wbond.net/packages.json", + "https://github.com/buymeasoda/soda-theme", + "https://github.com/SublimeText" + ], + + // The "packages_cache" is completely optional, but allows the + // channel to cache and deliver package data from multiple + // repositories in a single HTTP request, allowing for significantly + // improved performance. + "packages_cache": { + + // The first level keys are the repository URLs + "http://sublime.wbond.net/packages.json": [ + + // Each repository has an array of packages with their fully + // expanded info. This means that the "details" key must be expanded + // into the various keys it provides. + { + "name": "Alignment", + "description": "Multi-line and multiple selection alignment plugin", + "author": "wbond", + "homepage": "http://wbond.net/sublime_packages/alignment", + "releases": [ + { + "version": "2.0.0", + "url": "https://sublime.wbond.net/Alignment.sublime-package", + "date": "2011-09-18 20:12:41" + } + ] + } + ] + }, + + // Package Control ships with the SSL Certificate Authority (CA) cert for the + // SSL certificate that secures and identifies sublime.wbond.net. After this + // initial connection is made, the channel server provides a list of CA certs + // for the various URLs that Package Control need to connect to. This way the + // default channel (https://sublime.wbond.net/channel.json) can provide + // real-time updates to CA certs in the case that a CA is compromised. The + // CA certs are extracted from openssl, and the server runs on an LTS version + // of Ubuntu, which automatically applies security patches from the official + // Ubuntu repositories. This architecture helps to ensure that the packages + // being downloaded are from the source listed and that users are very + // unlikely to be the subject of the man-in-the-middle attack. + "certs": { + + // All certs have the domain they apply to as the key + "sublime.wbond.net": [ + // The value is an array of two elements, the first being an md5 + // hash of the contents of the certificate. This helps in detecting + // CA cert changes. The second element is the URL where the cert + // can be downloaded, if it is not already installed on the user’s + // copy of Sublime Text. + "7f4f8622b4fd001c7f648e09aae7edaa", + "https://sublime.wbond.net/certs/7f4f8622b4fd001c7f648e09aae7edaa" + ] + } +} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/example-packages.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/example-packages.json deleted file mode 100644 index 96945cc..0000000 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/example-packages.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "schema_version": "1.2", - "packages": [ - { - "name": "GitHub Example", - "description": "An example from GitHub, be sure to use the zipball URL", - "author": "John Smith", - "homepage": "http://example.com", - "last_modified": "2011-12-12 05:04:31", - "platforms": { - "*": [ - { - "version": "1.1", - "url": "http://nodeload.github.com/john_smith/github_example/zipball/master" - } - ] - } - }, - { - "name": "BitBucket Example", - "description": "An example from BitBucket, be sure to use the zip URL", - "author": "John Smith", - "homepage": "http://example.com", - "last_modified": "2011-08-12 12:21:09", - "platforms": { - "*": [ - { - "version": "1.0", - "url": "https://bitbucket.org/john_smith/bitbucket_example/get/tip.zip" - } - ] - } - }, - { - "name": "Tortoise", - "description": "Keyboard shortcuts and menu entries to execute TortoiseSVN, TortoiseHg and TortoiseGit commands", - "author": "Will Bond", - "homepage": "http://sublime.wbond.net", - "last_modified": "2011-11-30 22:55:52", - "platforms": { - "windows": [ - { - "version": "1.0", - "url": "http://sublime.wbond.net/Tortoise.sublime-package" - } - ] - } - } - ], - "renamed_packages": { - "sublime-old-package": "NewPackage", - "OldPackage": "NewName" - } -} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/example-repositories.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/example-repositories.json deleted file mode 100644 index 9e03b1e..0000000 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/example-repositories.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "schema_version": "1.2", - "repositories": [ - "http://sublime.wbond.net/packages.json", - "https://github.com/buymeasoda/soda-theme", - "https://github.com/SublimeText" - ], - "package_name_map": { - "soda-theme": "Theme - Soda" - }, - "renamed_packages": { - "old-name": "New Name" - }, - "packages": { - "http://sublime.wbond.net/packages.json": [ - { - "name": "GitHub Example", - "description": "An example from GitHub, be sure to use the zipball URL", - "author": "John Smith", - "homepage": "http://example.com", - "platforms": { - "*": [ - { - "version": "1.1", - "url": "http://nodeload.github.com/john_smith/github_example/zipball/master" - } - ] - } - }, - { - "name": "BitBucket Example", - "description": "An example from BitBucket, be sure to use the zip URL", - "author": "John Smith", - "homepage": "http://example.com", - "platforms": { - "*": [ - { - "version": "1.0", - "url": "https://bitbucket.org/john_smith/bitbucket_example/get/tip.zip" - } - ] - } - }, - { - "name": "Tortoise", - "description": "Keyboard shortcuts and menu entries to execute TortoiseSVN, TortoiseHg and TortoiseGit commands", - "author": "Will Bond", - "homepage": "http://sublime.wbond.net", - "platforms": { - "windows": [ - { - "version": "1.0", - "url": "http://sublime.wbond.net/Tortoise.sublime-package" - } - ] - } - } - ] - } -} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/example-repository.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/example-repository.json new file mode 100644 index 0000000..39fe43d --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/example-repository.json @@ -0,0 +1,275 @@ +{ + "schema_version": "2.0", + + // Packages can be specified with a simple URL to a GitHub or BitBucket + // repository, but details can be overridden for every field. It is + // also possible not utilize GitHub or BitBucket at all, but just to + // host your packages on any server with an SSL certificate. + "packages": [ + + // This is what most packages should aim to model. + // + // The majority of the information about a package ("name", + // "description", "author") are all pulled from the GitHub (or + // BitBucket) repository info. + // + // If the word "sublime" exists in the repository name, the name + // can be overridden by the "name" key. + // + // A release is created from the the tag that is the highest semantic + // versioning version number in the list of tags. + { + "name": "Alignment", + "details": "https://github.com/wbond/sublime_alignment", + "releases": [ + { + "details": "https://github.com/wbond/sublime_alignment/tags" + } + ] + }, + + // Here is an equivalent package being pulled from BitBucket + { + "name": "Alignment", + "details": "https://bitbucket.org/wbond/sublime_alignment", + "releases": [ + { + "details": "https://bitbucket.org/wbond/sublime_alignment#tags" + } + ] + }, + + // Pull most details from GitHub, releases from master branch. + // This form is discouraged because users will upgrade to every single + // commit you make to master. + { + "details": "https://github.com/wbond/sublime_alignment" + }, + + // Pull most details from a BitBucket repository and releases from + // the branch "default" or "master", depending on how your repository + // is configured. + // Similar to the above example, this form is discouraged because users + // will upgrade to every single commit you make to master. + { + "details": "https://bitbucket.org/wbond/sublime_alignment" + }, + + // Use a custom name instead of just the URL slug + { + "name": "Alignment", + "details": "https://github.com/wbond/sublime_alignment" + }, + + // You can also override the homepage and author + { + "name": "Alignment", + "details": "https://github.com/wbond/sublime_alignment", + "homepage": "http://wbond.net/sublime_packages/alignment", + "author": "wbond" + }, + + // It is possible to provide the URL to a readme file. This URL + // should be to the raw source of the file, not rendered HTML. + // GitHub and BitBucket repositories will automatically provide + // these. + // + // The following extensions will be rendered: + // + // .markdown, .mdown, .mkd, .md + // .texttile + // .creole + // .rst + // + // All others are treated as plaintext. + { + "details": "https://github.com/wbond/sublime_alignment", + "readme": "https://raw.github.com/wbond/sublime_alignment/master/readme.creole" + }, + + // If a package has a public bug tracker, the URL should be + // included via the "issues" key. Both GitHub and BitBucket + // repositories will automatically provide this if they have + // issues enabled. + { + "details": "https://github.com/wbond/sublime_alignment", + "issues": "https://github.com/wbond/sublime_alignment/issues" + }, + + // The URL to donate to support the development of a package. + // GitHub and BitBucket repositories will default to: + // + // https://www.gittip.com/{username}/ + // + // Other URLs with special integration include: + // + // https://flattr.com/profile/{username} + // https://www.dwolla.com/hub/{username} + // + // This may also contain a URL to another other donation-type site + // where users may support the author for their development of the + // package. + { + "details": "https://github.com/wbond/sublime_alignment", + "donate": "https://www.gittip.com/wbond/" + }, + + // The URL to purchase a license to the package + { + "details": "https://github.com/wbond/sublime_alignment", + "buy": "https://wbond.net/sublime_packages/alignment/buy" + }, + + // If you rename a package, you can provide the previous name(s) + // so that users with the old package name can be automatically + // upgraded to the new one. + { + "name": "Alignment", + "details": "https://github.com/wbond/sublime_alignment", + "previous_names": ["sublime_alignment"] + }, + + // Packages can be labelled for the purpose of creating a + // folksonomy so users may more easily find relevant packages. + // Labels should be all lower case and should use spaces instead + // of _ or - to separate words. + // + // Some suggested labels are listed below, however, anything can + // be used as a label: + // + // auto-complete + // browser integration + // build system + // code navigation + // code sharing + // color scheme + // deprecated + // diff/merge + // editor emulation + // file creation + // file navigation + // formatting + // ftp + // language syntax + // linting + // minification + // search + // snippets + // terminal/shell/repl + // testing + // text manipulation + // text navigation + // theme + // todo + // vcs + { + "details": "https://github.com/wbond/sublime_alignment", + "labels": ["text manipulation", "formatting"] + }, + + // In addition to the recommendation above of pulling releases + // from tags that are semantic version numbers, releases can also + // comefrom a custom branch. + { + "details": "https://github.com/wbond/sublime_alignment", + "releases": [ + { + "details": "https://github.com/wbond/sublime_alignment/tree/custom_branch" + } + ] + }, + + // An equivalent package being pulled from BitBucket. + { + "details": "https://bitbucket.org/wbond/sublime_alignment", + "releases": [ + { + "details": "https://bitbucket.org/wbond/sublime_alignment/src/custom_branch" + } + ] + }, + + // If your package is only compatible with specific builds of + // Sublime Text, this will cause the package to be hidden from + // users with incompatible versions. + { + "details": "https://github.com/wbond/sublime_alignment", + "releases": [ + { + // Could also be >2999 for ST3. Leaving this out indicates + // the package works with both ST2 and ST3. + "sublime_text": "<3000", + "details": "https://github.com/wbond/sublime_alignment" + } + ] + }, + + // The "platforms" key allows specifying what platform(s) the release + // is valid for. As shown, there can be multiple releases of a package + // at any given time. However, only the latest version for any given + // platform/arch will be shown to the user. + // + // The "platforms" key allows specifying a single platform, or a list + // of platforms. Valid platform indentifiers include: + // + // "*" + // "windows", "windows-x64", "windows-x32" + // "osx", "osx-x64" + // "linux", "linux-x32", "linux-x64" + { + "details": "https://github.com/wbond/sublime_alignment", + "releases": [ + { + // Defaults to "*", or all platforms. + "platforms": ["osx", "linux"], + "details": "https://github.com/wbond/sublime_alignment/tree/posix" + }, + { + "platforms": "windows", + "details": "https://github.com/wbond/sublime_alignment/tree/win32" + } + ] + }, + + // If you don't use a "details" key for a "releases" entry, you need to + // specify the "version", "url" and "date" manually. + { + "details": "https://github.com/wbond/sublime_alignment", + "releases": [ + { + // The version number needs to be a semantic version number per + // http://semver.org 2.x.x + "version": "2.0.0", + + // The URL needs to be a zip file containing the package. It is permissible + // for the zip file to contain a single root folder with any name. All + // file will be extracted out of this single root folder. This allows + // zip files from GitHub and BitBucket to be used a sources. + "url": "https://codeload.github.com/wbond/sublime_alignment/zip/v2.0.0", + + // The date MUST be in the form "YYYY-MM-DD HH:MM:SS" and SHOULD be UTC + "date": "2011-09-18 20:12:41" + } + ] + } + ], + + // If you need/want to split your repository up into multiple smaller files + // for the sake of organization, the "includes" key allows you to enter + // URL paths that will be combined together and dynamically inserted + // into the "packages" key. These URLs these can be relative or absolute. + "includes": [ + + // Here is an example of how relative paths work for URLs. If this file + // was loaded from: + // "https://sublime.wbond.net/example-repository.json" + // then the following files would be loaded from: + // "https://sublime.wbond.net/repository/0-9.json" + // "https://sublime.wbond.net/repository/a.json" + "./repository/0-9.json", + "./repository/a.json", + + // An example of an absolute URL + "https://sublime.wbond.net/repository/b.json" + ] +} diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/all/semver.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/all/semver.py deleted file mode 100644 index 73d4ea2..0000000 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/all/semver.py +++ /dev/null @@ -1,86 +0,0 @@ -# -*- coding: utf-8 -*- -# This code is copyright Konstantine Rybnikov , and is -# available at https://github.com/k-bx/python-semver and is licensed under the -# BSD License - -import re - -_REGEX = re.compile('^(?P[0-9]+)' - '\.(?P[0-9]+)' - '\.(?P[0-9]+)' - '(\-(?P[0-9A-Za-z]+(\.[0-9A-Za-z]+)*))?' - '(\+(?P[0-9A-Za-z]+(\.[0-9A-Za-z]+)*))?$') - -if 'cmp' not in __builtins__: - cmp = lambda a,b: (a > b) - (a < b) - -def parse(version): - """ - Parse version to major, minor, patch, pre-release, build parts. - """ - match = _REGEX.match(version) - if match is None: - raise ValueError('%s is not valid SemVer string' % version) - - verinfo = match.groupdict() - - verinfo['major'] = int(verinfo['major']) - verinfo['minor'] = int(verinfo['minor']) - verinfo['patch'] = int(verinfo['patch']) - - return verinfo - - -def compare(ver1, ver2): - def nat_cmp(a, b): - a, b = a or '', b or '' - convert = lambda text: text.isdigit() and int(text) or text.lower() - alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)] - return cmp(alphanum_key(a), alphanum_key(b)) - - def compare_by_keys(d1, d2): - for key in ['major', 'minor', 'patch']: - v = cmp(d1.get(key), d2.get(key)) - if v: - return v - rc1, rc2 = d1.get('prerelease'), d2.get('prerelease') - build1, build2 = d1.get('build'), d2.get('build') - rccmp = nat_cmp(rc1, rc2) - buildcmp = nat_cmp(build1, build2) - if not (rc1 or rc2): - return buildcmp - elif not rc1: - return 1 - elif not rc2: - return -1 - return rccmp or buildcmp or 0 - - v1, v2 = parse(ver1), parse(ver2) - - return compare_by_keys(v1, v2) - - -def match(version, match_expr): - prefix = match_expr[:2] - if prefix in ('>=', '<=', '=='): - match_version = match_expr[2:] - elif prefix and prefix[0] in ('>', '<', '='): - prefix = prefix[0] - match_version = match_expr[1:] - else: - raise ValueError("match_expr parameter should be in format , " - "where is one of ['<', '>', '==', '<=', '>=']. " - "You provided: %r" % match_expr) - - possibilities_dict = { - '>': (1,), - '<': (-1,), - '==': (0,), - '>=': (0, 1), - '<=': (-1, 0) - } - - possibilities = possibilities_dict[prefix] - cmp_res = compare(version, match_version) - - return cmp_res in possibilities \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/U32.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/U32.py deleted file mode 100644 index 7b3e4c8..0000000 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/U32.py +++ /dev/null @@ -1,113 +0,0 @@ -# This file is part of 'NTLM Authorization Proxy Server' http://sourceforge.net/projects/ntlmaps/ -# Copyright 2001 Dmitry A. Rozmanov -# -# This library is free software: you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation, either -# version 3 of the License, or (at your option) any later version. - -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library. If not, see or . - - -C = 0x1000000000L - -def norm(n): - return n & 0xFFFFFFFFL - - -class U32: - v = 0L - - def __init__(self, value = 0): - self.v = C + norm(abs(long(value))) - - def set(self, value = 0): - self.v = C + norm(abs(long(value))) - - def __repr__(self): - return hex(norm(self.v)) - - def __long__(self): return long(norm(self.v)) - def __int__(self): return int(norm(self.v)) - def __chr__(self): return chr(norm(self.v)) - - def __add__(self, b): - r = U32() - r.v = C + norm(self.v + b.v) - return r - - def __sub__(self, b): - r = U32() - if self.v < b.v: - r.v = C + norm(0x100000000L - (b.v - self.v)) - else: r.v = C + norm(self.v - b.v) - return r - - def __mul__(self, b): - r = U32() - r.v = C + norm(self.v * b.v) - return r - - def __div__(self, b): - r = U32() - r.v = C + (norm(self.v) / norm(b.v)) - return r - - def __mod__(self, b): - r = U32() - r.v = C + (norm(self.v) % norm(b.v)) - return r - - def __neg__(self): return U32(self.v) - def __pos__(self): return U32(self.v) - def __abs__(self): return U32(self.v) - - def __invert__(self): - r = U32() - r.v = C + norm(~self.v) - return r - - def __lshift__(self, b): - r = U32() - r.v = C + norm(self.v << b) - return r - - def __rshift__(self, b): - r = U32() - r.v = C + (norm(self.v) >> b) - return r - - def __and__(self, b): - r = U32() - r.v = C + norm(self.v & b.v) - return r - - def __or__(self, b): - r = U32() - r.v = C + norm(self.v | b.v) - return r - - def __xor__(self, b): - r = U32() - r.v = C + norm(self.v ^ b.v) - return r - - def __not__(self): - return U32(not norm(self.v)) - - def truth(self): - return norm(self.v) - - def __cmp__(self, b): - if norm(self.v) > norm(b.v): return 1 - elif norm(self.v) < norm(b.v): return -1 - else: return 0 - - def __nonzero__(self): - return norm(self.v) \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/des.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/des.py deleted file mode 100644 index 6a59964..0000000 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/des.py +++ /dev/null @@ -1,92 +0,0 @@ -# This file is part of 'NTLM Authorization Proxy Server' http://sourceforge.net/projects/ntlmaps/ -# Copyright 2001 Dmitry A. Rozmanov -# -# This library is free software: you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation, either -# version 3 of the License, or (at your option) any later version. - -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library. If not, see or . - -import des_c - -#--------------------------------------------------------------------- -class DES: - - des_c_obj = None - - #----------------------------------------------------------------- - def __init__(self, key_str): - "" - k = str_to_key56(key_str) - k = key56_to_key64(k) - key_str = '' - for i in k: - key_str += chr(i & 0xFF) - self.des_c_obj = des_c.DES(key_str) - - #----------------------------------------------------------------- - def encrypt(self, plain_text): - "" - return self.des_c_obj.encrypt(plain_text) - - #----------------------------------------------------------------- - def decrypt(self, crypted_text): - "" - return self.des_c_obj.decrypt(crypted_text) - -#--------------------------------------------------------------------- -#Some Helpers -#--------------------------------------------------------------------- - -DESException = 'DESException' - -#--------------------------------------------------------------------- -def str_to_key56(key_str): - "" - if type(key_str) != type(''): - #rise DESException, 'ERROR. Wrong key type.' - pass - if len(key_str) < 7: - key_str = key_str + '\000\000\000\000\000\000\000'[:(7 - len(key_str))] - key_56 = [] - for i in key_str[:7]: key_56.append(ord(i)) - - return key_56 - -#--------------------------------------------------------------------- -def key56_to_key64(key_56): - "" - key = [] - for i in range(8): key.append(0) - - key[0] = key_56[0]; - key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1); - key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2); - key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3); - key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4); - key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5); - key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6); - key[7] = (key_56[6] << 1) & 0xFF; - - key = set_key_odd_parity(key) - - return key - -#--------------------------------------------------------------------- -def set_key_odd_parity(key): - "" - for i in range(len(key)): - for k in range(7): - bit = 0 - t = key[i] >> k - bit = (t ^ bit) & 0x1 - key[i] = (key[i] & 0xFE) | bit - - return key diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/des_c.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/des_c.py deleted file mode 100644 index 61af403..0000000 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/des_c.py +++ /dev/null @@ -1,328 +0,0 @@ -# This file is part of 'NTLM Authorization Proxy Server' http://sourceforge.net/projects/ntlmaps/ -# Copyright 2001 Dmitry A. Rozmanov -# -# This library is free software: you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation, either -# version 3 of the License, or (at your option) any later version. - -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library. If not, see or . - -from U32 import U32 - -# --NON ASCII COMMENT ELIDED-- -#typedef unsigned char des_cblock[8]; -#define HDRSIZE 4 - -def c2l(c): - "char[4] to unsigned long" - l = U32(c[0]) - l = l | (U32(c[1]) << 8) - l = l | (U32(c[2]) << 16) - l = l | (U32(c[3]) << 24) - return l - -def c2ln(c,l1,l2,n): - "char[n] to two unsigned long???" - c = c + n - l1, l2 = U32(0), U32(0) - - f = 0 - if n == 8: - l2 = l2 | (U32(c[7]) << 24) - f = 1 - if f or (n == 7): - l2 = l2 | (U32(c[6]) << 16) - f = 1 - if f or (n == 6): - l2 = l2 | (U32(c[5]) << 8) - f = 1 - if f or (n == 5): - l2 = l2 | U32(c[4]) - f = 1 - if f or (n == 4): - l1 = l1 | (U32(c[3]) << 24) - f = 1 - if f or (n == 3): - l1 = l1 | (U32(c[2]) << 16) - f = 1 - if f or (n == 2): - l1 = l1 | (U32(c[1]) << 8) - f = 1 - if f or (n == 1): - l1 = l1 | U32(c[0]) - return (l1, l2) - -def l2c(l): - "unsigned long to char[4]" - c = [] - c.append(int(l & U32(0xFF))) - c.append(int((l >> 8) & U32(0xFF))) - c.append(int((l >> 16) & U32(0xFF))) - c.append(int((l >> 24) & U32(0xFF))) - return c - -def n2l(c, l): - "network to host long" - l = U32(c[0] << 24) - l = l | (U32(c[1]) << 16) - l = l | (U32(c[2]) << 8) - l = l | (U32(c[3])) - return l - -def l2n(l, c): - "host to network long" - c = [] - c.append(int((l >> 24) & U32(0xFF))) - c.append(int((l >> 16) & U32(0xFF))) - c.append(int((l >> 8) & U32(0xFF))) - c.append(int((l ) & U32(0xFF))) - return c - -def l2cn(l1, l2, c, n): - "" - for i in range(n): c.append(0x00) - f = 0 - if f or (n == 8): - c[7] = int((l2 >> 24) & U32(0xFF)) - f = 1 - if f or (n == 7): - c[6] = int((l2 >> 16) & U32(0xFF)) - f = 1 - if f or (n == 6): - c[5] = int((l2 >> 8) & U32(0xFF)) - f = 1 - if f or (n == 5): - c[4] = int((l2 ) & U32(0xFF)) - f = 1 - if f or (n == 4): - c[3] = int((l1 >> 24) & U32(0xFF)) - f = 1 - if f or (n == 3): - c[2] = int((l1 >> 16) & U32(0xFF)) - f = 1 - if f or (n == 2): - c[1] = int((l1 >> 8) & U32(0xFF)) - f = 1 - if f or (n == 1): - c[0] = int((l1 ) & U32(0xFF)) - f = 1 - return c[:n] - -# array of data -# static unsigned long des_SPtrans[8][64]={ -# static unsigned long des_skb[8][64]={ -from des_data import des_SPtrans, des_skb - -def D_ENCRYPT(tup, u, t, s): - L, R, S = tup - #print 'LRS1', L, R, S, u, t, '-->', - u = (R ^ s[S]) - t = R ^ s[S + 1] - t = ((t >> 4) + (t << 28)) - L = L ^ (des_SPtrans[1][int((t ) & U32(0x3f))] | \ - des_SPtrans[3][int((t >> 8) & U32(0x3f))] | \ - des_SPtrans[5][int((t >> 16) & U32(0x3f))] | \ - des_SPtrans[7][int((t >> 24) & U32(0x3f))] | \ - des_SPtrans[0][int((u ) & U32(0x3f))] | \ - des_SPtrans[2][int((u >> 8) & U32(0x3f))] | \ - des_SPtrans[4][int((u >> 16) & U32(0x3f))] | \ - des_SPtrans[6][int((u >> 24) & U32(0x3f))]) - #print 'LRS:', L, R, S, u, t - return ((L, R, S), u, t, s) - - -def PERM_OP (tup, n, m): - "tup - (a, b, t)" - a, b, t = tup - t = ((a >> n) ^ b) & m - b = b ^ t - a = a ^ (t << n) - return (a, b, t) - -def HPERM_OP (tup, n, m): - "tup - (a, t)" - a, t = tup - t = ((a << (16 - n)) ^ a) & m - a = a ^ t ^ (t >> (16 - n)) - return (a, t) - -shifts2 = [0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0] - -class DES: - KeySched = None # des_key_schedule - - def __init__(self, key_str): - # key - UChar[8] - key = [] - for i in key_str: key.append(ord(i)) - #print 'key:', key - self.KeySched = des_set_key(key) - #print 'schedule:', self.KeySched, len(self.KeySched) - - def decrypt(self, str): - # block - UChar[] - block = [] - for i in str: block.append(ord(i)) - #print block - block = des_ecb_encrypt(block, self.KeySched, 0) - res = '' - for i in block: res = res + (chr(i)) - return res - - def encrypt(self, str): - # block - UChar[] - block = [] - for i in str: block.append(ord(i)) - block = des_ecb_encrypt(block, self.KeySched, 1) - res = '' - for i in block: res = res + (chr(i)) - return res - - - - - - -#------------------------ -def des_encript(input, ks, encrypt): - # input - U32[] - # output - U32[] - # ks - des_key_shedule - U32[2][16] - # encrypt - int - # l, r, t, u - U32 - # i - int - # s - U32[] - - l = input[0] - r = input[1] - t = U32(0) - u = U32(0) - - r, l, t = PERM_OP((r, l, t), 4, U32(0x0f0f0f0fL)) - l, r, t = PERM_OP((l, r, t), 16, U32(0x0000ffffL)) - r, l, t = PERM_OP((r, l, t), 2, U32(0x33333333L)) - l, r, t = PERM_OP((l, r, t), 8, U32(0x00ff00ffL)) - r, l, t = PERM_OP((r, l, t), 1, U32(0x55555555L)) - - t = (r << 1)|(r >> 31) - r = (l << 1)|(l >> 31) - l = t - - s = ks # ??????????????? - #print l, r - if(encrypt): - for i in range(0, 32, 4): - rtup, u, t, s = D_ENCRYPT((l, r, i + 0), u, t, s) - l = rtup[0] - r = rtup[1] - rtup, u, t, s = D_ENCRYPT((r, l, i + 2), u, t, s) - r = rtup[0] - l = rtup[1] - else: - for i in range(30, 0, -4): - rtup, u, t, s = D_ENCRYPT((l, r, i - 0), u, t, s) - l = rtup[0] - r = rtup[1] - rtup, u, t, s = D_ENCRYPT((r, l, i - 2), u, t, s) - r = rtup[0] - l = rtup[1] - #print l, r - l = (l >> 1)|(l << 31) - r = (r >> 1)|(r << 31) - - r, l, t = PERM_OP((r, l, t), 1, U32(0x55555555L)) - l, r, t = PERM_OP((l, r, t), 8, U32(0x00ff00ffL)) - r, l, t = PERM_OP((r, l, t), 2, U32(0x33333333L)) - l, r, t = PERM_OP((l, r, t), 16, U32(0x0000ffffL)) - r, l, t = PERM_OP((r, l, t), 4, U32(0x0f0f0f0fL)) - - output = [l] - output.append(r) - l, r, t, u = U32(0), U32(0), U32(0), U32(0) - return output - -def des_ecb_encrypt(input, ks, encrypt): - # input - des_cblock - UChar[8] - # output - des_cblock - UChar[8] - # ks - des_key_shedule - U32[2][16] - # encrypt - int - - #print input - l0 = c2l(input[0:4]) - l1 = c2l(input[4:8]) - ll = [l0] - ll.append(l1) - #print ll - ll = des_encript(ll, ks, encrypt) - #print ll - l0 = ll[0] - l1 = ll[1] - output = l2c(l0) - output = output + l2c(l1) - #print output - l0, l1, ll[0], ll[1] = U32(0), U32(0), U32(0), U32(0) - return output - -def des_set_key(key): - # key - des_cblock - UChar[8] - # schedule - des_key_schedule - - # register unsigned long c,d,t,s; - # register unsigned char *in; - # register unsigned long *k; - # register int i; - - #k = schedule - # in = key - - k = [] - c = c2l(key[0:4]) - d = c2l(key[4:8]) - t = U32(0) - - d, c, t = PERM_OP((d, c, t), 4, U32(0x0f0f0f0fL)) - c, t = HPERM_OP((c, t), -2, U32(0xcccc0000L)) - d, t = HPERM_OP((d, t), -2, U32(0xcccc0000L)) - d, c, t = PERM_OP((d, c, t), 1, U32(0x55555555L)) - c, d, t = PERM_OP((c, d, t), 8, U32(0x00ff00ffL)) - d, c, t = PERM_OP((d, c, t), 1, U32(0x55555555L)) - - d = (((d & U32(0x000000ffL)) << 16)|(d & U32(0x0000ff00L))|((d & U32(0x00ff0000L)) >> 16)|((c & U32(0xf0000000L)) >> 4)) - c = c & U32(0x0fffffffL) - - for i in range(16): - if (shifts2[i]): - c = ((c >> 2)|(c << 26)) - d = ((d >> 2)|(d << 26)) - else: - c = ((c >> 1)|(c << 27)) - d = ((d >> 1)|(d << 27)) - c = c & U32(0x0fffffffL) - d = d & U32(0x0fffffffL) - - s= des_skb[0][int((c ) & U32(0x3f))]|\ - des_skb[1][int(((c>> 6) & U32(0x03))|((c>> 7) & U32(0x3c)))]|\ - des_skb[2][int(((c>>13) & U32(0x0f))|((c>>14) & U32(0x30)))]|\ - des_skb[3][int(((c>>20) & U32(0x01))|((c>>21) & U32(0x06)) | ((c>>22) & U32(0x38)))] - - t= des_skb[4][int((d ) & U32(0x3f) )]|\ - des_skb[5][int(((d>> 7) & U32(0x03))|((d>> 8) & U32(0x3c)))]|\ - des_skb[6][int((d>>15) & U32(0x3f) )]|\ - des_skb[7][int(((d>>21) & U32(0x0f))|((d>>22) & U32(0x30)))] - #print s, t - - k.append(((t << 16)|(s & U32(0x0000ffffL))) & U32(0xffffffffL)) - s = ((s >> 16)|(t & U32(0xffff0000L))) - s = (s << 4)|(s >> 28) - k.append(s & U32(0xffffffffL)) - - schedule = k - - return schedule diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/des_data.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/des_data.py deleted file mode 100644 index 9867876..0000000 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/des_data.py +++ /dev/null @@ -1,348 +0,0 @@ -# This file is part of 'NTLM Authorization Proxy Server' http://sourceforge.net/projects/ntlmaps/ -# Copyright 2001 Dmitry A. Rozmanov -# -# This library is free software: you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation, either -# version 3 of the License, or (at your option) any later version. - -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library. If not, see or . - -from U32 import U32 - -# static unsigned long des_SPtrans[8][64]={ - -des_SPtrans =\ -[ -#nibble 0 -[ -U32(0x00820200L), U32(0x00020000L), U32(0x80800000L), U32(0x80820200L), -U32(0x00800000L), U32(0x80020200L), U32(0x80020000L), U32(0x80800000L), -U32(0x80020200L), U32(0x00820200L), U32(0x00820000L), U32(0x80000200L), -U32(0x80800200L), U32(0x00800000L), U32(0x00000000L), U32(0x80020000L), -U32(0x00020000L), U32(0x80000000L), U32(0x00800200L), U32(0x00020200L), -U32(0x80820200L), U32(0x00820000L), U32(0x80000200L), U32(0x00800200L), -U32(0x80000000L), U32(0x00000200L), U32(0x00020200L), U32(0x80820000L), -U32(0x00000200L), U32(0x80800200L), U32(0x80820000L), U32(0x00000000L), -U32(0x00000000L), U32(0x80820200L), U32(0x00800200L), U32(0x80020000L), -U32(0x00820200L), U32(0x00020000L), U32(0x80000200L), U32(0x00800200L), -U32(0x80820000L), U32(0x00000200L), U32(0x00020200L), U32(0x80800000L), -U32(0x80020200L), U32(0x80000000L), U32(0x80800000L), U32(0x00820000L), -U32(0x80820200L), U32(0x00020200L), U32(0x00820000L), U32(0x80800200L), -U32(0x00800000L), U32(0x80000200L), U32(0x80020000L), U32(0x00000000L), -U32(0x00020000L), U32(0x00800000L), U32(0x80800200L), U32(0x00820200L), -U32(0x80000000L), U32(0x80820000L), U32(0x00000200L), U32(0x80020200L), -], - -#nibble 1 -[ -U32(0x10042004L), U32(0x00000000L), U32(0x00042000L), U32(0x10040000L), -U32(0x10000004L), U32(0x00002004L), U32(0x10002000L), U32(0x00042000L), -U32(0x00002000L), U32(0x10040004L), U32(0x00000004L), U32(0x10002000L), -U32(0x00040004L), U32(0x10042000L), U32(0x10040000L), U32(0x00000004L), -U32(0x00040000L), U32(0x10002004L), U32(0x10040004L), U32(0x00002000L), -U32(0x00042004L), U32(0x10000000L), U32(0x00000000L), U32(0x00040004L), -U32(0x10002004L), U32(0x00042004L), U32(0x10042000L), U32(0x10000004L), -U32(0x10000000L), U32(0x00040000L), U32(0x00002004L), U32(0x10042004L), -U32(0x00040004L), U32(0x10042000L), U32(0x10002000L), U32(0x00042004L), -U32(0x10042004L), U32(0x00040004L), U32(0x10000004L), U32(0x00000000L), -U32(0x10000000L), U32(0x00002004L), U32(0x00040000L), U32(0x10040004L), -U32(0x00002000L), U32(0x10000000L), U32(0x00042004L), U32(0x10002004L), -U32(0x10042000L), U32(0x00002000L), U32(0x00000000L), U32(0x10000004L), -U32(0x00000004L), U32(0x10042004L), U32(0x00042000L), U32(0x10040000L), -U32(0x10040004L), U32(0x00040000L), U32(0x00002004L), U32(0x10002000L), -U32(0x10002004L), U32(0x00000004L), U32(0x10040000L), U32(0x00042000L), -], - -#nibble 2 -[ -U32(0x41000000L), U32(0x01010040L), U32(0x00000040L), U32(0x41000040L), -U32(0x40010000L), U32(0x01000000L), U32(0x41000040L), U32(0x00010040L), -U32(0x01000040L), U32(0x00010000L), U32(0x01010000L), U32(0x40000000L), -U32(0x41010040L), U32(0x40000040L), U32(0x40000000L), U32(0x41010000L), -U32(0x00000000L), U32(0x40010000L), U32(0x01010040L), U32(0x00000040L), -U32(0x40000040L), U32(0x41010040L), U32(0x00010000L), U32(0x41000000L), -U32(0x41010000L), U32(0x01000040L), U32(0x40010040L), U32(0x01010000L), -U32(0x00010040L), U32(0x00000000L), U32(0x01000000L), U32(0x40010040L), -U32(0x01010040L), U32(0x00000040L), U32(0x40000000L), U32(0x00010000L), -U32(0x40000040L), U32(0x40010000L), U32(0x01010000L), U32(0x41000040L), -U32(0x00000000L), U32(0x01010040L), U32(0x00010040L), U32(0x41010000L), -U32(0x40010000L), U32(0x01000000L), U32(0x41010040L), U32(0x40000000L), -U32(0x40010040L), U32(0x41000000L), U32(0x01000000L), U32(0x41010040L), -U32(0x00010000L), U32(0x01000040L), U32(0x41000040L), U32(0x00010040L), -U32(0x01000040L), U32(0x00000000L), U32(0x41010000L), U32(0x40000040L), -U32(0x41000000L), U32(0x40010040L), U32(0x00000040L), U32(0x01010000L), -], - -#nibble 3 -[ -U32(0x00100402L), U32(0x04000400L), U32(0x00000002L), U32(0x04100402L), -U32(0x00000000L), U32(0x04100000L), U32(0x04000402L), U32(0x00100002L), -U32(0x04100400L), U32(0x04000002L), U32(0x04000000L), U32(0x00000402L), -U32(0x04000002L), U32(0x00100402L), U32(0x00100000L), U32(0x04000000L), -U32(0x04100002L), U32(0x00100400L), U32(0x00000400L), U32(0x00000002L), -U32(0x00100400L), U32(0x04000402L), U32(0x04100000L), U32(0x00000400L), -U32(0x00000402L), U32(0x00000000L), U32(0x00100002L), U32(0x04100400L), -U32(0x04000400L), U32(0x04100002L), U32(0x04100402L), U32(0x00100000L), -U32(0x04100002L), U32(0x00000402L), U32(0x00100000L), U32(0x04000002L), -U32(0x00100400L), U32(0x04000400L), U32(0x00000002L), U32(0x04100000L), -U32(0x04000402L), U32(0x00000000L), U32(0x00000400L), U32(0x00100002L), -U32(0x00000000L), U32(0x04100002L), U32(0x04100400L), U32(0x00000400L), -U32(0x04000000L), U32(0x04100402L), U32(0x00100402L), U32(0x00100000L), -U32(0x04100402L), U32(0x00000002L), U32(0x04000400L), U32(0x00100402L), -U32(0x00100002L), U32(0x00100400L), U32(0x04100000L), U32(0x04000402L), -U32(0x00000402L), U32(0x04000000L), U32(0x04000002L), U32(0x04100400L), -], - -#nibble 4 -[ -U32(0x02000000L), U32(0x00004000L), U32(0x00000100L), U32(0x02004108L), -U32(0x02004008L), U32(0x02000100L), U32(0x00004108L), U32(0x02004000L), -U32(0x00004000L), U32(0x00000008L), U32(0x02000008L), U32(0x00004100L), -U32(0x02000108L), U32(0x02004008L), U32(0x02004100L), U32(0x00000000L), -U32(0x00004100L), U32(0x02000000L), U32(0x00004008L), U32(0x00000108L), -U32(0x02000100L), U32(0x00004108L), U32(0x00000000L), U32(0x02000008L), -U32(0x00000008L), U32(0x02000108L), U32(0x02004108L), U32(0x00004008L), -U32(0x02004000L), U32(0x00000100L), U32(0x00000108L), U32(0x02004100L), -U32(0x02004100L), U32(0x02000108L), U32(0x00004008L), U32(0x02004000L), -U32(0x00004000L), U32(0x00000008L), U32(0x02000008L), U32(0x02000100L), -U32(0x02000000L), U32(0x00004100L), U32(0x02004108L), U32(0x00000000L), -U32(0x00004108L), U32(0x02000000L), U32(0x00000100L), U32(0x00004008L), -U32(0x02000108L), U32(0x00000100L), U32(0x00000000L), U32(0x02004108L), -U32(0x02004008L), U32(0x02004100L), U32(0x00000108L), U32(0x00004000L), -U32(0x00004100L), U32(0x02004008L), U32(0x02000100L), U32(0x00000108L), -U32(0x00000008L), U32(0x00004108L), U32(0x02004000L), U32(0x02000008L), -], - -#nibble 5 -[ -U32(0x20000010L), U32(0x00080010L), U32(0x00000000L), U32(0x20080800L), -U32(0x00080010L), U32(0x00000800L), U32(0x20000810L), U32(0x00080000L), -U32(0x00000810L), U32(0x20080810L), U32(0x00080800L), U32(0x20000000L), -U32(0x20000800L), U32(0x20000010L), U32(0x20080000L), U32(0x00080810L), -U32(0x00080000L), U32(0x20000810L), U32(0x20080010L), U32(0x00000000L), -U32(0x00000800L), U32(0x00000010L), U32(0x20080800L), U32(0x20080010L), -U32(0x20080810L), U32(0x20080000L), U32(0x20000000L), U32(0x00000810L), -U32(0x00000010L), U32(0x00080800L), U32(0x00080810L), U32(0x20000800L), -U32(0x00000810L), U32(0x20000000L), U32(0x20000800L), U32(0x00080810L), -U32(0x20080800L), U32(0x00080010L), U32(0x00000000L), U32(0x20000800L), -U32(0x20000000L), U32(0x00000800L), U32(0x20080010L), U32(0x00080000L), -U32(0x00080010L), U32(0x20080810L), U32(0x00080800L), U32(0x00000010L), -U32(0x20080810L), U32(0x00080800L), U32(0x00080000L), U32(0x20000810L), -U32(0x20000010L), U32(0x20080000L), U32(0x00080810L), U32(0x00000000L), -U32(0x00000800L), U32(0x20000010L), U32(0x20000810L), U32(0x20080800L), -U32(0x20080000L), U32(0x00000810L), U32(0x00000010L), U32(0x20080010L), -], - -#nibble 6 -[ -U32(0x00001000L), U32(0x00000080L), U32(0x00400080L), U32(0x00400001L), -U32(0x00401081L), U32(0x00001001L), U32(0x00001080L), U32(0x00000000L), -U32(0x00400000L), U32(0x00400081L), U32(0x00000081L), U32(0x00401000L), -U32(0x00000001L), U32(0x00401080L), U32(0x00401000L), U32(0x00000081L), -U32(0x00400081L), U32(0x00001000L), U32(0x00001001L), U32(0x00401081L), -U32(0x00000000L), U32(0x00400080L), U32(0x00400001L), U32(0x00001080L), -U32(0x00401001L), U32(0x00001081L), U32(0x00401080L), U32(0x00000001L), -U32(0x00001081L), U32(0x00401001L), U32(0x00000080L), U32(0x00400000L), -U32(0x00001081L), U32(0x00401000L), U32(0x00401001L), U32(0x00000081L), -U32(0x00001000L), U32(0x00000080L), U32(0x00400000L), U32(0x00401001L), -U32(0x00400081L), U32(0x00001081L), U32(0x00001080L), U32(0x00000000L), -U32(0x00000080L), U32(0x00400001L), U32(0x00000001L), U32(0x00400080L), -U32(0x00000000L), U32(0x00400081L), U32(0x00400080L), U32(0x00001080L), -U32(0x00000081L), U32(0x00001000L), U32(0x00401081L), U32(0x00400000L), -U32(0x00401080L), U32(0x00000001L), U32(0x00001001L), U32(0x00401081L), -U32(0x00400001L), U32(0x00401080L), U32(0x00401000L), U32(0x00001001L), -], - -#nibble 7 -[ -U32(0x08200020L), U32(0x08208000L), U32(0x00008020L), U32(0x00000000L), -U32(0x08008000L), U32(0x00200020L), U32(0x08200000L), U32(0x08208020L), -U32(0x00000020L), U32(0x08000000L), U32(0x00208000L), U32(0x00008020L), -U32(0x00208020L), U32(0x08008020L), U32(0x08000020L), U32(0x08200000L), -U32(0x00008000L), U32(0x00208020L), U32(0x00200020L), U32(0x08008000L), -U32(0x08208020L), U32(0x08000020L), U32(0x00000000L), U32(0x00208000L), -U32(0x08000000L), U32(0x00200000L), U32(0x08008020L), U32(0x08200020L), -U32(0x00200000L), U32(0x00008000L), U32(0x08208000L), U32(0x00000020L), -U32(0x00200000L), U32(0x00008000L), U32(0x08000020L), U32(0x08208020L), -U32(0x00008020L), U32(0x08000000L), U32(0x00000000L), U32(0x00208000L), -U32(0x08200020L), U32(0x08008020L), U32(0x08008000L), U32(0x00200020L), -U32(0x08208000L), U32(0x00000020L), U32(0x00200020L), U32(0x08008000L), -U32(0x08208020L), U32(0x00200000L), U32(0x08200000L), U32(0x08000020L), -U32(0x00208000L), U32(0x00008020L), U32(0x08008020L), U32(0x08200000L), -U32(0x00000020L), U32(0x08208000L), U32(0x00208020L), U32(0x00000000L), -U32(0x08000000L), U32(0x08200020L), U32(0x00008000L), U32(0x00208020L), -], -] - -#static unsigned long des_skb[8][64]={ - -des_skb = \ -[ -#for C bits (numbered as per FIPS 46) 1 2 3 4 5 6 -[ -U32(0x00000000L),U32(0x00000010L),U32(0x20000000L),U32(0x20000010L), -U32(0x00010000L),U32(0x00010010L),U32(0x20010000L),U32(0x20010010L), -U32(0x00000800L),U32(0x00000810L),U32(0x20000800L),U32(0x20000810L), -U32(0x00010800L),U32(0x00010810L),U32(0x20010800L),U32(0x20010810L), -U32(0x00000020L),U32(0x00000030L),U32(0x20000020L),U32(0x20000030L), -U32(0x00010020L),U32(0x00010030L),U32(0x20010020L),U32(0x20010030L), -U32(0x00000820L),U32(0x00000830L),U32(0x20000820L),U32(0x20000830L), -U32(0x00010820L),U32(0x00010830L),U32(0x20010820L),U32(0x20010830L), -U32(0x00080000L),U32(0x00080010L),U32(0x20080000L),U32(0x20080010L), -U32(0x00090000L),U32(0x00090010L),U32(0x20090000L),U32(0x20090010L), -U32(0x00080800L),U32(0x00080810L),U32(0x20080800L),U32(0x20080810L), -U32(0x00090800L),U32(0x00090810L),U32(0x20090800L),U32(0x20090810L), -U32(0x00080020L),U32(0x00080030L),U32(0x20080020L),U32(0x20080030L), -U32(0x00090020L),U32(0x00090030L),U32(0x20090020L),U32(0x20090030L), -U32(0x00080820L),U32(0x00080830L),U32(0x20080820L),U32(0x20080830L), -U32(0x00090820L),U32(0x00090830L),U32(0x20090820L),U32(0x20090830L), -], - -#for C bits (numbered as per FIPS 46) 7 8 10 11 12 13 -[ -U32(0x00000000L),U32(0x02000000L),U32(0x00002000L),U32(0x02002000L), -U32(0x00200000L),U32(0x02200000L),U32(0x00202000L),U32(0x02202000L), -U32(0x00000004L),U32(0x02000004L),U32(0x00002004L),U32(0x02002004L), -U32(0x00200004L),U32(0x02200004L),U32(0x00202004L),U32(0x02202004L), -U32(0x00000400L),U32(0x02000400L),U32(0x00002400L),U32(0x02002400L), -U32(0x00200400L),U32(0x02200400L),U32(0x00202400L),U32(0x02202400L), -U32(0x00000404L),U32(0x02000404L),U32(0x00002404L),U32(0x02002404L), -U32(0x00200404L),U32(0x02200404L),U32(0x00202404L),U32(0x02202404L), -U32(0x10000000L),U32(0x12000000L),U32(0x10002000L),U32(0x12002000L), -U32(0x10200000L),U32(0x12200000L),U32(0x10202000L),U32(0x12202000L), -U32(0x10000004L),U32(0x12000004L),U32(0x10002004L),U32(0x12002004L), -U32(0x10200004L),U32(0x12200004L),U32(0x10202004L),U32(0x12202004L), -U32(0x10000400L),U32(0x12000400L),U32(0x10002400L),U32(0x12002400L), -U32(0x10200400L),U32(0x12200400L),U32(0x10202400L),U32(0x12202400L), -U32(0x10000404L),U32(0x12000404L),U32(0x10002404L),U32(0x12002404L), -U32(0x10200404L),U32(0x12200404L),U32(0x10202404L),U32(0x12202404L), -], - -#for C bits (numbered as per FIPS 46) 14 15 16 17 19 20 -[ -U32(0x00000000L),U32(0x00000001L),U32(0x00040000L),U32(0x00040001L), -U32(0x01000000L),U32(0x01000001L),U32(0x01040000L),U32(0x01040001L), -U32(0x00000002L),U32(0x00000003L),U32(0x00040002L),U32(0x00040003L), -U32(0x01000002L),U32(0x01000003L),U32(0x01040002L),U32(0x01040003L), -U32(0x00000200L),U32(0x00000201L),U32(0x00040200L),U32(0x00040201L), -U32(0x01000200L),U32(0x01000201L),U32(0x01040200L),U32(0x01040201L), -U32(0x00000202L),U32(0x00000203L),U32(0x00040202L),U32(0x00040203L), -U32(0x01000202L),U32(0x01000203L),U32(0x01040202L),U32(0x01040203L), -U32(0x08000000L),U32(0x08000001L),U32(0x08040000L),U32(0x08040001L), -U32(0x09000000L),U32(0x09000001L),U32(0x09040000L),U32(0x09040001L), -U32(0x08000002L),U32(0x08000003L),U32(0x08040002L),U32(0x08040003L), -U32(0x09000002L),U32(0x09000003L),U32(0x09040002L),U32(0x09040003L), -U32(0x08000200L),U32(0x08000201L),U32(0x08040200L),U32(0x08040201L), -U32(0x09000200L),U32(0x09000201L),U32(0x09040200L),U32(0x09040201L), -U32(0x08000202L),U32(0x08000203L),U32(0x08040202L),U32(0x08040203L), -U32(0x09000202L),U32(0x09000203L),U32(0x09040202L),U32(0x09040203L), -], - -#for C bits (numbered as per FIPS 46) 21 23 24 26 27 28 -[ -U32(0x00000000L),U32(0x00100000L),U32(0x00000100L),U32(0x00100100L), -U32(0x00000008L),U32(0x00100008L),U32(0x00000108L),U32(0x00100108L), -U32(0x00001000L),U32(0x00101000L),U32(0x00001100L),U32(0x00101100L), -U32(0x00001008L),U32(0x00101008L),U32(0x00001108L),U32(0x00101108L), -U32(0x04000000L),U32(0x04100000L),U32(0x04000100L),U32(0x04100100L), -U32(0x04000008L),U32(0x04100008L),U32(0x04000108L),U32(0x04100108L), -U32(0x04001000L),U32(0x04101000L),U32(0x04001100L),U32(0x04101100L), -U32(0x04001008L),U32(0x04101008L),U32(0x04001108L),U32(0x04101108L), -U32(0x00020000L),U32(0x00120000L),U32(0x00020100L),U32(0x00120100L), -U32(0x00020008L),U32(0x00120008L),U32(0x00020108L),U32(0x00120108L), -U32(0x00021000L),U32(0x00121000L),U32(0x00021100L),U32(0x00121100L), -U32(0x00021008L),U32(0x00121008L),U32(0x00021108L),U32(0x00121108L), -U32(0x04020000L),U32(0x04120000L),U32(0x04020100L),U32(0x04120100L), -U32(0x04020008L),U32(0x04120008L),U32(0x04020108L),U32(0x04120108L), -U32(0x04021000L),U32(0x04121000L),U32(0x04021100L),U32(0x04121100L), -U32(0x04021008L),U32(0x04121008L),U32(0x04021108L),U32(0x04121108L), -], - -#for D bits (numbered as per FIPS 46) 1 2 3 4 5 6 -[ -U32(0x00000000L),U32(0x10000000L),U32(0x00010000L),U32(0x10010000L), -U32(0x00000004L),U32(0x10000004L),U32(0x00010004L),U32(0x10010004L), -U32(0x20000000L),U32(0x30000000L),U32(0x20010000L),U32(0x30010000L), -U32(0x20000004L),U32(0x30000004L),U32(0x20010004L),U32(0x30010004L), -U32(0x00100000L),U32(0x10100000L),U32(0x00110000L),U32(0x10110000L), -U32(0x00100004L),U32(0x10100004L),U32(0x00110004L),U32(0x10110004L), -U32(0x20100000L),U32(0x30100000L),U32(0x20110000L),U32(0x30110000L), -U32(0x20100004L),U32(0x30100004L),U32(0x20110004L),U32(0x30110004L), -U32(0x00001000L),U32(0x10001000L),U32(0x00011000L),U32(0x10011000L), -U32(0x00001004L),U32(0x10001004L),U32(0x00011004L),U32(0x10011004L), -U32(0x20001000L),U32(0x30001000L),U32(0x20011000L),U32(0x30011000L), -U32(0x20001004L),U32(0x30001004L),U32(0x20011004L),U32(0x30011004L), -U32(0x00101000L),U32(0x10101000L),U32(0x00111000L),U32(0x10111000L), -U32(0x00101004L),U32(0x10101004L),U32(0x00111004L),U32(0x10111004L), -U32(0x20101000L),U32(0x30101000L),U32(0x20111000L),U32(0x30111000L), -U32(0x20101004L),U32(0x30101004L),U32(0x20111004L),U32(0x30111004L), -], - -#for D bits (numbered as per FIPS 46) 8 9 11 12 13 14 -[ -U32(0x00000000L),U32(0x08000000L),U32(0x00000008L),U32(0x08000008L), -U32(0x00000400L),U32(0x08000400L),U32(0x00000408L),U32(0x08000408L), -U32(0x00020000L),U32(0x08020000L),U32(0x00020008L),U32(0x08020008L), -U32(0x00020400L),U32(0x08020400L),U32(0x00020408L),U32(0x08020408L), -U32(0x00000001L),U32(0x08000001L),U32(0x00000009L),U32(0x08000009L), -U32(0x00000401L),U32(0x08000401L),U32(0x00000409L),U32(0x08000409L), -U32(0x00020001L),U32(0x08020001L),U32(0x00020009L),U32(0x08020009L), -U32(0x00020401L),U32(0x08020401L),U32(0x00020409L),U32(0x08020409L), -U32(0x02000000L),U32(0x0A000000L),U32(0x02000008L),U32(0x0A000008L), -U32(0x02000400L),U32(0x0A000400L),U32(0x02000408L),U32(0x0A000408L), -U32(0x02020000L),U32(0x0A020000L),U32(0x02020008L),U32(0x0A020008L), -U32(0x02020400L),U32(0x0A020400L),U32(0x02020408L),U32(0x0A020408L), -U32(0x02000001L),U32(0x0A000001L),U32(0x02000009L),U32(0x0A000009L), -U32(0x02000401L),U32(0x0A000401L),U32(0x02000409L),U32(0x0A000409L), -U32(0x02020001L),U32(0x0A020001L),U32(0x02020009L),U32(0x0A020009L), -U32(0x02020401L),U32(0x0A020401L),U32(0x02020409L),U32(0x0A020409L), -], - -#for D bits (numbered as per FIPS 46) 16 17 18 19 20 21 -[ -U32(0x00000000L),U32(0x00000100L),U32(0x00080000L),U32(0x00080100L), -U32(0x01000000L),U32(0x01000100L),U32(0x01080000L),U32(0x01080100L), -U32(0x00000010L),U32(0x00000110L),U32(0x00080010L),U32(0x00080110L), -U32(0x01000010L),U32(0x01000110L),U32(0x01080010L),U32(0x01080110L), -U32(0x00200000L),U32(0x00200100L),U32(0x00280000L),U32(0x00280100L), -U32(0x01200000L),U32(0x01200100L),U32(0x01280000L),U32(0x01280100L), -U32(0x00200010L),U32(0x00200110L),U32(0x00280010L),U32(0x00280110L), -U32(0x01200010L),U32(0x01200110L),U32(0x01280010L),U32(0x01280110L), -U32(0x00000200L),U32(0x00000300L),U32(0x00080200L),U32(0x00080300L), -U32(0x01000200L),U32(0x01000300L),U32(0x01080200L),U32(0x01080300L), -U32(0x00000210L),U32(0x00000310L),U32(0x00080210L),U32(0x00080310L), -U32(0x01000210L),U32(0x01000310L),U32(0x01080210L),U32(0x01080310L), -U32(0x00200200L),U32(0x00200300L),U32(0x00280200L),U32(0x00280300L), -U32(0x01200200L),U32(0x01200300L),U32(0x01280200L),U32(0x01280300L), -U32(0x00200210L),U32(0x00200310L),U32(0x00280210L),U32(0x00280310L), -U32(0x01200210L),U32(0x01200310L),U32(0x01280210L),U32(0x01280310L), -], - -#for D bits (numbered as per FIPS 46) 22 23 24 25 27 28 -[ -U32(0x00000000L),U32(0x04000000L),U32(0x00040000L),U32(0x04040000L), -U32(0x00000002L),U32(0x04000002L),U32(0x00040002L),U32(0x04040002L), -U32(0x00002000L),U32(0x04002000L),U32(0x00042000L),U32(0x04042000L), -U32(0x00002002L),U32(0x04002002L),U32(0x00042002L),U32(0x04042002L), -U32(0x00000020L),U32(0x04000020L),U32(0x00040020L),U32(0x04040020L), -U32(0x00000022L),U32(0x04000022L),U32(0x00040022L),U32(0x04040022L), -U32(0x00002020L),U32(0x04002020L),U32(0x00042020L),U32(0x04042020L), -U32(0x00002022L),U32(0x04002022L),U32(0x00042022L),U32(0x04042022L), -U32(0x00000800L),U32(0x04000800L),U32(0x00040800L),U32(0x04040800L), -U32(0x00000802L),U32(0x04000802L),U32(0x00040802L),U32(0x04040802L), -U32(0x00002800L),U32(0x04002800L),U32(0x00042800L),U32(0x04042800L), -U32(0x00002802L),U32(0x04002802L),U32(0x00042802L),U32(0x04042802L), -U32(0x00000820L),U32(0x04000820L),U32(0x00040820L),U32(0x04040820L), -U32(0x00000822L),U32(0x04000822L),U32(0x00040822L),U32(0x04040822L), -U32(0x00002820L),U32(0x04002820L),U32(0x00042820L),U32(0x04042820L), -U32(0x00002822L),U32(0x04002822L),U32(0x00042822L),U32(0x04042822L), -] - -] \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/ntlm.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/ntlm.py deleted file mode 100644 index 79d6dfe..0000000 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/ntlm.py +++ /dev/null @@ -1,466 +0,0 @@ -# This library is free software: you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation, either -# version 3 of the License, or (at your option) any later version. - -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library. If not, see or . - -import struct -import base64 -import string -import des -import hashlib -import hmac -import random -from socket import gethostname - -NTLM_NegotiateUnicode = 0x00000001 -NTLM_NegotiateOEM = 0x00000002 -NTLM_RequestTarget = 0x00000004 -NTLM_Unknown9 = 0x00000008 -NTLM_NegotiateSign = 0x00000010 -NTLM_NegotiateSeal = 0x00000020 -NTLM_NegotiateDatagram = 0x00000040 -NTLM_NegotiateLanManagerKey = 0x00000080 -NTLM_Unknown8 = 0x00000100 -NTLM_NegotiateNTLM = 0x00000200 -NTLM_NegotiateNTOnly = 0x00000400 -NTLM_Anonymous = 0x00000800 -NTLM_NegotiateOemDomainSupplied = 0x00001000 -NTLM_NegotiateOemWorkstationSupplied = 0x00002000 -NTLM_Unknown6 = 0x00004000 -NTLM_NegotiateAlwaysSign = 0x00008000 -NTLM_TargetTypeDomain = 0x00010000 -NTLM_TargetTypeServer = 0x00020000 -NTLM_TargetTypeShare = 0x00040000 -NTLM_NegotiateExtendedSecurity = 0x00080000 -NTLM_NegotiateIdentify = 0x00100000 -NTLM_Unknown5 = 0x00200000 -NTLM_RequestNonNTSessionKey = 0x00400000 -NTLM_NegotiateTargetInfo = 0x00800000 -NTLM_Unknown4 = 0x01000000 -NTLM_NegotiateVersion = 0x02000000 -NTLM_Unknown3 = 0x04000000 -NTLM_Unknown2 = 0x08000000 -NTLM_Unknown1 = 0x10000000 -NTLM_Negotiate128 = 0x20000000 -NTLM_NegotiateKeyExchange = 0x40000000 -NTLM_Negotiate56 = 0x80000000 - -# we send these flags with our type 1 message -NTLM_TYPE1_FLAGS = (NTLM_NegotiateUnicode | \ - NTLM_NegotiateOEM | \ - NTLM_RequestTarget | \ - NTLM_NegotiateNTLM | \ - NTLM_NegotiateOemDomainSupplied | \ - NTLM_NegotiateOemWorkstationSupplied | \ - NTLM_NegotiateAlwaysSign | \ - NTLM_NegotiateExtendedSecurity | \ - NTLM_NegotiateVersion | \ - NTLM_Negotiate128 | \ - NTLM_Negotiate56 ) -NTLM_TYPE2_FLAGS = (NTLM_NegotiateUnicode | \ - NTLM_RequestTarget | \ - NTLM_NegotiateNTLM | \ - NTLM_NegotiateAlwaysSign | \ - NTLM_NegotiateExtendedSecurity | \ - NTLM_NegotiateTargetInfo | \ - NTLM_NegotiateVersion | \ - NTLM_Negotiate128 | \ - NTLM_Negotiate56) - -NTLM_MsvAvEOL = 0 # Indicates that this is the last AV_PAIR in the list. AvLen MUST be 0. This type of information MUST be present in the AV pair list. -NTLM_MsvAvNbComputerName = 1 # The server's NetBIOS computer name. The name MUST be in Unicode, and is not null-terminated. This type of information MUST be present in the AV_pair list. -NTLM_MsvAvNbDomainName = 2 # The server's NetBIOS domain name. The name MUST be in Unicode, and is not null-terminated. This type of information MUST be present in the AV_pair list. -NTLM_MsvAvDnsComputerName = 3 # The server's Active Directory DNS computer name. The name MUST be in Unicode, and is not null-terminated. -NTLM_MsvAvDnsDomainName = 4 # The server's Active Directory DNS domain name. The name MUST be in Unicode, and is not null-terminated. -NTLM_MsvAvDnsTreeName = 5 # The server's Active Directory (AD) DNS forest tree name. The name MUST be in Unicode, and is not null-terminated. -NTLM_MsvAvFlags = 6 # A field containing a 32-bit value indicating server or client configuration. 0x00000001: indicates to the client that the account authentication is constrained. 0x00000002: indicates that the client is providing message integrity in the MIC field (section 2.2.1.3) in the AUTHENTICATE_MESSAGE. -NTLM_MsvAvTimestamp = 7 # A FILETIME structure ([MS-DTYP] section 2.3.1) in little-endian byte order that contains the server local time.<12> -NTLM_MsAvRestrictions = 8 #A Restriction_Encoding structure (section 2.2.2.2). The Value field contains a structure representing the integrity level of the security principal, as well as a MachineID created at computer startup to identify the calling machine. <13> - - -""" -utility functions for Microsoft NTLM authentication - -References: -[MS-NLMP]: NT LAN Manager (NTLM) Authentication Protocol Specification -http://download.microsoft.com/download/a/e/6/ae6e4142-aa58-45c6-8dcf-a657e5900cd3/%5BMS-NLMP%5D.pdf - -[MS-NTHT]: NTLM Over HTTP Protocol Specification -http://download.microsoft.com/download/a/e/6/ae6e4142-aa58-45c6-8dcf-a657e5900cd3/%5BMS-NTHT%5D.pdf - -Cntlm Authentication Proxy -http://cntlm.awk.cz/ - -NTLM Authorization Proxy Server -http://sourceforge.net/projects/ntlmaps/ - -Optimized Attack for NTLM2 Session Response -http://www.blackhat.com/presentations/bh-asia-04/bh-jp-04-pdfs/bh-jp-04-seki.pdf -""" -def dump_NegotiateFlags(NegotiateFlags): - if NegotiateFlags & NTLM_NegotiateUnicode: - print "NTLM_NegotiateUnicode set" - if NegotiateFlags & NTLM_NegotiateOEM: - print "NTLM_NegotiateOEM set" - if NegotiateFlags & NTLM_RequestTarget: - print "NTLM_RequestTarget set" - if NegotiateFlags & NTLM_Unknown9: - print "NTLM_Unknown9 set" - if NegotiateFlags & NTLM_NegotiateSign: - print "NTLM_NegotiateSign set" - if NegotiateFlags & NTLM_NegotiateSeal: - print "NTLM_NegotiateSeal set" - if NegotiateFlags & NTLM_NegotiateDatagram: - print "NTLM_NegotiateDatagram set" - if NegotiateFlags & NTLM_NegotiateLanManagerKey: - print "NTLM_NegotiateLanManagerKey set" - if NegotiateFlags & NTLM_Unknown8: - print "NTLM_Unknown8 set" - if NegotiateFlags & NTLM_NegotiateNTLM: - print "NTLM_NegotiateNTLM set" - if NegotiateFlags & NTLM_NegotiateNTOnly: - print "NTLM_NegotiateNTOnly set" - if NegotiateFlags & NTLM_Anonymous: - print "NTLM_Anonymous set" - if NegotiateFlags & NTLM_NegotiateOemDomainSupplied: - print "NTLM_NegotiateOemDomainSupplied set" - if NegotiateFlags & NTLM_NegotiateOemWorkstationSupplied: - print "NTLM_NegotiateOemWorkstationSupplied set" - if NegotiateFlags & NTLM_Unknown6: - print "NTLM_Unknown6 set" - if NegotiateFlags & NTLM_NegotiateAlwaysSign: - print "NTLM_NegotiateAlwaysSign set" - if NegotiateFlags & NTLM_TargetTypeDomain: - print "NTLM_TargetTypeDomain set" - if NegotiateFlags & NTLM_TargetTypeServer: - print "NTLM_TargetTypeServer set" - if NegotiateFlags & NTLM_TargetTypeShare: - print "NTLM_TargetTypeShare set" - if NegotiateFlags & NTLM_NegotiateExtendedSecurity: - print "NTLM_NegotiateExtendedSecurity set" - if NegotiateFlags & NTLM_NegotiateIdentify: - print "NTLM_NegotiateIdentify set" - if NegotiateFlags & NTLM_Unknown5: - print "NTLM_Unknown5 set" - if NegotiateFlags & NTLM_RequestNonNTSessionKey: - print "NTLM_RequestNonNTSessionKey set" - if NegotiateFlags & NTLM_NegotiateTargetInfo: - print "NTLM_NegotiateTargetInfo set" - if NegotiateFlags & NTLM_Unknown4: - print "NTLM_Unknown4 set" - if NegotiateFlags & NTLM_NegotiateVersion: - print "NTLM_NegotiateVersion set" - if NegotiateFlags & NTLM_Unknown3: - print "NTLM_Unknown3 set" - if NegotiateFlags & NTLM_Unknown2: - print "NTLM_Unknown2 set" - if NegotiateFlags & NTLM_Unknown1: - print "NTLM_Unknown1 set" - if NegotiateFlags & NTLM_Negotiate128: - print "NTLM_Negotiate128 set" - if NegotiateFlags & NTLM_NegotiateKeyExchange: - print "NTLM_NegotiateKeyExchange set" - if NegotiateFlags & NTLM_Negotiate56: - print "NTLM_Negotiate56 set" - -def create_NTLM_NEGOTIATE_MESSAGE(user, type1_flags=NTLM_TYPE1_FLAGS): - BODY_LENGTH = 40 - Payload_start = BODY_LENGTH # in bytes - protocol = 'NTLMSSP\0' #name - - type = struct.pack(' 40: - TargetInfoLen = struct.unpack(" time.time(): + self.print_skip() + return + + self.upgrade_packages() + + def install_missing(self): + """ + Installs all packages that were listed in the list of + `installed_packages` from Package Control.sublime-settings but were not + found on the filesystem and passed as `found_packages`. + """ + + if not self.missing_packages or not self.should_install_missing: + return + + console_write(u'Installing %s missing packages' % len(self.missing_packages), True) + for package in self.missing_packages: + if self.installer.manager.install_package(package): + console_write(u'Installed missing package %s' % package, True) + + def print_skip(self): + """ + Prints a notice in the console if the automatic upgrade is skipped + due to already having been run in the last `auto_upgrade_frequency` + hours. + """ + + last_run = datetime.datetime.fromtimestamp(self.last_run) + next_run = datetime.datetime.fromtimestamp(self.next_run) + date_format = '%Y-%m-%d %H:%M:%S' + message_string = u'Skipping automatic upgrade, last run at %s, next run at %s or after' % ( + last_run.strftime(date_format), next_run.strftime(date_format)) + console_write(message_string, True) + + def upgrade_packages(self): + """ + Upgrades all packages that are not currently upgraded to the lastest + version. Also renames any installed packages to their new names. + """ + + if not self.auto_upgrade: + return + + self.package_renamer.rename_packages(self.installer) + + package_list = self.installer.make_package_list(['install', + 'reinstall', 'downgrade', 'overwrite', 'none'], + ignore_packages=self.auto_upgrade_ignore) + + # If Package Control is being upgraded, just do that and restart + for package in package_list: + if package[0] != 'Package Control': + continue + + def reset_last_run(): + # Re-save the last run time so it runs again after PC has + # been updated + self.save_last_run(self.last_run) + sublime.set_timeout(reset_last_run, 1) + package_list = [package] + break + + if not package_list: + console_write(u'No updated packages', True) + return + + console_write(u'Installing %s upgrades' % len(package_list), True) + + disabled_packages = [] + + def do_upgrades(): + # Wait so that the ignored packages can be "unloaded" + time.sleep(0.5) + + # We use a function to generate the on-complete lambda because if + # we don't, the lambda will bind to info at the current scope, and + # thus use the last value of info from the loop + def make_on_complete(name): + return lambda: self.installer.reenable_package(name) + + for info in package_list: + if info[0] in disabled_packages: + on_complete = make_on_complete(info[0]) + else: + on_complete = None + + self.installer.manager.install_package(info[0]) + + version = re.sub('^.*?(v[\d\.]+).*?$', '\\1', info[2]) + if version == info[2] and version.find('pull with') != -1: + vcs = re.sub('^pull with (\w+).*?$', '\\1', version) + version = 'latest %s commit' % vcs + message_string = u'Upgraded %s to %s' % (info[0], version) + console_write(message_string, True) + if on_complete: + sublime.set_timeout(on_complete, 1) + + # Disabling a package means changing settings, which can only be done + # in the main thread. We then create a new background thread so that + # the upgrade process does not block the UI. + def disable_packages(): + disabled_packages.extend(self.installer.disable_packages([info[0] for info in package_list])) + threading.Thread(target=do_upgrades).start() + sublime.set_timeout(disable_packages, 1) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/ca_certs.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/ca_certs.py new file mode 100644 index 0000000..d29d2e0 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/ca_certs.py @@ -0,0 +1,378 @@ +import hashlib +import os +import re +import time +import sys + +from .cmd import Cli +from .console_write import console_write +from .open_compat import open_compat, read_compat + + +# Have somewhere to store the CA bundle, even when not running in Sublime Text +try: + import sublime + ca_bundle_dir = None +except (ImportError): + ca_bundle_dir = os.path.join(os.path.expanduser('~'), '.package_control') + if not os.path.exists(ca_bundle_dir): + os.mkdir(ca_bundle_dir) + + +def find_root_ca_cert(settings, domain): + runner = OpensslCli(settings.get('openssl_binary'), settings.get('debug')) + binary = runner.retrieve_binary() + + args = [binary, 's_client', '-showcerts', '-connect', domain + ':443'] + result = runner.execute(args, os.path.dirname(binary)) + + certs = [] + temp = [] + + in_block = False + for line in result.splitlines(): + if line.find('BEGIN CERTIFICATE') != -1: + in_block = True + if in_block: + temp.append(line) + if line.find('END CERTIFICATE') != -1: + in_block = False + certs.append(u"\n".join(temp)) + temp = [] + + # Remove the cert for the domain itself, just leaving the + # chain cert and the CA cert + certs.pop(0) + + # Look for the "parent" root CA cert + subject = openssl_get_cert_subject(settings, certs[-1]) + issuer = openssl_get_cert_issuer(settings, certs[-1]) + + cert = get_ca_cert_by_subject(settings, issuer) + cert_hash = hashlib.md5(cert.encode('utf-8')).hexdigest() + + return [cert, cert_hash] + + + +def get_system_ca_bundle_path(settings): + """ + Get the filesystem path to the system CA bundle. On Linux it looks in a + number of predefined places, however on OS X it has to be programatically + exported from the SystemRootCertificates.keychain. Windows does not ship + with a CA bundle, but also we use WinINet on Windows, so we don't need to + worry about CA certs. + + :param settings: + A dict to look in for `debug` and `openssl_binary` keys + + :return: + The full filesystem path to the .ca-bundle file, or False on error + """ + + # If the sublime module is available, we bind this value at run time + # since the sublime.packages_path() is not available at import time + global ca_bundle_dir + + platform = sys.platform + debug = settings.get('debug') + + ca_path = False + + if platform == 'win32': + console_write(u"Unable to get system CA cert path since Windows does not ship with them", True) + return False + + # OS X + if platform == 'darwin': + if not ca_bundle_dir: + ca_bundle_dir = os.path.join(sublime.packages_path(), 'User') + ca_path = os.path.join(ca_bundle_dir, 'Package Control.system-ca-bundle') + + exists = os.path.exists(ca_path) + # The bundle is old if it is a week or more out of date + is_old = exists and os.stat(ca_path).st_mtime < time.time() - 604800 + + if not exists or is_old: + if debug: + console_write(u"Generating new CA bundle from system keychain", True) + _osx_create_ca_bundle(settings, ca_path) + if debug: + console_write(u"Finished generating new CA bundle at %s" % ca_path, True) + elif debug: + console_write(u"Found previously exported CA bundle at %s" % ca_path, True) + + # Linux + else: + # Common CA cert paths + paths = [ + '/usr/lib/ssl/certs/ca-certificates.crt', + '/etc/ssl/certs/ca-certificates.crt', + '/etc/pki/tls/certs/ca-bundle.crt', + '/etc/ssl/ca-bundle.pem' + ] + for path in paths: + if os.path.exists(path): + ca_path = path + break + + if debug and ca_path: + console_write(u"Found system CA bundle at %s" % ca_path, True) + + return ca_path + + +def get_ca_cert_by_subject(settings, subject): + bundle_path = get_system_ca_bundle_path(settings) + + with open_compat(bundle_path, 'r') as f: + contents = read_compat(f) + + temp = [] + + in_block = False + for line in contents.splitlines(): + if line.find('BEGIN CERTIFICATE') != -1: + in_block = True + + if in_block: + temp.append(line) + + if line.find('END CERTIFICATE') != -1: + in_block = False + cert = u"\n".join(temp) + temp = [] + + if openssl_get_cert_subject(settings, cert) == subject: + return cert + + return False + + +def openssl_get_cert_issuer(settings, cert): + """ + Uses the openssl command line client to extract the issuer of an x509 + certificate. + + :param settings: + A dict to look in for `debug` and `openssl_binary` keys + + :param cert: + A string containing the PEM-encoded x509 certificate to extract the + issuer from + + :return: + The cert issuer + """ + + runner = OpensslCli(settings.get('openssl_binary'), settings.get('debug')) + binary = runner.retrieve_binary() + args = [binary, 'x509', '-noout', '-issuer'] + output = runner.execute(args, os.path.dirname(binary), cert) + return re.sub('^issuer=\s*', '', output) + + +def openssl_get_cert_name(settings, cert): + """ + Uses the openssl command line client to extract the name of an x509 + certificate. If the commonName is set, that is used, otherwise the first + organizationalUnitName is used. This mirrors what OS X uses for storing + trust preferences. + + :param settings: + A dict to look in for `debug` and `openssl_binary` keys + + :param cert: + A string containing the PEM-encoded x509 certificate to extract the + name from + + :return: + The cert subject name, which is the commonName (if available), or the + first organizationalUnitName + """ + + runner = OpensslCli(settings.get('openssl_binary'), settings.get('debug')) + + binary = runner.retrieve_binary() + + args = [binary, 'x509', '-noout', '-subject', '-nameopt', + 'sep_multiline,lname,utf8'] + result = runner.execute(args, os.path.dirname(binary), cert) + + # First look for the common name + cn = None + # If there is no common name for the cert, the trust prefs use the first + # orginizational unit name + first_ou = None + + for line in result.splitlines(): + match = re.match('^\s+commonName=(.*)$', line) + if match: + cn = match.group(1) + break + match = re.match('^\s+organizationalUnitName=(.*)$', line) + if first_ou is None and match: + first_ou = match.group(1) + continue + + # This is the name of the cert that would be used in the trust prefs + return cn or first_ou + + +def openssl_get_cert_subject(settings, cert): + """ + Uses the openssl command line client to extract the subject of an x509 + certificate. + + :param settings: + A dict to look in for `debug` and `openssl_binary` keys + + :param cert: + A string containing the PEM-encoded x509 certificate to extract the + subject from + + :return: + The cert subject + """ + + runner = OpensslCli(settings.get('openssl_binary'), settings.get('debug')) + binary = runner.retrieve_binary() + args = [binary, 'x509', '-noout', '-subject'] + output = runner.execute(args, os.path.dirname(binary), cert) + return re.sub('^subject=\s*', '', output) + + +def _osx_create_ca_bundle(settings, destination): + """ + Uses the OS X `security` command line tool to export the system's list of + CA certs from /System/Library/Keychains/SystemRootCertificates.keychain. + Checks the cert names against the trust preferences, ensuring that + distrusted certs are not exported. + + :param settings: + A dict to look in for `debug` and `openssl_binary` keys + + :param destination: + The full filesystem path to the destination .ca-bundle file + """ + + distrusted_certs = _osx_get_distrusted_certs(settings) + + # Export the root certs + args = ['/usr/bin/security', 'export', '-k', + '/System/Library/Keychains/SystemRootCertificates.keychain', '-t', + 'certs', '-p'] + result = Cli(None, settings.get('debug')).execute(args, '/usr/bin') + + certs = [] + temp = [] + + in_block = False + for line in result.splitlines(): + if line.find('BEGIN CERTIFICATE') != -1: + in_block = True + + if in_block: + temp.append(line) + + if line.find('END CERTIFICATE') != -1: + in_block = False + cert = u"\n".join(temp) + temp = [] + + if distrusted_certs: + # If it is a distrusted cert, we move on to the next + cert_name = openssl_get_cert_name(settings, cert) + if cert_name in distrusted_certs: + if settings.get('debug'): + console_write(u'Skipping root certficate %s because it is distrusted' % cert_name, True) + continue + + certs.append(cert) + + with open_compat(destination, 'w') as f: + f.write(u"\n".join(certs)) + + +def _osx_get_distrusted_certs(settings): + """ + Uses the OS X `security` binary to get a list of admin trust settings, + which is what is set when a user changes the trust setting on a root + certificate. By looking at the SSL policy, we can properly exclude + distrusted certs from out export. + + Tested on OS X 10.6 and 10.8 + + :param settings: + A dict to look in for `debug` key + + :return: + A list of CA cert names, where the name is the commonName (if + available), or the first organizationalUnitName + """ + + args = ['/usr/bin/security', 'dump-trust-settings', '-d'] + result = Cli(None, settings.get('debug')).execute(args, '/usr/bin') + + distrusted_certs = [] + cert_name = None + ssl_policy = False + for line in result.splitlines(): + if line == '': + continue + + # Reset for each cert + match = re.match('Cert\s+\d+:\s+(.*)$', line) + if match: + cert_name = match.group(1) + continue + + # Reset for each trust setting + if re.match('^\s+Trust\s+Setting\s+\d+:', line): + ssl_policy = False + continue + + # We are only interested in SSL policies + if re.match('^\s+Policy\s+OID\s+:\s+SSL', line): + ssl_policy = True + continue + + distrusted = re.match('^\s+Result\s+Type\s+:\s+kSecTrustSettingsResultDeny', line) + if ssl_policy and distrusted and cert_name not in distrusted_certs: + if settings.get('debug'): + console_write(u'Found SSL distrust setting for root certificate %s' % cert_name, True) + distrusted_certs.append(cert_name) + + return distrusted_certs + + +class OpensslCli(Cli): + + cli_name = 'openssl' + + def retrieve_binary(self): + """ + Returns the path to the openssl executable + + :return: The string path to the executable or False on error + """ + + name = 'openssl' + if os.name == 'nt': + name += '.exe' + + binary = self.find_binary(name) + if binary and os.path.isdir(binary): + full_path = os.path.join(binary, name) + if os.path.exists(full_path): + binary = full_path + + if not binary: + show_error((u'Unable to find %s. Please set the openssl_binary ' + + u'setting by accessing the Preferences > Package Settings > ' + + u'Package Control > Settings \u2013 User menu entry. The ' + + u'Settings \u2013 Default entry can be used for reference, ' + + u'but changes to that will be overwritten upon next upgrade.') % name) + return False + + return binary diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/cache.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/cache.py new file mode 100644 index 0000000..015744c --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/cache.py @@ -0,0 +1,173 @@ +import time + + +# A cache of channel and repository info to allow users to install multiple +# packages without having to wait for the metadata to be downloaded more +# than once. The keys are managed locally by the utilizing code. +_channel_repository_cache = {} + + +def clear_cache(): + global _channel_repository_cache + _channel_repository_cache = {} + + +def get_cache(key, default=None): + """ + Gets an in-memory cache value + + :param key: + The string key + + :param default: + The value to return if the key has not been set, or the ttl expired + + :return: + The cached value, or default + """ + + struct = _channel_repository_cache.get(key, {}) + expires = struct.get('expires') + if expires and expires > time.time(): + return struct.get('data') + return default + + +def merge_cache_over_settings(destination, setting, key_prefix): + """ + Take the cached value of `key` and put it into the key `setting` of + the destination.settings dict. Merge the values by overlaying the + cached setting over the existing info. + + :param destination: + An object that has a `.settings` attribute that is a dict + + :param setting: + The dict key to use when pushing the value into the settings dict + + :param key_prefix: + The string to prefix to `setting` to make the cache key + """ + + existing = destination.settings.get(setting, {}) + value = get_cache(key_prefix + '.' + setting, {}) + if value: + existing.update(value) + destination.settings[setting] = existing + + +def merge_cache_under_settings(destination, setting, key_prefix, list_=False): + """ + Take the cached value of `key` and put it into the key `setting` of + the destination.settings dict. Merge the values by overlaying the + existing setting value over the cached info. + + :param destination: + An object that has a `.settings` attribute that is a dict + + :param setting: + The dict key to use when pushing the value into the settings dict + + :param key_prefix: + The string to prefix to `setting` to make the cache key + + :param list_: + If a list should be used instead of a dict + """ + + default = {} if not list_ else [] + existing = destination.settings.get(setting) + value = get_cache(key_prefix + '.' + setting, default) + if value: + if existing: + if list_: + # Prevent duplicate values + base = dict(zip(value, [None]*len(value))) + for val in existing: + if val in base: + continue + value.append(val) + else: + value.update(existing) + destination.settings[setting] = value + + +def set_cache(key, data, ttl=300): + """ + Sets an in-memory cache value + + :param key: + The string key + + :param data: + The data to cache + + :param ttl: + The integer number of second to cache the data for + """ + + _channel_repository_cache[key] = { + 'data': data, + 'expires': time.time() + ttl + } + + +def set_cache_over_settings(destination, setting, key_prefix, value, ttl): + """ + Take the value passed, and merge it over the current `setting`. Once + complete, take the value and set the cache `key` and destination.settings + `setting` to that value, using the `ttl` for set_cache(). + + :param destination: + An object that has a `.settings` attribute that is a dict + + :param setting: + The dict key to use when pushing the value into the settings dict + + :param key_prefix: + The string to prefix to `setting` to make the cache key + + :param value: + The value to set + + :param ttl: + The cache ttl to use + """ + + existing = destination.settings.get(setting, {}) + existing.update(value) + set_cache(key_prefix + '.' + setting, value, ttl) + destination.settings[setting] = value + + +def set_cache_under_settings(destination, setting, key_prefix, value, ttl, list_=False): + """ + Take the value passed, and merge the current `setting` over it. Once + complete, take the value and set the cache `key` and destination.settings + `setting` to that value, using the `ttl` for set_cache(). + + :param destination: + An object that has a `.settings` attribute that is a dict + + :param setting: + The dict key to use when pushing the value into the settings dict + + :param key_prefix: + The string to prefix to `setting` to make the cache key + + :param value: + The value to set + + :param ttl: + The cache ttl to use + """ + + default = {} if not list_ else [] + existing = destination.settings.get(setting, default) + if value: + if list_: + value.extend(existing) + else: + value.update(existing) + set_cache(key_prefix + '.' + setting, value, ttl) + destination.settings[setting] = value diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clear_directory.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clear_directory.py new file mode 100644 index 0000000..4ddfc07 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clear_directory.py @@ -0,0 +1,37 @@ +import os + + +def clear_directory(directory, ignore_paths=None): + """ + Tries to delete all files and folders from a directory + + :param directory: + The string directory path + + :param ignore_paths: + An array of paths to ignore while deleting files + + :return: + If all of the files and folders were successfully deleted + """ + + was_exception = False + for root, dirs, files in os.walk(directory, topdown=False): + paths = [os.path.join(root, f) for f in files] + paths.extend([os.path.join(root, d) for d in dirs]) + + for path in paths: + try: + # Don't delete the metadata file, that way we have it + # when the reinstall happens, and the appropriate + # usage info can be sent back to the server + if ignore_paths and path in ignore_paths: + continue + if os.path.isdir(path): + os.rmdir(path) + else: + os.remove(path) + except (OSError, IOError): + was_exception = True + + return not was_exception diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/__init__.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/__init__.py similarity index 100% rename from EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/__init__.py rename to EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/__init__.py diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/bitbucket_client.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/bitbucket_client.py new file mode 100644 index 0000000..87a4334 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/bitbucket_client.py @@ -0,0 +1,257 @@ +import re + +from ..versions import version_sort, version_filter +from .json_api_client import JSONApiClient + + +# A predefined list of readme filenames to look for +_readme_filenames = [ + 'readme', + 'readme.txt', + 'readme.md', + 'readme.mkd', + 'readme.mdown', + 'readme.markdown', + 'readme.textile', + 'readme.creole', + 'readme.rst' +] + + +class BitBucketClient(JSONApiClient): + + def download_info(self, url): + """ + Retrieve information about downloading a package + + :param url: + The URL of the repository, in one of the forms: + https://bitbucket.org/{user}/{repo} + https://bitbucket.org/{user}/{repo}/src/{branch} + https://bitbucket.org/{user}/{repo}/#tags + If the last option, grabs the info from the newest + tag that is a valid semver version. + + :raises: + DownloaderException: when there is an error downloading + ClientException: when there is an error parsing the response + + :return: + None if no match, False if no commit, or a dict with the following keys: + `version` - the version number of the download + `url` - the download URL of a zip file of the package + `date` - the ISO-8601 timestamp string when the version was published + """ + + commit_info = self._commit_info(url) + if not commit_info: + return commit_info + + return { + 'version': commit_info['version'], + 'url': 'https://bitbucket.org/%s/get/%s.zip' % (commit_info['user_repo'], commit_info['commit']), + 'date': commit_info['timestamp'] + } + + def repo_info(self, url): + """ + Retrieve general information about a repository + + :param url: + The URL to the repository, in one of the forms: + https://bitbucket.org/{user}/{repo} + https://bitbucket.org/{user}/{repo}/src/{branch} + + :raises: + DownloaderException: when there is an error downloading + ClientException: when there is an error parsing the response + + :return: + None if no match, or a dict with the following keys: + `name` + `description` + `homepage` - URL of the homepage + `author` + `readme` - URL of the readme + `issues` - URL of bug tracker + `donate` - URL of a donate page + """ + + user_repo, branch = self._user_repo_branch(url) + if not user_repo: + return user_repo + + api_url = self._make_api_url(user_repo) + + info = self.fetch_json(api_url) + + issues_url = u'https://bitbucket.org/%s/issues' % user_repo + + return { + 'name': info['name'], + 'description': info['description'] or 'No description provided', + 'homepage': info['website'] or url, + 'author': info['owner'], + 'donate': u'https://www.gittip.com/on/bitbucket/%s/' % info['owner'], + 'readme': self._readme_url(user_repo, branch), + 'issues': issues_url if info['has_issues'] else None + } + + def _commit_info(self, url): + """ + Fetches info about the latest commit to a repository + + :param url: + The URL to the repository, in one of the forms: + https://bitbucket.org/{user}/{repo} + https://bitbucket.org/{user}/{repo}/src/{branch} + https://bitbucket.org/{user}/{repo}/#tags + If the last option, grabs the info from the newest + tag that is a valid semver version. + + :raises: + DownloaderException: when there is an error downloading + ClientException: when there is an error parsing the response + + :return: + None if no match, False if no commit, or a dict with the following keys: + `user_repo` - the user/repo name + `timestamp` - the ISO-8601 UTC timestamp string + `commit` - the branch or tag name + `version` - the extracted version number + """ + + tags_match = re.match('https?://bitbucket.org/([^/]+/[^#/]+)/?#tags$', url) + + version = None + + if tags_match: + user_repo = tags_match.group(1) + tags_url = self._make_api_url(user_repo, '/tags') + tags_list = self.fetch_json(tags_url) + tags = version_filter(tags_list.keys(), self.settings.get('install_prereleases')) + tags = version_sort(tags, reverse=True) + if not tags: + return False + commit = tags[0] + version = re.sub('^v', '', commit) + + else: + user_repo, commit = self._user_repo_branch(url) + if not user_repo: + return user_repo + + changeset_url = self._make_api_url(user_repo, '/changesets/%s' % commit) + commit_info = self.fetch_json(changeset_url) + + commit_date = commit_info['timestamp'][0:19] + + if not version: + version = re.sub('[\-: ]', '.', commit_date) + + return { + 'user_repo': user_repo, + 'timestamp': commit_date, + 'commit': commit, + 'version': version + } + + def _main_branch_name(self, user_repo): + """ + Fetch the name of the default branch + + :param user_repo: + The user/repo name to get the main branch for + + :raises: + DownloaderException: when there is an error downloading + ClientException: when there is an error parsing the response + + :return: + The name of the main branch - `master` or `default` + """ + + main_branch_url = self._make_api_url(user_repo, '/main-branch') + main_branch_info = self.fetch_json(main_branch_url, True) + return main_branch_info['name'] + + def _make_api_url(self, user_repo, suffix=''): + """ + Generate a URL for the BitBucket API + + :param user_repo: + The user/repo of the repository + + :param suffix: + The extra API path info to add to the URL + + :return: + The API URL + """ + + return 'https://api.bitbucket.org/1.0/repositories/%s%s' % (user_repo, suffix) + + def _readme_url(self, user_repo, branch, prefer_cached=False): + """ + Parse the root directory listing for the repo and return the URL + to any file that looks like a readme + + :param user_repo: + The user/repo string + + :param branch: + The branch to fetch the readme from + + :param prefer_cached: + If a cached directory listing should be used instead of a new HTTP request + + :raises: + DownloaderException: when there is an error downloading + ClientException: when there is an error parsing the response + + :return: + The URL to the readme file, or None + """ + + listing_url = self._make_api_url(user_repo, '/src/%s/' % branch) + root_dir_info = self.fetch_json(listing_url, prefer_cached) + + for entry in root_dir_info['files']: + if entry['path'].lower() in _readme_filenames: + return 'https://bitbucket.org/%s/raw/%s/%s' % (user_repo, + branch, entry['path']) + + return None + + def _user_repo_branch(self, url): + """ + Extract the username/repo and branch name from the URL + + :param url: + The URL to extract the info from, in one of the forms: + https://bitbucket.org/{user}/{repo} + https://bitbucket.org/{user}/{repo}/src/{branch} + + :raises: + DownloaderException: when there is an error downloading + ClientException: when there is an error parsing the response + + :return: + A tuple of (user/repo, branch name) or (None, None) if not matching + """ + + repo_match = re.match('https?://bitbucket.org/([^/]+/[^/]+)/?$', url) + branch_match = re.match('https?://bitbucket.org/([^/]+/[^/]+)/src/([^/]+)/?$', url) + + if repo_match: + user_repo = repo_match.group(1) + branch = self._main_branch_name(user_repo) + + elif branch_match: + user_repo = branch_match.group(1) + branch = branch_match.group(2) + + else: + return (None, None) + + return (user_repo, branch) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/client_exception.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/client_exception.py new file mode 100644 index 0000000..fb8dd72 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/client_exception.py @@ -0,0 +1,5 @@ +class ClientException(Exception): + """If a client could not fetch information""" + + def __str__(self): + return self.args[0] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/github_client.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/github_client.py new file mode 100644 index 0000000..bd519f7 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/github_client.py @@ -0,0 +1,292 @@ +import re + +try: + # Python 3 + from urllib.parse import urlencode, quote +except (ImportError): + # Python 2 + from urllib import urlencode, quote + +from ..versions import version_sort, version_filter +from .json_api_client import JSONApiClient +from ..downloaders.downloader_exception import DownloaderException + + +class GitHubClient(JSONApiClient): + + def download_info(self, url): + """ + Retrieve information about downloading a package + + :param url: + The URL of the repository, in one of the forms: + https://github.com/{user}/{repo} + https://github.com/{user}/{repo}/tree/{branch} + https://github.com/{user}/{repo}/tags + If the last option, grabs the info from the newest + tag that is a valid semver version. + + :raises: + DownloaderException: when there is an error downloading + ClientException: when there is an error parsing the response + + :return: + None if no match, False if no commit, or a dict with the following keys: + `version` - the version number of the download + `url` - the download URL of a zip file of the package + `date` - the ISO-8601 timestamp string when the version was published + """ + + commit_info = self._commit_info(url) + if not commit_info: + return commit_info + + return { + 'version': commit_info['version'], + # We specifically use codeload.github.com here because the download + # URLs all redirect there, and some of the downloaders don't follow + # HTTP redirect headers + 'url': 'https://codeload.github.com/%s/zip/%s' % (commit_info['user_repo'], quote(commit_info['commit'])), + 'date': commit_info['timestamp'] + } + + def repo_info(self, url): + """ + Retrieve general information about a repository + + :param url: + The URL to the repository, in one of the forms: + https://github.com/{user}/{repo} + https://github.com/{user}/{repo}/tree/{branch} + + :raises: + DownloaderException: when there is an error downloading + ClientException: when there is an error parsing the response + + :return: + None if no match, or a dict with the following keys: + `name` + `description` + `homepage` - URL of the homepage + `author` + `readme` - URL of the readme + `issues` - URL of bug tracker + `donate` - URL of a donate page + """ + + user_repo, branch = self._user_repo_branch(url) + if not user_repo: + return user_repo + + api_url = self._make_api_url(user_repo) + + info = self.fetch_json(api_url) + + output = self._extract_repo_info(info) + output['readme'] = None + + readme_info = self._readme_info(user_repo, branch) + if not readme_info: + return output + + output['readme'] = 'https://raw.github.com/%s/%s/%s' % (user_repo, + branch, readme_info['path']) + return output + + def user_info(self, url): + """ + Retrieve general information about all repositories that are + part of a user/organization. + + :param url: + The URL to the user/organization, in the following form: + https://github.com/{user} + + :raises: + DownloaderException: when there is an error downloading + ClientException: when there is an error parsing the response + + :return: + None if no match, or am list of dicts with the following keys: + `name` + `description` + `homepage` - URL of the homepage + `author` + `readme` - URL of the readme + `issues` - URL of bug tracker + `donate` - URL of a donate page + """ + + user_match = re.match('https?://github.com/([^/]+)/?$', url) + if user_match == None: + return None + + user = user_match.group(1) + api_url = self._make_api_url(user) + + repos_info = self.fetch_json(api_url) + + output = [] + for info in repos_info: + output.append(self._extract_repo_info(info)) + return output + + def _commit_info(self, url): + """ + Fetches info about the latest commit to a repository + + :param url: + The URL to the repository, in one of the forms: + https://github.com/{user}/{repo} + https://github.com/{user}/{repo}/tree/{branch} + https://github.com/{user}/{repo}/tags + If the last option, grabs the info from the newest + tag that is a valid semver version. + + :raises: + DownloaderException: when there is an error downloading + ClientException: when there is an error parsing the response + + :return: + None if no match, False is no commit, or a dict with the following keys: + `user_repo` - the user/repo name + `timestamp` - the ISO-8601 UTC timestamp string + `commit` - the branch or tag name + `version` - the extracted version number + """ + + tags_match = re.match('https?://github.com/([^/]+/[^/]+)/tags/?$', url) + + version = None + + if tags_match: + user_repo = tags_match.group(1) + tags_url = self._make_api_url(user_repo, '/tags') + tags_list = self.fetch_json(tags_url) + tags = [tag['name'] for tag in tags_list] + tags = version_filter(tags, self.settings.get('install_prereleases')) + tags = version_sort(tags, reverse=True) + if not tags: + return False + commit = tags[0] + version = re.sub('^v', '', commit) + + else: + user_repo, commit = self._user_repo_branch(url) + if not user_repo: + return user_repo + + query_string = urlencode({'sha': commit, 'per_page': 1}) + commit_url = self._make_api_url(user_repo, '/commits?%s' % query_string) + commit_info = self.fetch_json(commit_url) + + commit_date = commit_info[0]['commit']['committer']['date'][0:19].replace('T', ' ') + + if not version: + version = re.sub('[\-: ]', '.', commit_date) + + return { + 'user_repo': user_repo, + 'timestamp': commit_date, + 'commit': commit, + 'version': version + } + + def _extract_repo_info(self, result): + """ + Extracts information about a repository from the API result + + :param result: + A dict representing the data returned from the GitHub API + + :return: + A dict with the following keys: + `name` + `description` + `homepage` - URL of the homepage + `author` + `issues` - URL of bug tracker + `donate` - URL of a donate page + """ + + issues_url = u'https://github.com/%s/%s/issues' % (result['owner']['login'], result['name']) + + return { + 'name': result['name'], + 'description': result['description'] or 'No description provided', + 'homepage': result['homepage'] or result['html_url'], + 'author': result['owner']['login'], + 'issues': issues_url if result['has_issues'] else None, + 'donate': u'https://www.gittip.com/on/github/%s/' % result['owner']['login'] + } + + def _make_api_url(self, user_repo, suffix=''): + """ + Generate a URL for the BitBucket API + + :param user_repo: + The user/repo of the repository + + :param suffix: + The extra API path info to add to the URL + + :return: + The API URL + """ + + return 'https://api.github.com/repos/%s%s' % (user_repo, suffix) + + def _readme_info(self, user_repo, branch, prefer_cached=False): + """ + Fetches the raw GitHub API information about a readme + + :param user_repo: + The user/repo of the repository + + :param branch: + The branch to pull the readme from + + :param prefer_cached: + If a cached version of the info should be returned instead of making a new HTTP request + + :raises: + DownloaderException: when there is an error downloading + ClientException: when there is an error parsing the response + + :return: + A dict containing all of the info from the GitHub API, or None if no readme exists + """ + + query_string = urlencode({'ref': branch}) + readme_url = self._make_api_url(user_repo, '/readme?%s' % query_string) + try: + return self.fetch_json(readme_url, prefer_cached) + except (DownloaderException) as e: + if str(e).find('HTTP error 404') != -1: + return None + raise + + def _user_repo_branch(self, url): + """ + Extract the username/repo and branch name from the URL + + :param url: + The URL to extract the info from, in one of the forms: + https://github.com/{user}/{repo} + https://github.com/{user}/{repo}/tree/{branch} + + :return: + A tuple of (user/repo, branch name) or (None, None) if no match + """ + + branch = 'master' + branch_match = re.match('https?://github.com/[^/]+/[^/]+/tree/([^/]+)/?$', url) + if branch_match != None: + branch = branch_match.group(1) + + repo_match = re.match('https?://github.com/([^/]+/[^/]+)($|/.*$)', url) + if repo_match == None: + return (None, None) + + user_repo = repo_match.group(1) + return (user_repo, branch) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/json_api_client.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/json_api_client.py new file mode 100644 index 0000000..a847302 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/json_api_client.py @@ -0,0 +1,64 @@ +import json + +try: + # Python 3 + from urllib.parse import urlencode, urlparse +except (ImportError): + # Python 2 + from urllib import urlencode + from urlparse import urlparse + +from .client_exception import ClientException +from ..download_manager import downloader + + +class JSONApiClient(): + def __init__(self, settings): + self.settings = settings + + def fetch(self, url, prefer_cached=False): + """ + Retrieves the contents of a URL + + :param url: + The URL to download the content from + + :param prefer_cached: + If a cached copy of the content is preferred + + :return: The bytes/string + """ + + # If there are extra params for the domain name, add them + extra_params = self.settings.get('query_string_params') + domain_name = urlparse(url).netloc + if extra_params and domain_name in extra_params: + params = urlencode(extra_params[domain_name]) + joiner = '?%s' if url.find('?') == -1 else '&%s' + url += joiner % params + + with downloader(url, self.settings) as manager: + content = manager.fetch(url, 'Error downloading repository.', + prefer_cached) + return content + + def fetch_json(self, url, prefer_cached=False): + """ + Retrieves and parses the JSON from a URL + + :param url: + The URL to download the JSON from + + :param prefer_cached: + If a cached copy of the JSON is preferred + + :return: A dict or list from the JSON + """ + + repository_json = self.fetch(url, prefer_cached) + + try: + return json.loads(repository_json.decode('utf-8')) + except (ValueError): + error_string = u'Error parsing JSON from URL %s.' % url + raise ClientException(error_string) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/readme_client.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/readme_client.py new file mode 100644 index 0000000..47e2a7b --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/readme_client.py @@ -0,0 +1,83 @@ +import re +import os +import base64 + +try: + # Python 3 + from urllib.parse import urlencode +except (ImportError): + # Python 2 + from urllib import urlencode + +from .json_api_client import JSONApiClient +from ..downloaders.downloader_exception import DownloaderException + + +# Used to map file extensions to formats +_readme_formats = { + '.md': 'markdown', + '.mkd': 'markdown', + '.mdown': 'markdown', + '.markdown': 'markdown', + '.textile': 'textile', + '.creole': 'creole', + '.rst': 'rst' +} + + +class ReadmeClient(JSONApiClient): + + def readme_info(self, url): + """ + Retrieve the readme and info about it + + :param url: + The URL of the readme file + + :raises: + DownloaderException: if there is an error downloading the readme + ClientException: if there is an error parsing the response + + :return: + A dict with the following keys: + `filename` + `format` - `markdown`, `textile`, `creole`, `rst` or `txt` + `contents` - contents of the readme as str/unicode + """ + + contents = None + + # Try to grab the contents of a GitHub-based readme by grabbing the cached + # content of the readme API call + github_match = re.match('https://raw.github.com/([^/]+/[^/]+)/([^/]+)/readme(\.(md|mkd|mdown|markdown|textile|creole|rst|txt))?$', url, re.I) + if github_match: + user_repo = github_match.group(1) + branch = github_match.group(2) + + query_string = urlencode({'ref': branch}) + readme_json_url = 'https://api.github.com/repos/%s/readme?%s' % (user_repo, query_string) + try: + info = self.fetch_json(readme_json_url, prefer_cached=True) + contents = base64.b64decode(info['content']) + except (ValueError) as e: + pass + + if not contents: + contents = self.fetch(url) + + basename, ext = os.path.splitext(url) + format = 'txt' + ext = ext.lower() + if ext in _readme_formats: + format = _readme_formats[ext] + + try: + contents = contents.decode('utf-8') + except (UnicodeDecodeError) as e: + contents = contents.decode('cp1252', errors='replace') + + return { + 'filename': os.path.basename(url), + 'format': format, + 'contents': contents + } diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/cmd.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/cmd.py new file mode 100644 index 0000000..0d5c999 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/cmd.py @@ -0,0 +1,167 @@ +import os +import subprocess +import re + +if os.name == 'nt': + from ctypes import windll, create_unicode_buffer + +from .console_write import console_write +from .unicode import unicode_from_os +from .show_error import show_error + +try: + # Python 2 + str_cls = unicode +except (NameError): + # Python 3 + str_cls = str + + +def create_cmd(args, basename_binary=False): + """ + Takes an array of strings to be passed to subprocess.Popen and creates + a string that can be pasted into a terminal + + :param args: + The array containing the binary name/path and all arguments + + :param basename_binary: + If only the basename of the binary should be disabled instead of the full path + + :return: + The command string + """ + + if basename_binary: + args[0] = os.path.basename(args[0]) + + if os.name == 'nt': + return subprocess.list2cmdline(args) + else: + escaped_args = [] + for arg in args: + if re.search('^[a-zA-Z0-9/_^\\-\\.:=]+$', arg) == None: + arg = u"'" + arg.replace(u"'", u"'\\''") + u"'" + escaped_args.append(arg) + return u' '.join(escaped_args) + + +class Cli(object): + """ + Base class for running command line apps + + :param binary: + The full filesystem path to the executable for the version control + system. May be set to None to allow the code to try and find it. + """ + + cli_name = None + + def __init__(self, binary, debug): + self.binary = binary + self.debug = debug + + def execute(self, args, cwd, input=None): + """ + Creates a subprocess with the executable/args + + :param args: + A list of the executable path and all arguments to it + + :param cwd: + The directory in which to run the executable + + :param input: + The input text to send to the program + + :return: A string of the executable output + """ + + startupinfo = None + if os.name == 'nt': + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + + # Make sure the cwd is ascii + try: + cwd.encode('ascii') + except UnicodeEncodeError: + buf = create_unicode_buffer(512) + if windll.kernel32.GetShortPathNameW(cwd, buf, len(buf)): + cwd = buf.value + + if self.debug: + console_write(u"Trying to execute command %s" % create_cmd(args), True) + + try: + proc = subprocess.Popen(args, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + startupinfo=startupinfo, cwd=cwd) + + if input and isinstance(input, str_cls): + input = input.encode('utf-8') + output, _ = proc.communicate(input) + output = output.decode('utf-8') + output = output.replace('\r\n', '\n').rstrip(' \n\r') + + return output + + except (OSError) as e: + cmd = create_cmd(args) + error = unicode_from_os(e) + message = u"Error executing: %s\n%s\n\nTry checking your \"%s_binary\" setting?" % (cmd, error, self.cli_name) + show_error(message) + return False + + def find_binary(self, name): + """ + Locates the executable by looking in the PATH and well-known directories + + :param name: + The string filename of the executable + + :return: The filesystem path to the executable, or None if not found + """ + + if self.binary: + if self.debug: + error_string = u"Using \"%s_binary\" from settings \"%s\"" % ( + self.cli_name, self.binary) + console_write(error_string, True) + return self.binary + + # Try the path first + for dir_ in os.environ['PATH'].split(os.pathsep): + path = os.path.join(dir_, name) + if os.path.exists(path): + if self.debug: + console_write(u"Found %s at \"%s\"" % (self.cli_name, path), True) + return path + + # This is left in for backwards compatibility and for windows + # users who may have the binary, albeit in a common dir that may + # not be part of the PATH + if os.name == 'nt': + dirs = ['C:\\Program Files\\Git\\bin', + 'C:\\Program Files (x86)\\Git\\bin', + 'C:\\Program Files\\TortoiseGit\\bin', + 'C:\\Program Files\\Mercurial', + 'C:\\Program Files (x86)\\Mercurial', + 'C:\\Program Files (x86)\\TortoiseHg', + 'C:\\Program Files\\TortoiseHg', + 'C:\\cygwin\\bin'] + else: + # ST seems to launch with a minimal set of environmental variables + # on OS X, so we add some common paths for it + dirs = ['/usr/local/git/bin', '/usr/local/bin'] + + for dir_ in dirs: + path = os.path.join(dir_, name) + if os.path.exists(path): + if self.debug: + console_write(u"Found %s at \"%s\"" % (self.cli_name, path), True) + return path + + if self.debug: + console_write(u"Could not find %s on your machine" % self.cli_name, True) + return None diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/__init__.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/__init__.py new file mode 100644 index 0000000..dde03d4 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/__init__.py @@ -0,0 +1,39 @@ +import os + +from .add_channel_command import AddChannelCommand +from .add_repository_command import AddRepositoryCommand +from .create_binary_package_command import CreateBinaryPackageCommand +from .create_package_command import CreatePackageCommand +from .disable_package_command import DisablePackageCommand +from .discover_packages_command import DiscoverPackagesCommand +from .enable_package_command import EnablePackageCommand +from .grab_certs_command import GrabCertsCommand +from .install_package_command import InstallPackageCommand +from .list_packages_command import ListPackagesCommand +from .remove_package_command import RemovePackageCommand +from .upgrade_all_packages_command import UpgradeAllPackagesCommand +from .upgrade_package_command import UpgradePackageCommand +from .package_message_command import PackageMessageCommand + + +__all__ = [ + 'AddChannelCommand', + 'AddRepositoryCommand', + 'CreateBinaryPackageCommand', + 'CreatePackageCommand', + 'DisablePackageCommand', + 'DiscoverPackagesCommand', + 'EnablePackageCommand', + 'InstallPackageCommand', + 'ListPackagesCommand', + 'RemovePackageCommand', + 'UpgradeAllPackagesCommand', + 'UpgradePackageCommand', + 'PackageMessageCommand' +] + +# Windows uses the wininet downloader, so it doesn't use the CA cert bundle +# and thus does not need the ability to grab to CA certs. Additionally, +# there is no openssl.exe on Windows. +if os.name != 'nt': + __all__.append('GrabCertsCommand') diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/add_channel_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/add_channel_command.py new file mode 100644 index 0000000..5e1b8d1 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/add_channel_command.py @@ -0,0 +1,46 @@ +import re + +import sublime +import sublime_plugin + +from ..show_error import show_error + + +class AddChannelCommand(sublime_plugin.WindowCommand): + """ + A command to add a new channel (list of repositories) to the user's machine + """ + + def run(self): + self.window.show_input_panel('Channel JSON URL', '', + self.on_done, self.on_change, self.on_cancel) + + def on_done(self, input): + """ + Input panel handler - adds the provided URL as a channel + + :param input: + A string of the URL to the new channel + """ + + input = input.strip() + + if re.match('https?://', input, re.I) == None: + show_error(u"Unable to add the channel \"%s\" since it does not appear to be served via HTTP (http:// or https://)." % input) + return + + settings = sublime.load_settings('Package Control.sublime-settings') + channels = settings.get('channels', []) + if not channels: + channels = [] + channels.append(input) + settings.set('channels', channels) + sublime.save_settings('Package Control.sublime-settings') + sublime.status_message(('Channel %s successfully ' + + 'added') % input) + + def on_change(self, input): + pass + + def on_cancel(self): + pass diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/add_repository_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/add_repository_command.py new file mode 100644 index 0000000..3d04323 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/add_repository_command.py @@ -0,0 +1,46 @@ +import re + +import sublime +import sublime_plugin + +from ..show_error import show_error + + +class AddRepositoryCommand(sublime_plugin.WindowCommand): + """ + A command to add a new repository to the user's machine + """ + + def run(self): + self.window.show_input_panel('GitHub or BitBucket Web URL, or Custom' + + ' JSON Repository URL', '', self.on_done, + self.on_change, self.on_cancel) + + def on_done(self, input): + """ + Input panel handler - adds the provided URL as a repository + + :param input: + A string of the URL to the new repository + """ + + input = input.strip() + + if re.match('https?://', input, re.I) == None: + show_error(u"Unable to add the repository \"%s\" since it does not appear to be served via HTTP (http:// or https://)." % input) + return + + settings = sublime.load_settings('Package Control.sublime-settings') + repositories = settings.get('repositories', []) + if not repositories: + repositories = [] + repositories.append(input) + settings.set('repositories', repositories) + sublime.save_settings('Package Control.sublime-settings') + sublime.status_message('Repository %s successfully added' % input) + + def on_change(self, input): + pass + + def on_cancel(self): + pass diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/create_binary_package_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/create_binary_package_command.py new file mode 100644 index 0000000..491dd1c --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/create_binary_package_command.py @@ -0,0 +1,35 @@ +import sublime_plugin + +from ..package_creator import PackageCreator + + +class CreateBinaryPackageCommand(sublime_plugin.WindowCommand, PackageCreator): + """ + Command to create a binary .sublime-package file. Binary packages in + general exclude the .py source files and instead include the .pyc files. + Actual included and excluded files are controlled by settings. + """ + + def run(self): + self.show_panel() + + def on_done(self, picked): + """ + Quick panel user selection handler - processes the user package + selection and create the package file + + :param picked: + An integer of the 0-based package name index from the presented + list. -1 means the user cancelled. + """ + + if picked == -1: + return + package_name = self.packages[picked] + package_destination = self.get_package_destination() + + if self.manager.create_package(package_name, package_destination, + binary_package=True): + self.window.run_command('open_dir', {"dir": + package_destination, "file": package_name + + '.sublime-package'}) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/create_package_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/create_package_command.py new file mode 100644 index 0000000..8b0524a --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/create_package_command.py @@ -0,0 +1,32 @@ +import sublime_plugin + +from ..package_creator import PackageCreator + + +class CreatePackageCommand(sublime_plugin.WindowCommand, PackageCreator): + """ + Command to create a regular .sublime-package file + """ + + def run(self): + self.show_panel() + + def on_done(self, picked): + """ + Quick panel user selection handler - processes the user package + selection and create the package file + + :param picked: + An integer of the 0-based package name index from the presented + list. -1 means the user cancelled. + """ + + if picked == -1: + return + package_name = self.packages[picked] + package_destination = self.get_package_destination() + + if self.manager.create_package(package_name, package_destination): + self.window.run_command('open_dir', {"dir": + package_destination, "file": package_name + + '.sublime-package'}) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/disable_package_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/disable_package_command.py new file mode 100644 index 0000000..d5ebd97 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/disable_package_command.py @@ -0,0 +1,48 @@ +import sublime +import sublime_plugin + +from ..show_error import show_error +from ..package_manager import PackageManager +from ..preferences_filename import preferences_filename + + +class DisablePackageCommand(sublime_plugin.WindowCommand): + """ + A command that adds a package to Sublime Text's ignored packages list + """ + + def run(self): + manager = PackageManager() + packages = manager.list_all_packages() + self.settings = sublime.load_settings(preferences_filename()) + ignored = self.settings.get('ignored_packages') + if not ignored: + ignored = [] + self.package_list = list(set(packages) - set(ignored)) + self.package_list.sort() + if not self.package_list: + show_error('There are no enabled packages to disable.') + return + self.window.show_quick_panel(self.package_list, self.on_done) + + def on_done(self, picked): + """ + Quick panel user selection handler - disables the selected package + + :param picked: + An integer of the 0-based package name index from the presented + list. -1 means the user cancelled. + """ + + if picked == -1: + return + package = self.package_list[picked] + ignored = self.settings.get('ignored_packages') + if not ignored: + ignored = [] + ignored.append(package) + self.settings.set('ignored_packages', ignored) + sublime.save_settings(preferences_filename()) + sublime.status_message(('Package %s successfully added to list of ' + + 'disabled packages - restarting Sublime Text may be required') % + package) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/discover_packages_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/discover_packages_command.py new file mode 100644 index 0000000..78d9812 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/discover_packages_command.py @@ -0,0 +1,11 @@ +import sublime_plugin + + +class DiscoverPackagesCommand(sublime_plugin.WindowCommand): + """ + A command that opens the community package list webpage + """ + + def run(self): + self.window.run_command('open_url', + {'url': 'http://wbond.net/sublime_packages/community'}) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/enable_package_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/enable_package_command.py new file mode 100644 index 0000000..2e5e6d1 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/enable_package_command.py @@ -0,0 +1,40 @@ +import sublime +import sublime_plugin + +from ..show_error import show_error +from ..preferences_filename import preferences_filename + + +class EnablePackageCommand(sublime_plugin.WindowCommand): + """ + A command that removes a package from Sublime Text's ignored packages list + """ + + def run(self): + self.settings = sublime.load_settings(preferences_filename()) + self.disabled_packages = self.settings.get('ignored_packages') + self.disabled_packages.sort() + if not self.disabled_packages: + show_error('There are no disabled packages to enable.') + return + self.window.show_quick_panel(self.disabled_packages, self.on_done) + + def on_done(self, picked): + """ + Quick panel user selection handler - enables the selected package + + :param picked: + An integer of the 0-based package name index from the presented + list. -1 means the user cancelled. + """ + + if picked == -1: + return + package = self.disabled_packages[picked] + ignored = self.settings.get('ignored_packages') + self.settings.set('ignored_packages', + list(set(ignored) - set([package]))) + sublime.save_settings(preferences_filename()) + sublime.status_message(('Package %s successfully removed from list ' + + 'of disabled packages - restarting Sublime Text may be required') % + package) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/existing_packages_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/existing_packages_command.py new file mode 100644 index 0000000..78615d6 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/existing_packages_command.py @@ -0,0 +1,69 @@ +import os +import re + +import sublime + +from ..package_manager import PackageManager + + +class ExistingPackagesCommand(): + """ + Allows listing installed packages and their current version + """ + + def __init__(self): + self.manager = PackageManager() + + def make_package_list(self, action=''): + """ + Returns a list of installed packages suitable for displaying in the + quick panel. + + :param action: + An action to display at the beginning of the third element of the + list returned for each package + + :return: + A list of lists, each containing three strings: + 0 - package name + 1 - package description + 2 - [action] installed version; package url + """ + + packages = self.manager.list_packages() + + if action: + action += ' ' + + package_list = [] + for package in sorted(packages, key=lambda s: s.lower()): + package_entry = [package] + metadata = self.manager.get_metadata(package) + package_dir = os.path.join(sublime.packages_path(), package) + + description = metadata.get('description') + if not description: + description = 'No description provided' + package_entry.append(description) + + version = metadata.get('version') + if not version and os.path.exists(os.path.join(package_dir, + '.git')): + installed_version = 'git repository' + elif not version and os.path.exists(os.path.join(package_dir, + '.hg')): + installed_version = 'hg repository' + else: + installed_version = 'v' + version if version else \ + 'unknown version' + + url = metadata.get('url') + if url: + url = '; ' + re.sub('^https?://', '', url) + else: + url = '' + + package_entry.append(action + installed_version + url) + package_list.append(package_entry) + + return package_list diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/grab_certs_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/grab_certs_command.py new file mode 100644 index 0000000..4eb77e0 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/grab_certs_command.py @@ -0,0 +1,109 @@ +import os +import re +import socket +import threading + +try: + # Python 3 + from urllib.parse import urlparse +except (ImportError): + # Python 2 + from urlparse import urlparse + +import sublime +import sublime_plugin + +from ..show_error import show_error +from ..open_compat import open_compat +from ..ca_certs import find_root_ca_cert +from ..thread_progress import ThreadProgress +from ..package_manager import PackageManager + + +class GrabCertsCommand(sublime_plugin.WindowCommand): + """ + A command that extracts the CA certs for a domain name, allowing a user to + fetch packages from sources other than those used by the default channel + """ + + def run(self): + panel = self.window.show_input_panel('Domain Name', 'example.com', self.on_done, + None, None) + panel.sel().add(sublime.Region(0, panel.size())) + + def on_done(self, domain): + """ + Input panel handler - grabs the CA certs for the domain name presented + + :param domain: + A string of the domain name + """ + + domain = domain.strip() + + # Make sure the user enters something + if domain == '': + show_error(u"Please enter a domain name, or press cancel") + self.run() + return + + # If the user inputs a URL, extract the domain name + if domain.find('/') != -1: + parts = urlparse(domain) + if parts.hostname: + domain = parts.hostname + + # Allow _ even though it technically isn't valid, this is really + # just to try and prevent people from typing in gibberish + if re.match('^(?:[a-zA-Z0-9]+(?:[\-_]*[a-zA-Z0-9]+)*\.)+[a-zA-Z]{2,6}$', domain, re.I) == None: + show_error(u"Unable to get the CA certs for \"%s\" since it does not appear to be a validly formed domain name" % domain) + return + + # Make sure it is a real domain + try: + socket.gethostbyname(domain) + except (socket.gaierror) as e: + error = unicode_from_os(e) + show_error(u"Error trying to lookup \"%s\":\n\n%s" % (domain, error)) + return + + manager = PackageManager() + + thread = GrabCertsThread(manager.settings, domain) + thread.start() + ThreadProgress(thread, 'Grabbing CA certs for %s' % domain, + 'CA certs for %s added to settings' % domain) + + +class GrabCertsThread(threading.Thread): + """ + A thread to run openssl so that the Sublime Text UI does not become frozen + """ + + def __init__(self, settings, domain): + self.settings = settings + self.domain = domain + threading.Thread.__init__(self) + + def run(self): + cert, cert_hash = find_root_ca_cert(self.settings, self.domain) + + certs_dir = os.path.join(sublime.packages_path(), 'User', + 'Package Control.ca-certs') + if not os.path.exists(certs_dir): + os.mkdir(certs_dir) + + cert_path = os.path.join(certs_dir, self.domain + '-ca.crt') + with open_compat(cert_path, 'w') as f: + f.write(cert) + + def save_certs(): + settings = sublime.load_settings('Package Control.sublime-settings') + certs = settings.get('certs', {}) + if not certs: + certs = {} + certs[self.domain] = [cert_hash, cert_path] + settings.set('certs', certs) + sublime.save_settings('Package Control.sublime-settings') + + sublime.set_timeout(save_certs, 10) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/install_package_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/install_package_command.py new file mode 100644 index 0000000..bbe9031 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/install_package_command.py @@ -0,0 +1,50 @@ +import threading + +import sublime +import sublime_plugin + +from ..show_error import show_error +from ..package_installer import PackageInstaller +from ..thread_progress import ThreadProgress + + +class InstallPackageCommand(sublime_plugin.WindowCommand): + """ + A command that presents the list of available packages and allows the + user to pick one to install. + """ + + def run(self): + thread = InstallPackageThread(self.window) + thread.start() + ThreadProgress(thread, 'Loading repositories', '') + + +class InstallPackageThread(threading.Thread, PackageInstaller): + """ + A thread to run the action of retrieving available packages in. Uses the + default PackageInstaller.on_done quick panel handler. + """ + + def __init__(self, window): + """ + :param window: + An instance of :class:`sublime.Window` that represents the Sublime + Text window to show the available package list in. + """ + + self.window = window + self.completion_type = 'installed' + threading.Thread.__init__(self) + PackageInstaller.__init__(self) + + def run(self): + self.package_list = self.make_package_list(['upgrade', 'downgrade', + 'reinstall', 'pull', 'none']) + + def show_quick_panel(): + if not self.package_list: + show_error('There are no packages available for installation') + return + self.window.show_quick_panel(self.package_list, self.on_done) + sublime.set_timeout(show_quick_panel, 10) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/list_packages_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/list_packages_command.py new file mode 100644 index 0000000..84c57e4 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/list_packages_command.py @@ -0,0 +1,63 @@ +import threading +import os + +import sublime +import sublime_plugin + +from ..show_error import show_error +from .existing_packages_command import ExistingPackagesCommand + + +class ListPackagesCommand(sublime_plugin.WindowCommand): + """ + A command that shows a list of all installed packages in the quick panel + """ + + def run(self): + ListPackagesThread(self.window).start() + + +class ListPackagesThread(threading.Thread, ExistingPackagesCommand): + """ + A thread to prevent the listing of existing packages from freezing the UI + """ + + def __init__(self, window): + """ + :param window: + An instance of :class:`sublime.Window` that represents the Sublime + Text window to show the list of installed packages in. + """ + + self.window = window + threading.Thread.__init__(self) + ExistingPackagesCommand.__init__(self) + + def run(self): + self.package_list = self.make_package_list() + + def show_quick_panel(): + if not self.package_list: + show_error('There are no packages to list') + return + self.window.show_quick_panel(self.package_list, self.on_done) + sublime.set_timeout(show_quick_panel, 10) + + def on_done(self, picked): + """ + Quick panel user selection handler - opens the homepage for any + selected package in the user's browser + + :param picked: + An integer of the 0-based package name index from the presented + list. -1 means the user cancelled. + """ + + if picked == -1: + return + package_name = self.package_list[picked][0] + + def open_dir(): + self.window.run_command('open_dir', + {"dir": os.path.join(sublime.packages_path(), package_name)}) + sublime.set_timeout(open_dir, 10) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/package_message_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/package_message_command.py new file mode 100644 index 0000000..6e083df --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/package_message_command.py @@ -0,0 +1,11 @@ +import sublime +import sublime_plugin + + +class PackageMessageCommand(sublime_plugin.TextCommand): + """ + A command to write a package message to the Package Control messaging buffer + """ + + def run(self, edit, string=''): + self.view.insert(edit, self.view.size(), string) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/remove_package_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/remove_package_command.py new file mode 100644 index 0000000..df0350c --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/remove_package_command.py @@ -0,0 +1,88 @@ +import threading + +import sublime +import sublime_plugin + +from ..show_error import show_error +from .existing_packages_command import ExistingPackagesCommand +from ..preferences_filename import preferences_filename +from ..thread_progress import ThreadProgress + + +class RemovePackageCommand(sublime_plugin.WindowCommand, + ExistingPackagesCommand): + """ + A command that presents a list of installed packages, allowing the user to + select one to remove + """ + + def __init__(self, window): + """ + :param window: + An instance of :class:`sublime.Window` that represents the Sublime + Text window to show the list of installed packages in. + """ + + self.window = window + ExistingPackagesCommand.__init__(self) + + def run(self): + self.package_list = self.make_package_list('remove') + if not self.package_list: + show_error('There are no packages that can be removed.') + return + self.window.show_quick_panel(self.package_list, self.on_done) + + def on_done(self, picked): + """ + Quick panel user selection handler - deletes the selected package + + :param picked: + An integer of the 0-based package name index from the presented + list. -1 means the user cancelled. + """ + + if picked == -1: + return + package = self.package_list[picked][0] + + settings = sublime.load_settings(preferences_filename()) + ignored = settings.get('ignored_packages') + if not ignored: + ignored = [] + + # Don't disable Package Control so it does not get stuck disabled + if package != 'Package Control': + if not package in ignored: + ignored.append(package) + settings.set('ignored_packages', ignored) + sublime.save_settings(preferences_filename()) + ignored.remove(package) + + thread = RemovePackageThread(self.manager, package, + ignored) + thread.start() + ThreadProgress(thread, 'Removing package %s' % package, + 'Package %s successfully removed' % package) + + +class RemovePackageThread(threading.Thread): + """ + A thread to run the remove package operation in so that the Sublime Text + UI does not become frozen + """ + + def __init__(self, manager, package, ignored): + self.manager = manager + self.package = package + self.ignored = ignored + threading.Thread.__init__(self) + + def run(self): + self.result = self.manager.remove_package(self.package) + + def unignore_package(): + settings = sublime.load_settings(preferences_filename()) + settings.set('ignored_packages', self.ignored) + sublime.save_settings(preferences_filename()) + sublime.set_timeout(unignore_package, 10) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/upgrade_all_packages_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/upgrade_all_packages_command.py new file mode 100644 index 0000000..a4a730d --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/upgrade_all_packages_command.py @@ -0,0 +1,77 @@ +import time +import threading + +import sublime +import sublime_plugin + +from ..thread_progress import ThreadProgress +from ..package_installer import PackageInstaller, PackageInstallerThread +from ..package_renamer import PackageRenamer + + +class UpgradeAllPackagesCommand(sublime_plugin.WindowCommand): + """ + A command to automatically upgrade all installed packages that are + upgradable. + """ + + def run(self): + package_renamer = PackageRenamer() + package_renamer.load_settings() + + thread = UpgradeAllPackagesThread(self.window, package_renamer) + thread.start() + ThreadProgress(thread, 'Loading repositories', '') + + +class UpgradeAllPackagesThread(threading.Thread, PackageInstaller): + """ + A thread to run the action of retrieving upgradable packages in. + """ + + def __init__(self, window, package_renamer): + self.window = window + self.package_renamer = package_renamer + self.completion_type = 'upgraded' + threading.Thread.__init__(self) + PackageInstaller.__init__(self) + + def run(self): + self.package_renamer.rename_packages(self) + package_list = self.make_package_list(['install', 'reinstall', 'none']) + + disabled_packages = [] + + def do_upgrades(): + # Pause so packages can be disabled + time.sleep(0.5) + + # We use a function to generate the on-complete lambda because if + # we don't, the lambda will bind to info at the current scope, and + # thus use the last value of info from the loop + def make_on_complete(name): + return lambda: self.reenable_package(name) + + for info in package_list: + if info[0] in disabled_packages: + on_complete = make_on_complete(info[0]) + else: + on_complete = None + thread = PackageInstallerThread(self.manager, info[0], + on_complete) + thread.start() + ThreadProgress(thread, 'Upgrading package %s' % info[0], + 'Package %s successfully %s' % (info[0], + self.completion_type)) + + # Disabling a package means changing settings, which can only be done + # in the main thread. We then create a new background thread so that + # the upgrade process does not block the UI. + def disable_packages(): + package_names = [] + for info in package_list: + package_names.append(info[0]) + disabled_packages.extend(self.disable_packages(package_names)) + threading.Thread(target=do_upgrades).start() + + sublime.set_timeout(disable_packages, 1) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/upgrade_package_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/upgrade_package_command.py new file mode 100644 index 0000000..6c478e6 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/upgrade_package_command.py @@ -0,0 +1,81 @@ +import threading + +import sublime +import sublime_plugin + +from ..show_error import show_error +from ..thread_progress import ThreadProgress +from ..package_installer import PackageInstaller, PackageInstallerThread +from ..package_renamer import PackageRenamer + + +class UpgradePackageCommand(sublime_plugin.WindowCommand): + """ + A command that presents the list of installed packages that can be upgraded + """ + + def run(self): + package_renamer = PackageRenamer() + package_renamer.load_settings() + + thread = UpgradePackageThread(self.window, package_renamer) + thread.start() + ThreadProgress(thread, 'Loading repositories', '') + + +class UpgradePackageThread(threading.Thread, PackageInstaller): + """ + A thread to run the action of retrieving upgradable packages in. + """ + + def __init__(self, window, package_renamer): + """ + :param window: + An instance of :class:`sublime.Window` that represents the Sublime + Text window to show the list of upgradable packages in. + + :param package_renamer: + An instance of :class:`PackageRenamer` + """ + self.window = window + self.package_renamer = package_renamer + self.completion_type = 'upgraded' + threading.Thread.__init__(self) + PackageInstaller.__init__(self) + + def run(self): + self.package_renamer.rename_packages(self) + + self.package_list = self.make_package_list(['install', 'reinstall', + 'none']) + + def show_quick_panel(): + if not self.package_list: + show_error('There are no packages ready for upgrade') + return + self.window.show_quick_panel(self.package_list, self.on_done) + sublime.set_timeout(show_quick_panel, 10) + + def on_done(self, picked): + """ + Quick panel user selection handler - disables a package, upgrades it, + then re-enables the package + + :param picked: + An integer of the 0-based package name index from the presented + list. -1 means the user cancelled. + """ + + if picked == -1: + return + name = self.package_list[picked][0] + + if name in self.disable_packages(name): + on_complete = lambda: self.reenable_package(name) + else: + on_complete = None + + thread = PackageInstallerThread(self.manager, name, on_complete) + thread.start() + ThreadProgress(thread, 'Upgrading package %s' % name, + 'Package %s successfully %s' % (name, self.completion_type)) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/console_write.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/console_write.py new file mode 100644 index 0000000..5fb0796 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/console_write.py @@ -0,0 +1,20 @@ +import sys + + +def console_write(string, prefix=False): + """ + Writes a value to the Sublime Text console, encoding unicode to utf-8 first + + :param string: + The value to write + + :param prefix: + If the string "Package Control: " should be prefixed to the string + """ + + if sys.version_info < (3,): + if isinstance(string, unicode): + string = string.encode('UTF-8') + if prefix: + sys.stdout.write('Package Control: ') + print(string) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/download_manager.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/download_manager.py new file mode 100644 index 0000000..a4d028d --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/download_manager.py @@ -0,0 +1,231 @@ +import sys +import re +import socket +from threading import Lock, Timer +from contextlib import contextmanager + +try: + # Python 3 + from urllib.parse import urlparse +except (ImportError): + # Python 2 + from urlparse import urlparse + +from . import __version__ + +from .show_error import show_error +from .console_write import console_write +from .cache import set_cache, get_cache +from .unicode import unicode_from_os + +from .downloaders import DOWNLOADERS +from .downloaders.binary_not_found_error import BinaryNotFoundError +from .downloaders.rate_limit_exception import RateLimitException +from .downloaders.no_ca_cert_exception import NoCaCertException +from .downloaders.downloader_exception import DownloaderException +from .http_cache import HttpCache + + +# A dict of domains - each points to a list of downloaders +_managers = {} + +# How many managers are currently checked out +_in_use = 0 + +# Make sure connection management doesn't run into threading issues +_lock = Lock() + +# A timer used to disconnect all managers after a period of no usage +_timer = None + + +@contextmanager +def downloader(url, settings): + try: + manager = _grab(url, settings) + yield manager + + finally: + _release(url, manager) + + +def _grab(url, settings): + global _managers, _lock, _in_use, _timer + + _lock.acquire() + try: + if _timer: + _timer.cancel() + _timer = None + + hostname = urlparse(url).hostname.lower() + if hostname not in _managers: + _managers[hostname] = [] + + if not _managers[hostname]: + _managers[hostname].append(DownloadManager(settings)) + + _in_use += 1 + + return _managers[hostname].pop() + + finally: + _lock.release() + + +def _release(url, manager): + global _managers, _lock, _in_use, _timer + + _lock.acquire() + try: + hostname = urlparse(url).hostname.lower() + _managers[hostname].insert(0, manager) + + _in_use -= 1 + + if _timer: + _timer.cancel() + _timer = None + + if _in_use == 0: + _timer = Timer(5.0, close_all_connections) + _timer.start() + + finally: + _lock.release() + + +def close_all_connections(): + global _managers, _lock, _in_use, _timer + + _lock.acquire() + try: + if _timer: + _timer.cancel() + _timer = None + + for domain, managers in _managers.items(): + for manager in managers: + manager.close() + _managers = {} + + finally: + _lock.release() + + +class DownloadManager(object): + def __init__(self, settings): + # Cache the downloader for re-use + self.downloader = None + + user_agent = settings.get('user_agent') + if user_agent and user_agent.find('%s') != -1: + settings['user_agent'] = user_agent % __version__ + + self.settings = settings + if settings.get('http_cache'): + cache_length = settings.get('http_cache_length', 604800) + self.settings['cache'] = HttpCache(cache_length) + + def close(self): + if self.downloader: + self.downloader.close() + self.downloader = None + + def fetch(self, url, error_message, prefer_cached=False): + """ + Downloads a URL and returns the contents + + :param url: + The string URL to download + + :param error_message: + The error message to include if the download fails + + :param prefer_cached: + If cached version of the URL content is preferred over a new request + + :raises: + DownloaderException: if there was an error downloading the URL + + :return: + The string contents of the URL + """ + + is_ssl = re.search('^https://', url) != None + + # Make sure we have a downloader, and it supports SSL if we need it + if not self.downloader or (is_ssl and not self.downloader.supports_ssl()): + for downloader_class in DOWNLOADERS: + try: + downloader = downloader_class(self.settings) + if is_ssl and not downloader.supports_ssl(): + continue + self.downloader = downloader + break + except (BinaryNotFoundError): + pass + + if not self.downloader: + error_string = u'Unable to download %s due to no ssl module available and no capable program found. Please install curl or wget.' % url + show_error(error_string) + raise DownloaderException(error_string) + + url = url.replace(' ', '%20') + hostname = urlparse(url).hostname + if hostname: + hostname = hostname.lower() + timeout = self.settings.get('timeout', 3) + + rate_limited_domains = get_cache('rate_limited_domains', []) + no_ca_cert_domains = get_cache('no_ca_cert_domains', []) + + if self.settings.get('debug'): + try: + ip = socket.gethostbyname(hostname) + except (socket.gaierror) as e: + ip = unicode_from_os(e) + except (TypeError) as e: + ip = None + + console_write(u"Download Debug", True) + console_write(u" URL: %s" % url) + console_write(u" Resolved IP: %s" % ip) + console_write(u" Timeout: %s" % str(timeout)) + + if hostname in rate_limited_domains: + error_string = u"Skipping due to hitting rate limit for %s" % hostname + if self.settings.get('debug'): + console_write(u" %s" % error_string) + raise DownloaderException(error_string) + + if hostname in no_ca_cert_domains: + error_string = u" Skipping since there are no CA certs for %s" % hostname + if self.settings.get('debug'): + console_write(u" %s" % error_string) + raise DownloaderException(error_string) + + try: + return self.downloader.download(url, error_message, timeout, 3, prefer_cached) + + except (RateLimitException) as e: + + rate_limited_domains.append(hostname) + set_cache('rate_limited_domains', rate_limited_domains, self.settings.get('cache_length')) + + error_string = (u'Hit rate limit of %s for %s, skipping all futher ' + + u'download requests for this domain') % (e.limit, e.domain) + console_write(error_string, True) + raise + + except (NoCaCertException) as e: + + no_ca_cert_domains.append(hostname) + set_cache('no_ca_cert_domains', no_ca_cert_domains, self.settings.get('cache_length')) + + error_string = (u'No CA certs available for %s, skipping all futher ' + + u'download requests for this domain. If you are on a trusted ' + + u'network, you can add the CA certs by running the "Grab ' + + u'CA Certs" command from the command palette.') % e.domain + console_write(error_string, True) + raise diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/__init__.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/__init__.py new file mode 100644 index 0000000..fb68aef --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/__init__.py @@ -0,0 +1,11 @@ +import os + +if os.name == 'nt': + from .wininet_downloader import WinINetDownloader + DOWNLOADERS = [WinINetDownloader] + +else: + from .urllib_downloader import UrlLibDownloader + from .curl_downloader import CurlDownloader + from .wget_downloader import WgetDownloader + DOWNLOADERS = [UrlLibDownloader, CurlDownloader, WgetDownloader] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/background_downloader.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/background_downloader.py new file mode 100644 index 0000000..250d2de --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/background_downloader.py @@ -0,0 +1,62 @@ +import threading + + +class BackgroundDownloader(threading.Thread): + """ + Downloads information from one or more URLs in the background. + Normal usage is to use one BackgroundDownloader per domain name. + + :param settings: + A dict containing at least the following fields: + `cache_length`, + `debug`, + `timeout`, + `user_agent`, + `http_proxy`, + `https_proxy`, + `proxy_username`, + `proxy_password` + + :param providers: + An array of providers that can download the URLs + """ + + def __init__(self, settings, providers): + self.settings = settings + self.urls = [] + self.providers = providers + self.used_providers = {} + threading.Thread.__init__(self) + + def add_url(self, url): + """ + Adds a URL to the list to download + + :param url: + The URL to download info about + """ + + self.urls.append(url) + + def get_provider(self, url): + """ + Returns the provider for the URL specified + + :param url: + The URL to return the provider for + + :return: + The provider object for the URL + """ + + return self.used_providers[url] + + def run(self): + for url in self.urls: + for provider_class in self.providers: + if provider_class.match_url(url): + provider = provider_class(url, self.settings) + break + + provider.prefetch() + self.used_providers[url] = provider diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/binary_not_found_error.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/binary_not_found_error.py new file mode 100644 index 0000000..a7955b9 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/binary_not_found_error.py @@ -0,0 +1,4 @@ +class BinaryNotFoundError(Exception): + """If a necessary executable is not found in the PATH on the system""" + + pass diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/caching_downloader.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/caching_downloader.py new file mode 100644 index 0000000..ab3d71f --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/caching_downloader.py @@ -0,0 +1,185 @@ +import sys +import re +import json +import hashlib + +from ..console_write import console_write + + +class CachingDownloader(object): + """ + A base downloader that will use a caching backend to cache HTTP requests + and make conditional requests. + """ + + def add_conditional_headers(self, url, headers): + """ + Add `If-Modified-Since` and `If-None-Match` headers to a request if a + cached copy exists + + :param headers: + A dict with the request headers + + :return: + The request headers dict, possibly with new headers added + """ + + if not self.settings.get('cache'): + return headers + + info_key = self.generate_key(url, '.info') + info_json = self.settings['cache'].get(info_key) + + if not info_json: + return headers + + # Make sure we have the cached content to use if we get a 304 + key = self.generate_key(url) + if not self.settings['cache'].has(key): + return headers + + try: + info = json.loads(info_json.decode('utf-8')) + except ValueError: + return headers + + etag = info.get('etag') + if etag: + headers['If-None-Match'] = etag + + last_modified = info.get('last-modified') + if last_modified: + headers['If-Modified-Since'] = last_modified + + return headers + + def cache_result(self, method, url, status, headers, content): + """ + Processes a request result, either caching the result, or returning + the cached version of the url. + + :param method: + The HTTP method used for the request + + :param url: + The url of the request + + :param status: + The numeric response status of the request + + :param headers: + A dict of reponse headers, with keys being lowercase + + :param content: + The response content + + :return: + The response content + """ + + debug = self.settings.get('debug', False) + + if not self.settings.get('cache'): + if debug: + console_write(u"Skipping cache since there is no cache object", True) + return content + + if method.lower() != 'get': + if debug: + console_write(u"Skipping cache since the HTTP method != GET", True) + return content + + status = int(status) + + # Don't do anything unless it was successful or not modified + if status not in [200, 304]: + if debug: + console_write(u"Skipping cache since the HTTP status code not one of: 200, 304", True) + return content + + key = self.generate_key(url) + + if status == 304: + cached_content = self.settings['cache'].get(key) + if cached_content: + if debug: + console_write(u"Using cached content for %s" % url, True) + return cached_content + + # If we got a 304, but did not have the cached content + # stop here so we don't cache an empty response + return content + + # If we got here, the status is 200 + + # Respect some basic cache control headers + cache_control = headers.get('cache-control', '') + if cache_control: + fields = re.split(',\s*', cache_control) + for field in fields: + if field == 'no-store': + return content + + # Don't ever cache zip/binary files for the sake of hard drive space + if headers.get('content-type') in ['application/zip', 'application/octet-stream']: + if debug: + console_write(u"Skipping cache since the response is a zip file", True) + return content + + etag = headers.get('etag') + last_modified = headers.get('last-modified') + + if not etag and not last_modified: + return content + + struct = {'etag': etag, 'last-modified': last_modified} + struct_json = json.dumps(struct, indent=4) + + info_key = self.generate_key(url, '.info') + if debug: + console_write(u"Caching %s in %s" % (url, key), True) + + self.settings['cache'].set(info_key, struct_json.encode('utf-8')) + self.settings['cache'].set(key, content) + + return content + + def generate_key(self, url, suffix=''): + """ + Generates a key to store the cache under + + :param url: + The URL being cached + + :param suffix: + A string to append to the key + + :return: + A string key for the URL + """ + + if sys.version_info >= (3,) or isinstance(url, unicode): + url = url.encode('utf-8') + + key = hashlib.md5(url).hexdigest() + return key + suffix + + def retrieve_cached(self, url): + """ + Tries to return the cached content for a URL + + :param url: + The URL to get the cached content for + + :return: + The cached content + """ + + key = self.generate_key(url) + if not self.settings['cache'].has(key): + return False + + if self.settings.get('debug'): + console_write(u"Using cached content for %s" % url, True) + + return self.settings['cache'].get(key) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/cert_provider.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/cert_provider.py new file mode 100644 index 0000000..f8c8c3b --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/cert_provider.py @@ -0,0 +1,203 @@ +import os +import re +import json + +import sublime + +from ..console_write import console_write +from ..open_compat import open_compat, read_compat +from ..package_io import read_package_file +from ..cache import get_cache +from ..ca_certs import get_system_ca_bundle_path +from .no_ca_cert_exception import NoCaCertException +from .downloader_exception import DownloaderException + + +class CertProvider(object): + """ + A base downloader that provides access to a ca-bundle for validating + SSL certificates. + """ + + def check_certs(self, domain, timeout): + """ + Ensures that the SSL CA cert for a domain is present on the machine + + :param domain: + The domain to ensure there is a CA cert for + + :param timeout: + The int timeout for downloading the CA cert from the channel + + :raises: + NoCaCertException: when a suitable CA cert could not be found + + :return: + The CA cert bundle path + """ + + # Try to use the system CA bundle + ca_bundle_path = get_system_ca_bundle_path(self.settings) + if ca_bundle_path: + return ca_bundle_path + + # If the system bundle did not work, fall back to our CA distribution + # system. Hopefully this will be going away soon. + if self.settings.get('debug'): + console_write(u'Unable to find system CA cert bundle, falling back to certs provided by Package Control') + + cert_match = False + + certs_list = get_cache('*.certs', self.settings.get('certs', {})) + + ca_bundle_path = os.path.join(sublime.packages_path(), 'User', 'Package Control.ca-bundle') + if not os.path.exists(ca_bundle_path) or os.stat(ca_bundle_path).st_size == 0: + bundle_contents = read_package_file('Package Control', 'Package Control.ca-bundle', True) + if not bundle_contents: + raise NoCaCertException(u'Unable to copy distributed Package Control.ca-bundle', domain) + with open_compat(ca_bundle_path, 'wb') as f: + f.write(bundle_contents) + + cert_info = certs_list.get(domain) + if cert_info: + cert_match = self.locate_cert(cert_info[0], + cert_info[1], domain, timeout) + + wildcard_info = certs_list.get('*') + if wildcard_info: + cert_match = self.locate_cert(wildcard_info[0], + wildcard_info[1], domain, timeout) or cert_match + + if not cert_match: + raise NoCaCertException(u'No CA certs available for %s' % domain, domain) + + return ca_bundle_path + + def locate_cert(self, cert_id, location, domain, timeout): + """ + Makes sure the SSL cert specified has been added to the CA cert + bundle that is present on the machine + + :param cert_id: + The identifier for CA cert(s). For those provided by the channel + system, this will be an md5 of the contents of the cert(s). For + user-provided certs, this is something they provide. + + :param location: + An http(s) URL, or absolute filesystem path to the CA cert(s) + + :param domain: + The domain to ensure there is a CA cert for + + :param timeout: + The int timeout for downloading the CA cert from the channel + + :return: + If the cert specified (by cert_id) is present on the machine and + part of the Package Control.ca-bundle file in the User package folder + """ + + ca_list_path = os.path.join(sublime.packages_path(), 'User', 'Package Control.ca-list') + if not os.path.exists(ca_list_path) or os.stat(ca_list_path).st_size == 0: + list_contents = read_package_file('Package Control', 'Package Control.ca-list') + if not list_contents: + raise NoCaCertException(u'Unable to copy distributed Package Control.ca-list', domain) + with open_compat(ca_list_path, 'w') as f: + f.write(list_contents) + + ca_certs = [] + with open_compat(ca_list_path, 'r') as f: + ca_certs = json.loads(read_compat(f)) + + if not cert_id in ca_certs: + if str(location) != '': + if re.match('^https?://', location): + contents = self.download_cert(cert_id, location, domain, + timeout) + else: + contents = self.load_cert(cert_id, location, domain) + if contents: + self.save_cert(cert_id, contents) + return True + return False + return True + + def download_cert(self, cert_id, url, domain, timeout): + """ + Downloads CA cert(s) from a URL + + :param cert_id: + The identifier for CA cert(s). For those provided by the channel + system, this will be an md5 of the contents of the cert(s). For + user-provided certs, this is something they provide. + + :param url: + An http(s) URL to the CA cert(s) + + :param domain: + The domain to ensure there is a CA cert for + + :param timeout: + The int timeout for downloading the CA cert from the channel + + :return: + The contents of the CA cert(s) + """ + + cert_downloader = self.__class__(self.settings) + if self.settings.get('debug'): + console_write(u"Downloading CA cert for %s from \"%s\"" % (domain, url), True) + return cert_downloader.download(url, + 'Error downloading CA certs for %s.' % domain, timeout, 1) + + def load_cert(self, cert_id, path, domain): + """ + Copies CA cert(s) from a file path + + :param cert_id: + The identifier for CA cert(s). For those provided by the channel + system, this will be an md5 of the contents of the cert(s). For + user-provided certs, this is something they provide. + + :param path: + The absolute filesystem path to a file containing the CA cert(s) + + :param domain: + The domain name the cert is for + + :return: + The contents of the CA cert(s) + """ + + if os.path.exists(path): + if self.settings.get('debug'): + console_write(u"Copying CA cert for %s from \"%s\"" % (domain, path), True) + with open_compat(path, 'rb') as f: + return f.read() + else: + raise NoCaCertException(u"Unable to find CA cert for %s at \"%s\"" % (domain, path), domain) + + def save_cert(self, cert_id, contents): + """ + Saves CA cert(s) to the Package Control.ca-bundle + + :param cert_id: + The identifier for CA cert(s). For those provided by the channel + system, this will be an md5 of the contents of the cert(s). For + user-provided certs, this is something they provide. + + :param contents: + The contents of the CA cert(s) + """ + + + ca_bundle_path = os.path.join(sublime.packages_path(), 'User', 'Package Control.ca-bundle') + with open_compat(ca_bundle_path, 'ab') as f: + f.write(b"\n" + contents) + + ca_list_path = os.path.join(sublime.packages_path(), 'User', 'Package Control.ca-list') + with open_compat(ca_list_path, 'r') as f: + ca_certs = json.loads(read_compat(f)) + ca_certs.append(cert_id) + with open_compat(ca_list_path, 'w') as f: + f.write(json.dumps(ca_certs, indent=4)) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/cli_downloader.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/cli_downloader.py new file mode 100644 index 0000000..76c42dd --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/cli_downloader.py @@ -0,0 +1,81 @@ +import os +import subprocess + +from ..console_write import console_write +from ..cmd import create_cmd +from .non_clean_exit_error import NonCleanExitError +from .binary_not_found_error import BinaryNotFoundError + + +class CliDownloader(object): + """ + Base for downloaders that use a command line program + + :param settings: + A dict of the various Package Control settings. The Sublime Text + Settings API is not used because this code is run in a thread. + """ + + def __init__(self, settings): + self.settings = settings + + def clean_tmp_file(self): + if os.path.exists(self.tmp_file): + os.remove(self.tmp_file) + + def find_binary(self, name): + """ + Finds the given executable name in the system PATH + + :param name: + The exact name of the executable to find + + :return: + The absolute path to the executable + + :raises: + BinaryNotFoundError when the executable can not be found + """ + + dirs = os.environ['PATH'].split(os.pathsep) + if os.name != 'nt': + # This is mostly for OS X, which seems to launch ST with a + # minimal set of environmental variables + dirs.append('/usr/local/bin') + + for dir_ in dirs: + path = os.path.join(dir_, name) + if os.path.exists(path): + return path + + raise BinaryNotFoundError('The binary %s could not be located' % name) + + def execute(self, args): + """ + Runs the executable and args and returns the result + + :param args: + A list of the executable path and all arguments to be passed to it + + :return: + The text output of the executable + + :raises: + NonCleanExitError when the executable exits with an error + """ + + if self.settings.get('debug'): + console_write(u"Trying to execute command %s" % create_cmd(args), True) + + proc = subprocess.Popen(args, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + output = proc.stdout.read() + self.stderr = proc.stderr.read() + returncode = proc.wait() + if returncode != 0: + error = NonCleanExitError(returncode) + error.stderr = self.stderr + error.stdout = output + raise error + return output diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/curl_downloader.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/curl_downloader.py new file mode 100644 index 0000000..b09d448 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/curl_downloader.py @@ -0,0 +1,267 @@ +import tempfile +import re +import os + +from ..console_write import console_write +from ..open_compat import open_compat, read_compat +from .cli_downloader import CliDownloader +from .non_clean_exit_error import NonCleanExitError +from .rate_limit_exception import RateLimitException +from .downloader_exception import DownloaderException +from .cert_provider import CertProvider +from .limiting_downloader import LimitingDownloader +from .caching_downloader import CachingDownloader + + +class CurlDownloader(CliDownloader, CertProvider, LimitingDownloader, CachingDownloader): + """ + A downloader that uses the command line program curl + + :param settings: + A dict of the various Package Control settings. The Sublime Text + Settings API is not used because this code is run in a thread. + + :raises: + BinaryNotFoundError: when curl can not be found + """ + + def __init__(self, settings): + self.settings = settings + self.curl = self.find_binary('curl') + + def close(self): + """ + No-op for compatibility with UrllibDownloader and WinINetDownloader + """ + + pass + + def download(self, url, error_message, timeout, tries, prefer_cached=False): + """ + Downloads a URL and returns the contents + + :param url: + The URL to download + + :param error_message: + A string to include in the console error that is printed + when an error occurs + + :param timeout: + The int number of seconds to set the timeout to + + :param tries: + The int number of times to try and download the URL in the case of + a timeout or HTTP 503 error + + :param prefer_cached: + If a cached version should be returned instead of trying a new request + + :raises: + NoCaCertException: when no CA certs can be found for the url + RateLimitException: when a rate limit is hit + DownloaderException: when any other download error occurs + + :return: + The string contents of the URL + """ + + if prefer_cached: + cached = self.retrieve_cached(url) + if cached: + return cached + + self.tmp_file = tempfile.NamedTemporaryFile().name + command = [self.curl, '--user-agent', self.settings.get('user_agent'), + '--connect-timeout', str(int(timeout)), '-sSL', + # Don't be alarmed if the response from the server does not select + # one of these since the server runs a relatively new version of + # OpenSSL which supports compression on the SSL layer, and Apache + # will use that instead of HTTP-level encoding. + '--compressed', + # We have to capture the headers to check for rate limit info + '--dump-header', self.tmp_file] + + request_headers = self.add_conditional_headers(url, {}) + + for name, value in request_headers.items(): + command.extend(['--header', "%s: %s" % (name, value)]) + + secure_url_match = re.match('^https://([^/]+)', url) + if secure_url_match != None: + secure_domain = secure_url_match.group(1) + bundle_path = self.check_certs(secure_domain, timeout) + command.extend(['--cacert', bundle_path]) + + debug = self.settings.get('debug') + if debug: + command.append('-v') + + http_proxy = self.settings.get('http_proxy') + https_proxy = self.settings.get('https_proxy') + proxy_username = self.settings.get('proxy_username') + proxy_password = self.settings.get('proxy_password') + + if debug: + console_write(u"Curl Debug Proxy", True) + console_write(u" http_proxy: %s" % http_proxy) + console_write(u" https_proxy: %s" % https_proxy) + console_write(u" proxy_username: %s" % proxy_username) + console_write(u" proxy_password: %s" % proxy_password) + + if http_proxy or https_proxy: + command.append('--proxy-anyauth') + + if proxy_username or proxy_password: + command.extend(['-U', u"%s:%s" % (proxy_username, proxy_password)]) + + if http_proxy: + os.putenv('http_proxy', http_proxy) + if https_proxy: + os.putenv('HTTPS_PROXY', https_proxy) + + command.append(url) + + error_string = None + while tries > 0: + tries -= 1 + try: + output = self.execute(command) + + with open_compat(self.tmp_file, 'r') as f: + headers_str = read_compat(f) + self.clean_tmp_file() + + message = 'OK' + status = 200 + headers = {} + for header in headers_str.splitlines(): + if header[0:5] == 'HTTP/': + message = re.sub('^HTTP/\d\.\d\s+\d+\s*', '', header) + status = int(re.sub('^HTTP/\d\.\d\s+(\d+)(\s+.*)?$', '\\1', header)) + continue + if header.strip() == '': + continue + name, value = header.split(':', 1) + headers[name.lower()] = value.strip() + + if debug: + self.print_debug(self.stderr.decode('utf-8')) + + self.handle_rate_limit(headers, url) + + if status not in [200, 304]: + e = NonCleanExitError(22) + e.stderr = "%s %s" % (status, message) + raise e + + output = self.cache_result('get', url, status, headers, output) + + return output + + except (NonCleanExitError) as e: + # Stderr is used for both the error message and the debug info + # so we need to process it to extra the debug info + if self.settings.get('debug'): + if hasattr(e.stderr, 'decode'): + e.stderr = e.stderr.decode('utf-8') + e.stderr = self.print_debug(e.stderr) + + self.clean_tmp_file() + + if e.returncode == 22: + code = re.sub('^.*?(\d+)([\w\s]+)?$', '\\1', e.stderr) + if code == '503' and tries != 0: + # GitHub and BitBucket seem to rate limit via 503 + error_string = u'Downloading %s was rate limited' % url + if tries: + error_string += ', trying again' + if debug: + console_write(error_string, True) + continue + + download_error = u'HTTP error ' + code + + elif e.returncode == 6: + download_error = u'URL error host not found' + + elif e.returncode == 28: + # GitHub and BitBucket seem to time out a lot + error_string = u'Downloading %s timed out' % url + if tries: + error_string += ', trying again' + if debug: + console_write(error_string, True) + continue + + else: + download_error = e.stderr.rstrip() + + error_string = u'%s %s downloading %s.' % (error_message, download_error, url) + + break + + raise DownloaderException(error_string) + + def supports_ssl(self): + """ + Indicates if the object can handle HTTPS requests + + :return: + If the object supports HTTPS requests + """ + + return True + + def print_debug(self, string): + """ + Takes debug output from curl and groups and prints it + + :param string: + The complete debug output from curl + + :return: + A string containing any stderr output + """ + + section = 'General' + last_section = None + + output = '' + + for line in string.splitlines(): + # Placeholder for body of request + if line and line[0:2] == '{ ': + continue + if line and line[0:18] == '} [data not shown]': + continue + + if len(line) > 1: + subtract = 0 + if line[0:2] == '* ': + section = 'General' + subtract = 2 + elif line[0:2] == '> ': + section = 'Write' + subtract = 2 + elif line[0:2] == '< ': + section = 'Read' + subtract = 2 + line = line[subtract:] + + # If the line does not start with "* ", "< ", "> " or " " + # then it is a real stderr message + if subtract == 0 and line[0:2] != ' ': + output += line.rstrip() + ' ' + continue + + if line.strip() == '': + continue + + if section != last_section: + console_write(u"Curl HTTP Debug %s" % section, True) + + console_write(u' ' + line) + last_section = section + + return output.rstrip() diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/decoding_downloader.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/decoding_downloader.py new file mode 100644 index 0000000..bc1acf3 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/decoding_downloader.py @@ -0,0 +1,24 @@ +import gzip +import zlib + +try: + # Python 3 + from io import BytesIO as StringIO +except (ImportError): + # Python 2 + from StringIO import StringIO + + +class DecodingDownloader(object): + """ + A base for downloaders that provides the ability to decode gzipped + or deflated content. + """ + + def decode_response(self, encoding, response): + if encoding == 'gzip': + return gzip.GzipFile(fileobj=StringIO(response)).read() + elif encoding == 'deflate': + decompresser = zlib.decompressobj(-zlib.MAX_WBITS) + return decompresser.decompress(response) + decompresser.flush() + return response diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/downloader_exception.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/downloader_exception.py new file mode 100644 index 0000000..7519d8f --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/downloader_exception.py @@ -0,0 +1,5 @@ +class DownloaderException(Exception): + """If a downloader could not download a URL""" + + def __str__(self): + return self.args[0] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/http_error.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/http_error.py new file mode 100644 index 0000000..996e46d --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/http_error.py @@ -0,0 +1,9 @@ +class HttpError(Exception): + """If a downloader was able to download a URL, but the result was not a 200 or 304""" + + def __init__(self, message, code): + self.code = code + super(HttpError, self).__init__(message) + + def __str__(self): + return self.args[0] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/limiting_downloader.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/limiting_downloader.py new file mode 100644 index 0000000..10d2f1f --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/limiting_downloader.py @@ -0,0 +1,36 @@ +try: + # Python 3 + from urllib.parse import urlparse +except (ImportError): + # Python 2 + from urlparse import urlparse + +from .rate_limit_exception import RateLimitException + + +class LimitingDownloader(object): + """ + A base for downloaders that checks for rate limiting headers. + """ + + def handle_rate_limit(self, headers, url): + """ + Checks the headers of a response object to make sure we are obeying the + rate limit + + :param headers: + The dict-like object that contains lower-cased headers + + :param url: + The URL that was requested + + :raises: + RateLimitException when the rate limit has been hit + """ + + limit_remaining = headers.get('x-ratelimit-remaining', '1') + limit = headers.get('x-ratelimit-limit', '1') + + if str(limit_remaining) == '0': + hostname = urlparse(url).hostname + raise RateLimitException(hostname, limit) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/no_ca_cert_exception.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/no_ca_cert_exception.py new file mode 100644 index 0000000..8452bd9 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/no_ca_cert_exception.py @@ -0,0 +1,11 @@ +from .downloader_exception import DownloaderException + + +class NoCaCertException(DownloaderException): + """ + An exception for when there is no CA cert for a domain name + """ + + def __init__(self, message, domain): + self.domain = domain + super(NoCaCertException, self).__init__(message) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/non_clean_exit_error.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/non_clean_exit_error.py new file mode 100644 index 0000000..a932363 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/non_clean_exit_error.py @@ -0,0 +1,13 @@ +class NonCleanExitError(Exception): + """ + When an subprocess does not exit cleanly + + :param returncode: + The command line integer return code of the subprocess + """ + + def __init__(self, returncode): + self.returncode = returncode + + def __str__(self): + return repr(self.returncode) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/non_http_error.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/non_http_error.py new file mode 100644 index 0000000..8a45595 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/non_http_error.py @@ -0,0 +1,5 @@ +class NonHttpError(Exception): + """If a downloader had a non-clean exit, but it was not due to an HTTP error""" + + def __str__(self): + return self.args[0] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/rate_limit_exception.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/rate_limit_exception.py new file mode 100644 index 0000000..18d2b9e --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/rate_limit_exception.py @@ -0,0 +1,13 @@ +from .downloader_exception import DownloaderException + + +class RateLimitException(DownloaderException): + """ + An exception for when the rate limit of an API has been exceeded. + """ + + def __init__(self, domain, limit): + self.domain = domain + self.limit = limit + message = u'Rate limit of %s exceeded for %s' % (limit, domain) + super(RateLimitException, self).__init__(message) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/urllib_downloader.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/urllib_downloader.py new file mode 100644 index 0000000..aa04d31 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/urllib_downloader.py @@ -0,0 +1,291 @@ +import re +import os +import sys + +from .. import http + +try: + # Python 3 + from http.client import HTTPException, BadStatusLine + from urllib.request import ProxyHandler, HTTPPasswordMgrWithDefaultRealm, ProxyBasicAuthHandler, ProxyDigestAuthHandler, build_opener, Request + from urllib.error import HTTPError, URLError + import urllib.request as urllib_compat +except (ImportError): + # Python 2 + from httplib import HTTPException, BadStatusLine + from urllib2 import ProxyHandler, HTTPPasswordMgrWithDefaultRealm, ProxyBasicAuthHandler, ProxyDigestAuthHandler, build_opener, Request + from urllib2 import HTTPError, URLError + import urllib2 as urllib_compat + +try: + # Python 3.3 + import ConnectionError +except (ImportError): + # Python 2.6-3.2 + from socket import error as ConnectionError + +from ..console_write import console_write +from ..unicode import unicode_from_os +from ..http.validating_https_handler import ValidatingHTTPSHandler +from ..http.debuggable_http_handler import DebuggableHTTPHandler +from .rate_limit_exception import RateLimitException +from .downloader_exception import DownloaderException +from .cert_provider import CertProvider +from .decoding_downloader import DecodingDownloader +from .limiting_downloader import LimitingDownloader +from .caching_downloader import CachingDownloader + + +class UrlLibDownloader(CertProvider, DecodingDownloader, LimitingDownloader, CachingDownloader): + """ + A downloader that uses the Python urllib module + + :param settings: + A dict of the various Package Control settings. The Sublime Text + Settings API is not used because this code is run in a thread. + """ + + def __init__(self, settings): + self.opener = None + self.settings = settings + + def close(self): + """ + Closes any persistent/open connections + """ + + if not self.opener: + return + handler = self.get_handler() + if handler: + handler.close() + self.opener = None + + def download(self, url, error_message, timeout, tries, prefer_cached=False): + """ + Downloads a URL and returns the contents + + Uses the proxy settings from the Package Control.sublime-settings file, + however there seem to be a decent number of proxies that this code + does not work with. Patches welcome! + + :param url: + The URL to download + + :param error_message: + A string to include in the console error that is printed + when an error occurs + + :param timeout: + The int number of seconds to set the timeout to + + :param tries: + The int number of times to try and download the URL in the case of + a timeout or HTTP 503 error + + :param prefer_cached: + If a cached version should be returned instead of trying a new request + + :raises: + NoCaCertException: when no CA certs can be found for the url + RateLimitException: when a rate limit is hit + DownloaderException: when any other download error occurs + + :return: + The string contents of the URL + """ + + if prefer_cached: + cached = self.retrieve_cached(url) + if cached: + return cached + + self.setup_opener(url, timeout) + + debug = self.settings.get('debug') + error_string = None + while tries > 0: + tries -= 1 + try: + request_headers = { + "User-Agent": self.settings.get('user_agent'), + # Don't be alarmed if the response from the server does not + # select one of these since the server runs a relatively new + # version of OpenSSL which supports compression on the SSL + # layer, and Apache will use that instead of HTTP-level + # encoding. + "Accept-Encoding": "gzip,deflate" + } + request_headers = self.add_conditional_headers(url, request_headers) + request = Request(url, headers=request_headers) + http_file = self.opener.open(request, timeout=timeout) + self.handle_rate_limit(http_file.headers, url) + + result = http_file.read() + # Make sure the response is closed so we can re-use the connection + http_file.close() + + encoding = http_file.headers.get('content-encoding') + result = self.decode_response(encoding, result) + + return self.cache_result('get', url, http_file.getcode(), + http_file.headers, result) + + except (HTTPException) as e: + # Since we use keep-alives, it is possible the other end closed + # the connection, and we may just need to re-open + if isinstance(e, BadStatusLine): + handler = self.get_handler() + if handler and handler.use_count > 1: + self.close() + self.setup_opener(url, timeout) + tries += 1 + continue + + error_string = u'%s HTTP exception %s (%s) downloading %s.' % ( + error_message, e.__class__.__name__, unicode_from_os(e), url) + + except (HTTPError) as e: + # Make sure the response is closed so we can re-use the connection + e.read() + e.close() + + # Make sure we obey Github's rate limiting headers + self.handle_rate_limit(e.headers, url) + + # Handle cached responses + if unicode_from_os(e.code) == '304': + return self.cache_result('get', url, int(e.code), e.headers, b'') + + # Bitbucket and Github return 503 a decent amount + if unicode_from_os(e.code) == '503' and tries != 0: + error_string = u'Downloading %s was rate limited' % url + if tries: + error_string += ', trying again' + if debug: + console_write(error_string, True) + continue + + error_string = u'%s HTTP error %s downloading %s.' % ( + error_message, unicode_from_os(e.code), url) + + except (URLError) as e: + + # Bitbucket and Github timeout a decent amount + if unicode_from_os(e.reason) == 'The read operation timed out' \ + or unicode_from_os(e.reason) == 'timed out': + error_string = u'Downloading %s timed out' % url + if tries: + error_string += ', trying again' + if debug: + console_write(error_string, True) + continue + + error_string = u'%s URL error %s downloading %s.' % ( + error_message, unicode_from_os(e.reason), url) + + except (ConnectionError): + # Handle broken pipes/reset connections by creating a new opener, and + # thus getting new handlers and a new connection + error_string = u'Connection went away while trying to download %s, trying again' % url + if debug: + console_write(error_string, True) + + self.opener = None + self.setup_opener(url, timeout) + tries += 1 + + continue + + break + + raise DownloaderException(error_string) + + def get_handler(self): + """ + Get the HTTPHandler object for the current connection + """ + + if not self.opener: + return None + + for handler in self.opener.handlers: + if isinstance(handler, ValidatingHTTPSHandler) or isinstance(handler, DebuggableHTTPHandler): + return handler + + def setup_opener(self, url, timeout): + """ + Sets up a urllib OpenerDirector to be used for requests. There is a + fair amount of custom urllib code in Package Control, and part of it + is to handle proxies and keep-alives. Creating an opener the way + below is because the handlers have been customized to send the + "Connection: Keep-Alive" header and hold onto connections so they + can be re-used. + + :param url: + The URL to download + + :param timeout: + The int number of seconds to set the timeout to + """ + + if not self.opener: + http_proxy = self.settings.get('http_proxy') + https_proxy = self.settings.get('https_proxy') + if http_proxy or https_proxy: + proxies = {} + if http_proxy: + proxies['http'] = http_proxy + if https_proxy: + proxies['https'] = https_proxy + proxy_handler = ProxyHandler(proxies) + else: + proxy_handler = ProxyHandler() + + password_manager = HTTPPasswordMgrWithDefaultRealm() + proxy_username = self.settings.get('proxy_username') + proxy_password = self.settings.get('proxy_password') + if proxy_username and proxy_password: + if http_proxy: + password_manager.add_password(None, http_proxy, proxy_username, + proxy_password) + if https_proxy: + password_manager.add_password(None, https_proxy, proxy_username, + proxy_password) + + handlers = [proxy_handler] + + basic_auth_handler = ProxyBasicAuthHandler(password_manager) + digest_auth_handler = ProxyDigestAuthHandler(password_manager) + handlers.extend([digest_auth_handler, basic_auth_handler]) + + debug = self.settings.get('debug') + + if debug: + console_write(u"Urllib Debug Proxy", True) + console_write(u" http_proxy: %s" % http_proxy) + console_write(u" https_proxy: %s" % https_proxy) + console_write(u" proxy_username: %s" % proxy_username) + console_write(u" proxy_password: %s" % proxy_password) + + secure_url_match = re.match('^https://([^/]+)', url) + if secure_url_match != None: + secure_domain = secure_url_match.group(1) + bundle_path = self.check_certs(secure_domain, timeout) + bundle_path = bundle_path.encode(sys.getfilesystemencoding()) + handlers.append(ValidatingHTTPSHandler(ca_certs=bundle_path, + debug=debug, passwd=password_manager, + user_agent=self.settings.get('user_agent'))) + else: + handlers.append(DebuggableHTTPHandler(debug=debug, + passwd=password_manager)) + self.opener = build_opener(*handlers) + + def supports_ssl(self): + """ + Indicates if the object can handle HTTPS requests + + :return: + If the object supports HTTPS requests + """ + return 'ssl' in sys.modules and hasattr(urllib_compat, 'HTTPSHandler') diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/wget_downloader.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/wget_downloader.py new file mode 100644 index 0000000..fb83d1b --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/wget_downloader.py @@ -0,0 +1,347 @@ +import tempfile +import re +import os + +from ..console_write import console_write +from ..unicode import unicode_from_os +from ..open_compat import open_compat, read_compat +from .cli_downloader import CliDownloader +from .non_http_error import NonHttpError +from .non_clean_exit_error import NonCleanExitError +from .rate_limit_exception import RateLimitException +from .downloader_exception import DownloaderException +from .cert_provider import CertProvider +from .decoding_downloader import DecodingDownloader +from .limiting_downloader import LimitingDownloader +from .caching_downloader import CachingDownloader + + +class WgetDownloader(CliDownloader, CertProvider, DecodingDownloader, LimitingDownloader, CachingDownloader): + """ + A downloader that uses the command line program wget + + :param settings: + A dict of the various Package Control settings. The Sublime Text + Settings API is not used because this code is run in a thread. + + :raises: + BinaryNotFoundError: when wget can not be found + """ + + def __init__(self, settings): + self.settings = settings + self.debug = settings.get('debug') + self.wget = self.find_binary('wget') + + def close(self): + """ + No-op for compatibility with UrllibDownloader and WinINetDownloader + """ + + pass + + def download(self, url, error_message, timeout, tries, prefer_cached=False): + """ + Downloads a URL and returns the contents + + :param url: + The URL to download + + :param error_message: + A string to include in the console error that is printed + when an error occurs + + :param timeout: + The int number of seconds to set the timeout to + + :param tries: + The int number of times to try and download the URL in the case of + a timeout or HTTP 503 error + + :param prefer_cached: + If a cached version should be returned instead of trying a new request + + :raises: + NoCaCertException: when no CA certs can be found for the url + RateLimitException: when a rate limit is hit + DownloaderException: when any other download error occurs + + :return: + The string contents of the URL + """ + + if prefer_cached: + cached = self.retrieve_cached(url) + if cached: + return cached + + self.tmp_file = tempfile.NamedTemporaryFile().name + command = [self.wget, '--connect-timeout=' + str(int(timeout)), '-o', + self.tmp_file, '-O', '-', '-U', self.settings.get('user_agent')] + + request_headers = { + # Don't be alarmed if the response from the server does not select + # one of these since the server runs a relatively new version of + # OpenSSL which supports compression on the SSL layer, and Apache + # will use that instead of HTTP-level encoding. + 'Accept-Encoding': 'gzip,deflate' + } + request_headers = self.add_conditional_headers(url, request_headers) + + for name, value in request_headers.items(): + command.extend(['--header', "%s: %s" % (name, value)]) + + secure_url_match = re.match('^https://([^/]+)', url) + if secure_url_match != None: + secure_domain = secure_url_match.group(1) + bundle_path = self.check_certs(secure_domain, timeout) + command.append(u'--ca-certificate=' + bundle_path) + + if self.debug: + command.append('-d') + else: + command.append('-S') + + http_proxy = self.settings.get('http_proxy') + https_proxy = self.settings.get('https_proxy') + proxy_username = self.settings.get('proxy_username') + proxy_password = self.settings.get('proxy_password') + + if proxy_username: + command.append(u"--proxy-user=%s" % proxy_username) + if proxy_password: + command.append(u"--proxy-password=%s" % proxy_password) + + if self.debug: + console_write(u"Wget Debug Proxy", True) + console_write(u" http_proxy: %s" % http_proxy) + console_write(u" https_proxy: %s" % https_proxy) + console_write(u" proxy_username: %s" % proxy_username) + console_write(u" proxy_password: %s" % proxy_password) + + command.append(url) + + if http_proxy: + os.putenv('http_proxy', http_proxy) + if https_proxy: + os.putenv('https_proxy', https_proxy) + + error_string = None + while tries > 0: + tries -= 1 + try: + result = self.execute(command) + + general, headers = self.parse_output() + encoding = headers.get('content-encoding') + if encoding: + result = self.decode_response(encoding, result) + + result = self.cache_result('get', url, general['status'], + headers, result) + + return result + + except (NonCleanExitError) as e: + + try: + general, headers = self.parse_output() + self.handle_rate_limit(headers, url) + + if general['status'] == 304: + return self.cache_result('get', url, general['status'], + headers, None) + + if general['status'] == 503 and tries != 0: + # GitHub and BitBucket seem to rate limit via 503 + error_string = u'Downloading %s was rate limited' % url + if tries: + error_string += ', trying again' + if self.debug: + console_write(error_string, True) + continue + + download_error = 'HTTP error %s' % general['status'] + + except (NonHttpError) as e: + + download_error = unicode_from_os(e) + + # GitHub and BitBucket seem to time out a lot + if download_error.find('timed out') != -1: + error_string = u'Downloading %s timed out' % url + if tries: + error_string += ', trying again' + if self.debug: + console_write(error_string, True) + continue + + error_string = u'%s %s downloading %s.' % (error_message, download_error, url) + + break + + raise DownloaderException(error_string) + + def supports_ssl(self): + """ + Indicates if the object can handle HTTPS requests + + :return: + If the object supports HTTPS requests + """ + + return True + + def parse_output(self): + """ + Parses the wget output file, prints debug information and returns headers + + :return: + A tuple of (general, headers) where general is a dict with the keys: + `version` - HTTP version number (string) + `status` - HTTP status code (integer) + `message` - HTTP status message (string) + And headers is a dict with the keys being lower-case version of the + HTTP header names. + """ + + with open_compat(self.tmp_file, 'r') as f: + output = read_compat(f).splitlines() + self.clean_tmp_file() + + error = None + header_lines = [] + if self.debug: + section = 'General' + last_section = None + for line in output: + if section == 'General': + if self.skippable_line(line): + continue + + # Skip blank lines + if line.strip() == '': + continue + + # Error lines + if line[0:5] == 'wget:': + error = line[5:].strip() + if line[0:7] == 'failed:': + error = line[7:].strip() + + if line == '---request begin---': + section = 'Write' + continue + elif line == '---request end---': + section = 'General' + continue + elif line == '---response begin---': + section = 'Read' + continue + elif line == '---response end---': + section = 'General' + continue + + if section != last_section: + console_write(u"Wget HTTP Debug %s" % section, True) + + if section == 'Read': + header_lines.append(line) + + console_write(u' ' + line) + last_section = section + + else: + for line in output: + if self.skippable_line(line): + continue + + # Check the resolving and connecting to lines for errors + if re.match('(Resolving |Connecting to )', line): + failed_match = re.search(' failed: (.*)$', line) + if failed_match: + error = failed_match.group(1).strip() + + # Error lines + if line[0:5] == 'wget:': + error = line[5:].strip() + if line[0:7] == 'failed:': + error = line[7:].strip() + + if line[0:2] == ' ': + header_lines.append(line.lstrip()) + + if error: + raise NonHttpError(error) + + return self.parse_headers(header_lines) + + def skippable_line(self, line): + """ + Determines if a debug line is skippable - usually because of extraneous + or duplicate information. + + :param line: + The debug line to check + + :return: + True if the line is skippable, otherwise None + """ + + # Skip date lines + if re.match('--\d{4}-\d{2}-\d{2}', line): + return True + if re.match('\d{4}-\d{2}-\d{2}', line): + return True + # Skip HTTP status code lines since we already have that info + if re.match('\d{3} ', line): + return True + # Skip Saving to and progress lines + if re.match('(Saving to:|\s*\d+K)', line): + return True + # Skip notice about ignoring body on HTTP error + if re.match('Skipping \d+ byte', line): + return True + + def parse_headers(self, output=None): + """ + Parses HTTP headers into two dict objects + + :param output: + An array of header lines, if None, loads from temp output file + + :return: + A tuple of (general, headers) where general is a dict with the keys: + `version` - HTTP version number (string) + `status` - HTTP status code (integer) + `message` - HTTP status message (string) + And headers is a dict with the keys being lower-case version of the + HTTP header names. + """ + + if not output: + with open_compat(self.tmp_file, 'r') as f: + output = read_compat(f).splitlines() + self.clean_tmp_file() + + general = { + 'version': '0.9', + 'status': 200, + 'message': 'OK' + } + headers = {} + for line in output: + # When using the -S option, headers have two spaces before them, + # additionally, valid headers won't have spaces, so this is always + # a safe operation to perform + line = line.lstrip() + if line.find('HTTP/') == 0: + match = re.match('HTTP/(\d\.\d)\s+(\d+)(?:\s+(.*))?$', line) + general['version'] = match.group(1) + general['status'] = int(match.group(2)) + general['message'] = match.group(3) or '' + else: + name, value = line.split(':', 1) + headers[name.lower()] = value.strip() + + return (general, headers) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/wininet_downloader.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/wininet_downloader.py new file mode 100644 index 0000000..7134db9 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/wininet_downloader.py @@ -0,0 +1,652 @@ +from ctypes import windll, wintypes +import ctypes +import time +import re +import datetime +import struct +import locale + +wininet = windll.wininet + +try: + # Python 3 + from urllib.parse import urlparse +except (ImportError): + # Python 2 + from urlparse import urlparse + +from ..console_write import console_write +from ..unicode import unicode_from_os +from .non_http_error import NonHttpError +from .http_error import HttpError +from .rate_limit_exception import RateLimitException +from .downloader_exception import DownloaderException +from .decoding_downloader import DecodingDownloader +from .limiting_downloader import LimitingDownloader +from .caching_downloader import CachingDownloader + + +class WinINetDownloader(DecodingDownloader, LimitingDownloader, CachingDownloader): + """ + A downloader that uses the Windows WinINet DLL to perform downloads. This + has the benefit of utilizing system-level proxy configuration and CA certs. + + :param settings: + A dict of the various Package Control settings. The Sublime Text + Settings API is not used because this code is run in a thread. + """ + + # General constants + ERROR_INSUFFICIENT_BUFFER = 122 + + # InternetOpen constants + INTERNET_OPEN_TYPE_PRECONFIG = 0 + + # InternetConnect constants + INTERNET_SERVICE_HTTP = 3 + INTERNET_FLAG_EXISTING_CONNECT = 0x20000000 + INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS = 0x00004000 + + # InternetSetOption constants + INTERNET_OPTION_CONNECT_TIMEOUT = 2 + INTERNET_OPTION_SEND_TIMEOUT = 5 + INTERNET_OPTION_RECEIVE_TIMEOUT = 6 + + # InternetQueryOption constants + INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT = 32 + INTERNET_OPTION_PROXY = 38 + INTERNET_OPTION_PROXY_USERNAME = 43 + INTERNET_OPTION_PROXY_PASSWORD = 44 + INTERNET_OPTION_CONNECTED_STATE = 50 + + # HttpOpenRequest constants + INTERNET_FLAG_KEEP_CONNECTION = 0x00400000 + INTERNET_FLAG_RELOAD = 0x80000000 + INTERNET_FLAG_NO_CACHE_WRITE = 0x04000000 + INTERNET_FLAG_PRAGMA_NOCACHE = 0x00000100 + INTERNET_FLAG_SECURE = 0x00800000 + + # HttpQueryInfo constants + HTTP_QUERY_RAW_HEADERS_CRLF = 22 + + # InternetConnectedState constants + INTERNET_STATE_CONNECTED = 1 + INTERNET_STATE_DISCONNECTED = 2 + INTERNET_STATE_DISCONNECTED_BY_USER = 0x10 + INTERNET_STATE_IDLE = 0x100 + INTERNET_STATE_BUSY = 0x200 + + + def __init__(self, settings): + self.settings = settings + self.debug = settings.get('debug') + self.network_connection = None + self.tcp_connection = None + self.use_count = 0 + self.hostname = None + self.port = None + self.scheme = None + self.was_offline = None + + def close(self): + """ + Closes any persistent/open connections + """ + + closed = False + changed_state_back = False + + if self.tcp_connection: + wininet.InternetCloseHandle(self.tcp_connection) + self.tcp_connection = None + closed = True + + if self.network_connection: + wininet.InternetCloseHandle(self.network_connection) + self.network_connection = None + closed = True + + if self.was_offline: + dw_connected_state = wintypes.DWORD(self.INTERNET_STATE_DISCONNECTED_BY_USER) + dw_flags = wintypes.DWORD(0) + connected_info = InternetConnectedInfo(dw_connected_state, dw_flags) + wininet.InternetSetOptionA(None, + self.INTERNET_OPTION_CONNECTED_STATE, ctypes.byref(connected_info), ctypes.sizeof(connected_info)) + changed_state_back = True + + if self.debug: + s = '' if self.use_count == 1 else 's' + console_write(u"WinINet %s Debug General" % self.scheme.upper(), True) + console_write(u" Closing connection to %s on port %s after %s request%s" % ( + self.hostname, self.port, self.use_count, s)) + if changed_state_back: + console_write(u" Changed Internet Explorer back to Work Offline") + + self.hostname = None + self.port = None + self.scheme = None + self.use_count = 0 + self.was_offline = None + + def download(self, url, error_message, timeout, tries, prefer_cached=False): + """ + Downloads a URL and returns the contents + + :param url: + The URL to download + + :param error_message: + A string to include in the console error that is printed + when an error occurs + + :param timeout: + The int number of seconds to set the timeout to + + :param tries: + The int number of times to try and download the URL in the case of + a timeout or HTTP 503 error + + :param prefer_cached: + If a cached version should be returned instead of trying a new request + + :raises: + RateLimitException: when a rate limit is hit + DownloaderException: when any other download error occurs + + :return: + The string contents of the URL + """ + + if prefer_cached: + cached = self.retrieve_cached(url) + if cached: + return cached + + url_info = urlparse(url) + + if not url_info.port: + port = 443 if url_info.scheme == 'https' else 80 + hostname = url_info.netloc + else: + port = url_info.port + hostname = url_info.hostname + + path = url_info.path + if url_info.params: + path += ';' + url_info.params + if url_info.query: + path += '?' + url_info.query + + request_headers = { + 'Accept-Encoding': 'gzip,deflate' + } + request_headers = self.add_conditional_headers(url, request_headers) + + created_connection = False + # If we switched Internet Explorer out of "Work Offline" mode + changed_to_online = False + + # If the user is requesting a connection to another server, close the connection + if (self.hostname and self.hostname != hostname) or (self.port and self.port != port): + self.close() + + # Reset the error info to a known clean state + ctypes.windll.kernel32.SetLastError(0) + + # Save the internet setup in the class for re-use + if not self.tcp_connection: + created_connection = True + + # Connect to the internet if necessary + state = self.read_option(None, self.INTERNET_OPTION_CONNECTED_STATE) + state = ord(state) + if state & self.INTERNET_STATE_DISCONNECTED or state & self.INTERNET_STATE_DISCONNECTED_BY_USER: + # Track the previous state so we can go back once complete + self.was_offline = True + + dw_connected_state = wintypes.DWORD(self.INTERNET_STATE_CONNECTED) + dw_flags = wintypes.DWORD(0) + connected_info = InternetConnectedInfo(dw_connected_state, dw_flags) + wininet.InternetSetOptionA(None, + self.INTERNET_OPTION_CONNECTED_STATE, ctypes.byref(connected_info), ctypes.sizeof(connected_info)) + changed_to_online = True + + self.network_connection = wininet.InternetOpenW(self.settings.get('user_agent'), + self.INTERNET_OPEN_TYPE_PRECONFIG, None, None, 0) + + if not self.network_connection: + error_string = u'%s %s during network phase of downloading %s.' % (error_message, self.extract_error(), url) + raise DownloaderException(error_string) + + win_timeout = wintypes.DWORD(int(timeout) * 1000) + # Apparently INTERNET_OPTION_CONNECT_TIMEOUT just doesn't work, leaving it in hoping they may fix in the future + wininet.InternetSetOptionA(self.network_connection, + self.INTERNET_OPTION_CONNECT_TIMEOUT, win_timeout, ctypes.sizeof(win_timeout)) + wininet.InternetSetOptionA(self.network_connection, + self.INTERNET_OPTION_SEND_TIMEOUT, win_timeout, ctypes.sizeof(win_timeout)) + wininet.InternetSetOptionA(self.network_connection, + self.INTERNET_OPTION_RECEIVE_TIMEOUT, win_timeout, ctypes.sizeof(win_timeout)) + + # Don't allow HTTPS sites to redirect to HTTP sites + tcp_flags = self.INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS + # Try to re-use an existing connection to the server + tcp_flags |= self.INTERNET_FLAG_EXISTING_CONNECT + self.tcp_connection = wininet.InternetConnectW(self.network_connection, + hostname, port, None, None, self.INTERNET_SERVICE_HTTP, tcp_flags, 0) + + if not self.tcp_connection: + error_string = u'%s %s during connection phase of downloading %s.' % (error_message, self.extract_error(), url) + raise DownloaderException(error_string) + + # Normally the proxy info would come from IE, but this allows storing it in + # the Package Control settings file. + proxy_username = self.settings.get('proxy_username') + proxy_password = self.settings.get('proxy_password') + if proxy_username and proxy_password: + username = ctypes.c_wchar_p(proxy_username) + password = ctypes.c_wchar_p(proxy_password) + wininet.InternetSetOptionW(self.tcp_connection, + self.INTERNET_OPTION_PROXY_USERNAME, ctypes.cast(username, ctypes.c_void_p), len(proxy_username)) + wininet.InternetSetOptionW(self.tcp_connection, + self.INTERNET_OPTION_PROXY_PASSWORD, ctypes.cast(password, ctypes.c_void_p), len(proxy_password)) + + self.hostname = hostname + self.port = port + self.scheme = url_info.scheme + + else: + if self.debug: + console_write(u"WinINet %s Debug General" % self.scheme.upper(), True) + console_write(u" Re-using connection to %s on port %s for request #%s" % ( + self.hostname, self.port, self.use_count)) + + error_string = None + while tries > 0: + tries -= 1 + try: + http_connection = None + + # Keep-alive for better performance + http_flags = self.INTERNET_FLAG_KEEP_CONNECTION + # Prevent caching/retrieving from cache + http_flags |= self.INTERNET_FLAG_RELOAD + http_flags |= self.INTERNET_FLAG_NO_CACHE_WRITE + http_flags |= self.INTERNET_FLAG_PRAGMA_NOCACHE + # Use SSL + if self.scheme == 'https': + http_flags |= self.INTERNET_FLAG_SECURE + + http_connection = wininet.HttpOpenRequestW(self.tcp_connection, u'GET', path, u'HTTP/1.1', None, None, http_flags, 0) + if not http_connection: + error_string = u'%s %s during HTTP connection phase of downloading %s.' % (error_message, self.extract_error(), url) + raise DownloaderException(error_string) + + request_header_lines = [] + for header, value in request_headers.items(): + request_header_lines.append(u"%s: %s" % (header, value)) + request_header_lines = u"\r\n".join(request_header_lines) + + success = wininet.HttpSendRequestW(http_connection, request_header_lines, len(request_header_lines), None, 0) + + if not success: + error_string = u'%s %s during HTTP write phase of downloading %s.' % (error_message, self.extract_error(), url) + raise DownloaderException(error_string) + + # If we try to query before here, the proxy info will not be available to the first request + if self.debug: + proxy_struct = self.read_option(self.network_connection, self.INTERNET_OPTION_PROXY) + proxy = '' + if proxy_struct.lpszProxy: + proxy = proxy_struct.lpszProxy.decode('cp1252') + proxy_bypass = '' + if proxy_struct.lpszProxyBypass: + proxy_bypass = proxy_struct.lpszProxyBypass.decode('cp1252') + + proxy_username = self.read_option(self.tcp_connection, self.INTERNET_OPTION_PROXY_USERNAME) + proxy_password = self.read_option(self.tcp_connection, self.INTERNET_OPTION_PROXY_PASSWORD) + + console_write(u"WinINet Debug Proxy", True) + console_write(u" proxy: %s" % proxy) + console_write(u" proxy bypass: %s" % proxy_bypass) + console_write(u" proxy username: %s" % proxy_username) + console_write(u" proxy password: %s" % proxy_password) + + self.use_count += 1 + + if self.debug and created_connection: + if self.scheme == 'https': + cert_struct = self.read_option(http_connection, self.INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT) + + if cert_struct.lpszIssuerInfo: + issuer_info = cert_struct.lpszIssuerInfo.decode('cp1252') + issuer_parts = issuer_info.split("\r\n") + else: + issuer_parts = ['No issuer info'] + + if cert_struct.lpszSubjectInfo: + subject_info = cert_struct.lpszSubjectInfo.decode('cp1252') + subject_parts = subject_info.split("\r\n") + else: + subject_parts = ["No subject info"] + + common_name = subject_parts[-1] + + if cert_struct.ftStart.dwLowDateTime != 0 and cert_struct.ftStart.dwHighDateTime != 0: + issue_date = self.convert_filetime_to_datetime(cert_struct.ftStart) + issue_date = issue_date.strftime('%a, %d %b %Y %H:%M:%S GMT') + else: + issue_date = u"No issue date" + + if cert_struct.ftExpiry.dwLowDateTime != 0 and cert_struct.ftExpiry.dwHighDateTime != 0: + expiration_date = self.convert_filetime_to_datetime(cert_struct.ftExpiry) + expiration_date = expiration_date.strftime('%a, %d %b %Y %H:%M:%S GMT') + else: + expiration_date = u"No expiration date" + + console_write(u"WinINet HTTPS Debug General", True) + if changed_to_online: + console_write(u" Internet Explorer was set to Work Offline, temporarily going online") + console_write(u" Server SSL Certificate:") + console_write(u" subject: %s" % ", ".join(subject_parts)) + console_write(u" issuer: %s" % ", ".join(issuer_parts)) + console_write(u" common name: %s" % common_name) + console_write(u" issue date: %s" % issue_date) + console_write(u" expire date: %s" % expiration_date) + + elif changed_to_online: + console_write(u"WinINet HTTP Debug General", True) + console_write(u" Internet Explorer was set to Work Offline, temporarily going online") + + if self.debug: + console_write(u"WinINet %s Debug Write" % self.scheme.upper(), True) + # Add in some known headers that WinINet sends since we can't get the real list + console_write(u" GET %s HTTP/1.1" % path) + for header, value in request_headers.items(): + console_write(u" %s: %s" % (header, value)) + console_write(u" User-Agent: %s" % self.settings.get('user_agent')) + console_write(u" Host: %s" % hostname) + console_write(u" Connection: Keep-Alive") + console_write(u" Cache-Control: no-cache") + + header_buffer_size = 8192 + + try_again = True + while try_again: + try_again = False + + to_read_was_read = wintypes.DWORD(header_buffer_size) + headers_buffer = ctypes.create_string_buffer(header_buffer_size) + + success = wininet.HttpQueryInfoA(http_connection, self.HTTP_QUERY_RAW_HEADERS_CRLF, ctypes.byref(headers_buffer), ctypes.byref(to_read_was_read), None) + if not success: + if ctypes.GetLastError() != self.ERROR_INSUFFICIENT_BUFFER: + error_string = u'%s %s during header read phase of downloading %s.' % (error_message, self.extract_error(), url) + raise DownloaderException(error_string) + # The error was a buffer that was too small, so try again + header_buffer_size = to_read_was_read.value + try_again = True + continue + + headers = b'' + if to_read_was_read.value > 0: + headers += headers_buffer.raw[:to_read_was_read.value] + headers = headers.decode('iso-8859-1').rstrip("\r\n").split("\r\n") + + if self.debug: + console_write(u"WinINet %s Debug Read" % self.scheme.upper(), True) + for header in headers: + console_write(u" %s" % header) + + buffer_length = 65536 + output_buffer = ctypes.create_string_buffer(buffer_length) + bytes_read = wintypes.DWORD() + + result = b'' + try_again = True + while try_again: + try_again = False + wininet.InternetReadFile(http_connection, output_buffer, buffer_length, ctypes.byref(bytes_read)) + if bytes_read.value > 0: + result += output_buffer.raw[:bytes_read.value] + try_again = True + + general, headers = self.parse_headers(headers) + self.handle_rate_limit(headers, url) + + if general['status'] == 503 and tries != 0: + # GitHub and BitBucket seem to rate limit via 503 + error_string = u'Downloading %s was rate limited' % url + if tries: + error_string += ', trying again' + if self.debug: + console_write(error_string, True) + continue + + encoding = headers.get('content-encoding') + if encoding: + result = self.decode_response(encoding, result) + + result = self.cache_result('get', url, general['status'], + headers, result) + + if general['status'] not in [200, 304]: + raise HttpError("HTTP error %s" % general['status'], general['status']) + + return result + + except (NonHttpError, HttpError) as e: + + # GitHub and BitBucket seem to time out a lot + if str(e).find('timed out') != -1: + error_string = u'Downloading %s timed out' % url + if tries: + error_string += ', trying again' + if self.debug: + console_write(error_string, True) + continue + + error_string = u'%s %s downloading %s.' % (error_message, e, url) + + finally: + if http_connection: + wininet.InternetCloseHandle(http_connection) + + break + + raise DownloaderException(error_string) + + def convert_filetime_to_datetime(self, filetime): + """ + Windows returns times as 64-bit unsigned longs that are the number + of hundreds of nanoseconds since Jan 1 1601. This converts it to + a datetime object. + + :param filetime: + A FileTime struct object + + :return: + A (UTC) datetime object + """ + + hundreds_nano_seconds = struct.unpack('>Q', struct.pack('>LL', filetime.dwHighDateTime, filetime.dwLowDateTime))[0] + seconds_since_1601 = hundreds_nano_seconds / 10000000 + epoch_seconds = seconds_since_1601 - 11644473600 # Seconds from Jan 1 1601 to Jan 1 1970 + return datetime.datetime.fromtimestamp(epoch_seconds) + + def extract_error(self): + """ + Retrieves and formats an error from WinINet + + :return: + A string with a nice description of the error + """ + + error_num = ctypes.GetLastError() + raw_error_string = ctypes.FormatError(error_num) + + error_string = unicode_from_os(raw_error_string) + + # Try to fill in some known errors + if error_string == u"": + error_lookup = { + 12007: u'host not found', + 12029: u'connection refused', + 12057: u'error checking for server certificate revocation', + 12169: u'invalid secure certificate', + 12157: u'secure channel error, server not providing SSL', + 12002: u'operation timed out' + } + if error_num in error_lookup: + error_string = error_lookup[error_num] + + if error_string == u"": + return u"(errno %s)" % error_num + + error_string = error_string[0].upper() + error_string[1:] + return u"%s (errno %s)" % (error_string, error_num) + + def supports_ssl(self): + """ + Indicates if the object can handle HTTPS requests + + :return: + If the object supports HTTPS requests + """ + + return True + + def read_option(self, handle, option): + """ + Reads information about the internet connection, which may be a string or struct + + :param handle: + The handle to query for the info + + :param option: + The (int) option to get + + :return: + A string, or one of the InternetCertificateInfo or InternetProxyInfo structs + """ + + option_buffer_size = 8192 + try_again = True + + while try_again: + try_again = False + + to_read_was_read = wintypes.DWORD(option_buffer_size) + option_buffer = ctypes.create_string_buffer(option_buffer_size) + ref = ctypes.byref(option_buffer) + + success = wininet.InternetQueryOptionA(handle, option, ref, ctypes.byref(to_read_was_read)) + if not success: + if ctypes.GetLastError() != self.ERROR_INSUFFICIENT_BUFFER: + raise NonHttpError(self.extract_error()) + # The error was a buffer that was too small, so try again + option_buffer_size = to_read_was_read.value + try_again = True + continue + + if option == self.INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: + length = min(len(option_buffer), ctypes.sizeof(InternetCertificateInfo)) + cert_info = InternetCertificateInfo() + ctypes.memmove(ctypes.addressof(cert_info), option_buffer, length) + return cert_info + elif option == self.INTERNET_OPTION_PROXY: + length = min(len(option_buffer), ctypes.sizeof(InternetProxyInfo)) + proxy_info = InternetProxyInfo() + ctypes.memmove(ctypes.addressof(proxy_info), option_buffer, length) + return proxy_info + else: + option = b'' + if to_read_was_read.value > 0: + option += option_buffer.raw[:to_read_was_read.value] + return option.decode('cp1252').rstrip("\x00") + + def parse_headers(self, output): + """ + Parses HTTP headers into two dict objects + + :param output: + An array of header lines + + :return: + A tuple of (general, headers) where general is a dict with the keys: + `version` - HTTP version number (string) + `status` - HTTP status code (integer) + `message` - HTTP status message (string) + And headers is a dict with the keys being lower-case version of the + HTTP header names. + """ + + general = { + 'version': '0.9', + 'status': 200, + 'message': 'OK' + } + headers = {} + for line in output: + line = line.lstrip() + if line.find('HTTP/') == 0: + match = re.match('HTTP/(\d\.\d)\s+(\d+)\s+(.*)$', line) + general['version'] = match.group(1) + general['status'] = int(match.group(2)) + general['message'] = match.group(3) + else: + name, value = line.split(':', 1) + headers[name.lower()] = value.strip() + + return (general, headers) + + +class FileTime(ctypes.Structure): + """ + A Windows struct used by InternetCertificateInfo for certificate + date information + """ + + _fields_ = [ + ("dwLowDateTime", wintypes.DWORD), + ("dwHighDateTime", wintypes.DWORD) + ] + + +class InternetCertificateInfo(ctypes.Structure): + """ + A Windows struct used to store information about an SSL certificate + """ + + _fields_ = [ + ("ftExpiry", FileTime), + ("ftStart", FileTime), + ("lpszSubjectInfo", ctypes.c_char_p), + ("lpszIssuerInfo", ctypes.c_char_p), + ("lpszProtocolName", ctypes.c_char_p), + ("lpszSignatureAlgName", ctypes.c_char_p), + ("lpszEncryptionAlgName", ctypes.c_char_p), + ("dwKeySize", wintypes.DWORD) + ] + + +class InternetProxyInfo(ctypes.Structure): + """ + A Windows struct usd to store information about the configured proxy server + """ + + _fields_ = [ + ("dwAccessType", wintypes.DWORD), + ("lpszProxy", ctypes.c_char_p), + ("lpszProxyBypass", ctypes.c_char_p) + ] + + +class InternetConnectedInfo(ctypes.Structure): + """ + A Windows struct usd to store information about the global internet connection state + """ + + _fields_ = [ + ("dwConnectedState", wintypes.DWORD), + ("dwFlags", wintypes.DWORD) + ] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/file_not_found_error.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/file_not_found_error.py new file mode 100644 index 0000000..3fd4da5 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/file_not_found_error.py @@ -0,0 +1,4 @@ +class FileNotFoundError(Exception): + """If a file is not found""" + + pass diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/__init__.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/__init__.py new file mode 100644 index 0000000..e3358df --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/__init__.py @@ -0,0 +1,65 @@ +import sys + +try: + # Python 2 + import urllib2 + import httplib + + # Monkey patch AbstractBasicAuthHandler to prevent infinite recursion + def non_recursive_http_error_auth_reqed(self, authreq, host, req, headers): + authreq = headers.get(authreq, None) + + if not hasattr(self, 'retried'): + self.retried = 0 + + if self.retried > 5: + raise urllib2.HTTPError(req.get_full_url(), 401, "basic auth failed", + headers, None) + else: + self.retried += 1 + + if authreq: + mo = urllib2.AbstractBasicAuthHandler.rx.search(authreq) + if mo: + scheme, quote, realm = mo.groups() + if scheme.lower() == 'basic': + return self.retry_http_basic_auth(host, req, realm) + + urllib2.AbstractBasicAuthHandler.http_error_auth_reqed = non_recursive_http_error_auth_reqed + + # Money patch urllib2.Request and httplib.HTTPConnection so that + # HTTPS proxies work in Python 2.6.1-2 + if sys.version_info < (2, 6, 3): + + urllib2.Request._tunnel_host = None + + def py268_set_proxy(self, host, type): + if self.type == 'https' and not self._tunnel_host: + self._tunnel_host = self.host + else: + self.type = type + # The _Request prefix is to handle python private name mangling + self._Request__r_host = self._Request__original + self.host = host + urllib2.Request.set_proxy = py268_set_proxy + + if sys.version_info < (2, 6, 5): + + def py268_set_tunnel(self, host, port=None, headers=None): + """ Sets up the host and the port for the HTTP CONNECT Tunnelling. + + The headers argument should be a mapping of extra HTTP headers + to send with the CONNECT request. + """ + self._tunnel_host = host + self._tunnel_port = port + if headers: + self._tunnel_headers = headers + else: + self._tunnel_headers.clear() + httplib.HTTPConnection._set_tunnel = py268_set_tunnel + + +except (ImportError): + # Python 3 does not need to be patched + pass diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/debuggable_http_connection.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/debuggable_http_connection.py new file mode 100644 index 0000000..e0044a9 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/debuggable_http_connection.py @@ -0,0 +1,72 @@ +import os +import re +import socket + +try: + # Python 3 + from http.client import HTTPConnection + from urllib.error import URLError +except (ImportError): + # Python 2 + from httplib import HTTPConnection + from urllib2 import URLError + +from ..console_write import console_write +from .debuggable_http_response import DebuggableHTTPResponse + + +class DebuggableHTTPConnection(HTTPConnection): + """ + A custom HTTPConnection that formats debugging info for Sublime Text + """ + + response_class = DebuggableHTTPResponse + _debug_protocol = 'HTTP' + + def __init__(self, host, port=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, + **kwargs): + self.passwd = kwargs.get('passwd') + + # Python 2.6.1 on OS X 10.6 does not include these + self._tunnel_host = None + self._tunnel_port = None + self._tunnel_headers = {} + if 'debug' in kwargs and kwargs['debug']: + self.debuglevel = 5 + elif 'debuglevel' in kwargs: + self.debuglevel = kwargs['debuglevel'] + + HTTPConnection.__init__(self, host, port=port, timeout=timeout) + + def connect(self): + if self.debuglevel == -1: + console_write(u'Urllib %s Debug General' % self._debug_protocol, True) + console_write(u" Connecting to %s on port %s" % (self.host, self.port)) + HTTPConnection.connect(self) + + def send(self, string): + # We have to use a positive debuglevel to get it passed to the + # HTTPResponse object, however we don't want to use it because by + # default debugging prints to the stdout and we can't capture it, so + # we temporarily set it to -1 for the standard httplib code + reset_debug = False + if self.debuglevel == 5: + reset_debug = 5 + self.debuglevel = -1 + HTTPConnection.send(self, string) + if reset_debug or self.debuglevel == -1: + if len(string.strip()) > 0: + console_write(u'Urllib %s Debug Write' % self._debug_protocol, True) + for line in string.strip().splitlines(): + console_write(u' ' + line.decode('iso-8859-1')) + if reset_debug: + self.debuglevel = reset_debug + + def request(self, method, url, body=None, headers={}): + original_headers = headers.copy() + + # By default urllib2 and urllib.request override the Connection header, + # however, it is preferred to be able to re-use it + original_headers['Connection'] = 'Keep-Alive' + + HTTPConnection.request(self, method, url, body, original_headers) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/debuggable_http_handler.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/debuggable_http_handler.py new file mode 100644 index 0000000..ae4b8d1 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/debuggable_http_handler.py @@ -0,0 +1,35 @@ +import sys + +try: + # Python 3 + from urllib.request import HTTPHandler +except (ImportError): + # Python 2 + from urllib2 import HTTPHandler + +from .debuggable_http_connection import DebuggableHTTPConnection +from .persistent_handler import PersistentHandler + + +class DebuggableHTTPHandler(PersistentHandler, HTTPHandler): + """ + A custom HTTPHandler that formats debugging info for Sublime Text + """ + + def __init__(self, debuglevel=0, debug=False, **kwargs): + # This is a special value that will not trigger the standard debug + # functionality, but custom code where we can format the output + if debug: + self._debuglevel = 5 + else: + self._debuglevel = debuglevel + self.passwd = kwargs.get('passwd') + + def http_open(self, req): + def http_class_wrapper(host, **kwargs): + kwargs['passwd'] = self.passwd + if 'debuglevel' not in kwargs: + kwargs['debuglevel'] = self._debuglevel + return DebuggableHTTPConnection(host, **kwargs) + + return self.do_open(http_class_wrapper, req) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/debuggable_http_response.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/debuggable_http_response.py new file mode 100644 index 0000000..2dd3af6 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/debuggable_http_response.py @@ -0,0 +1,66 @@ +try: + # Python 3 + from http.client import HTTPResponse, IncompleteRead +except (ImportError): + # Python 2 + from httplib import HTTPResponse, IncompleteRead + +from ..console_write import console_write + + +class DebuggableHTTPResponse(HTTPResponse): + """ + A custom HTTPResponse that formats debugging info for Sublime Text + """ + + _debug_protocol = 'HTTP' + + def __init__(self, sock, debuglevel=0, method=None, **kwargs): + # We have to use a positive debuglevel to get it passed to here, + # however we don't want to use it because by default debugging prints + # to the stdout and we can't capture it, so we use a special -1 value + if debuglevel == 5: + debuglevel = -1 + HTTPResponse.__init__(self, sock, debuglevel=debuglevel, method=method) + + def begin(self): + return_value = HTTPResponse.begin(self) + if self.debuglevel == -1: + console_write(u'Urllib %s Debug Read' % self._debug_protocol, True) + + # Python 2 + if hasattr(self.msg, 'headers'): + headers = self.msg.headers + # Python 3 + else: + headers = [] + for header in self.msg: + headers.append("%s: %s" % (header, self.msg[header])) + + versions = { + 9: 'HTTP/0.9', + 10: 'HTTP/1.0', + 11: 'HTTP/1.1' + } + status_line = versions[self.version] + ' ' + str(self.status) + ' ' + self.reason + headers.insert(0, status_line) + for line in headers: + console_write(u" %s" % line.rstrip()) + return return_value + + def is_keep_alive(self): + # Python 2 + if hasattr(self.msg, 'headers'): + connection = self.msg.getheader('connection') + # Python 3 + else: + connection = self.msg['connection'] + if connection and connection.lower() == 'keep-alive': + return True + return False + + def read(self, *args): + try: + return HTTPResponse.read(self, *args) + except (IncompleteRead) as e: + return e.partial diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/debuggable_https_response.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/debuggable_https_response.py new file mode 100644 index 0000000..edc9fb0 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/debuggable_https_response.py @@ -0,0 +1,9 @@ +from .debuggable_http_response import DebuggableHTTPResponse + + +class DebuggableHTTPSResponse(DebuggableHTTPResponse): + """ + A version of DebuggableHTTPResponse that sets the debug protocol to HTTPS + """ + + _debug_protocol = 'HTTPS' diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/invalid_certificate_exception.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/invalid_certificate_exception.py new file mode 100644 index 0000000..2715707 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/invalid_certificate_exception.py @@ -0,0 +1,25 @@ +try: + # Python 3 + from http.client import HTTPException + from urllib.error import URLError +except (ImportError): + # Python 2 + from httplib import HTTPException + from urllib2 import URLError + + +class InvalidCertificateException(HTTPException, URLError): + """ + An exception for when an SSL certification is not valid for the URL + it was presented for. + """ + + def __init__(self, host, cert, reason): + HTTPException.__init__(self) + self.host = host + self.cert = cert + self.reason = reason + + def __str__(self): + return ('Host %s returned an invalid certificate (%s) %s\n' % + (self.host, self.reason, self.cert)) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/persistent_handler.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/persistent_handler.py new file mode 100644 index 0000000..4bfd3d7 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/persistent_handler.py @@ -0,0 +1,116 @@ +import sys +import socket + +try: + # Python 3 + from urllib.error import URLError +except ImportError: + # Python 2 + from urllib2 import URLError + from urllib import addinfourl + +from ..console_write import console_write + + +class PersistentHandler: + connection = None + use_count = 0 + + def close(self): + if self.connection: + if self._debuglevel == 5: + s = '' if self.use_count == 1 else 's' + console_write(u"Urllib %s Debug General" % self.connection._debug_protocol, True) + console_write(u" Closing connection to %s on port %s after %s request%s" % ( + self.connection.host, self.connection.port, self.use_count, s)) + self.connection.close() + self.connection = None + self.use_count = 0 + + def do_open(self, http_class, req): + # Large portions from Python 3.3 Lib/urllib/request.py and + # Python 2.6 Lib/urllib2.py + + if sys.version_info >= (3,): + host = req.host + else: + host = req.get_host() + + if not host: + raise URLError('no host given') + + if self.connection and self.connection.host != host: + self.close() + + # Re-use the connection if possible + self.use_count += 1 + if not self.connection: + h = http_class(host, timeout=req.timeout) + else: + h = self.connection + if self._debuglevel == 5: + console_write(u"Urllib %s Debug General" % h._debug_protocol, True) + console_write(u" Re-using connection to %s on port %s for request #%s" % ( + h.host, h.port, self.use_count)) + + if sys.version_info >= (3,): + headers = dict(req.unredirected_hdrs) + headers.update(dict((k, v) for k, v in req.headers.items() + if k not in headers)) + headers = dict((name.title(), val) for name, val in headers.items()) + + else: + h.set_debuglevel(self._debuglevel) + + headers = dict(req.headers) + headers.update(req.unredirected_hdrs) + headers = dict( + (name.title(), val) for name, val in headers.items()) + + if req._tunnel_host and not self.connection: + tunnel_headers = {} + proxy_auth_hdr = "Proxy-Authorization" + if proxy_auth_hdr in headers: + tunnel_headers[proxy_auth_hdr] = headers[proxy_auth_hdr] + del headers[proxy_auth_hdr] + + if sys.version_info >= (3,): + h.set_tunnel(req._tunnel_host, headers=tunnel_headers) + else: + h._set_tunnel(req._tunnel_host, headers=tunnel_headers) + + try: + if sys.version_info >= (3,): + h.request(req.get_method(), req.selector, req.data, headers) + else: + h.request(req.get_method(), req.get_selector(), req.data, headers) + except socket.error as err: # timeout error + h.close() + raise URLError(err) + else: + r = h.getresponse() + + # Keep the connection around for re-use + if r.is_keep_alive(): + self.connection = h + else: + if self._debuglevel == 5: + s = '' if self.use_count == 1 else 's' + console_write(u"Urllib %s Debug General" % h._debug_protocol, True) + console_write(u" Closing connection to %s on port %s after %s request%s" % ( + h.host, h.port, self.use_count, s)) + self.use_count = 0 + self.connection = None + + if sys.version_info >= (3,): + r.url = req.get_full_url() + r.msg = r.reason + return r + + r.recv = r.read + fp = socket._fileobject(r, close=True) + + resp = addinfourl(fp, r.msg, req.get_full_url()) + resp.code = r.status + resp.msg = r.reason + return resp diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/validating_https_connection.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/validating_https_connection.py new file mode 100644 index 0000000..a01afdb --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/validating_https_connection.py @@ -0,0 +1,345 @@ +import re +import socket +import base64 +import hashlib +import os +import sys + +try: + # Python 3 + from http.client import HTTPS_PORT + from urllib.request import parse_keqv_list, parse_http_list +except (ImportError): + # Python 2 + from httplib import HTTPS_PORT + from urllib2 import parse_keqv_list, parse_http_list + +from ..console_write import console_write +from .debuggable_https_response import DebuggableHTTPSResponse +from .debuggable_http_connection import DebuggableHTTPConnection +from .invalid_certificate_exception import InvalidCertificateException + + +# The following code is wrapped in a try because the Linux versions of Sublime +# Text do not include the ssl module due to the fact that different distros +# have different versions +try: + import ssl + + class ValidatingHTTPSConnection(DebuggableHTTPConnection): + """ + A custom HTTPConnection class that validates SSL certificates, and + allows proxy authentication for HTTPS connections. + """ + + default_port = HTTPS_PORT + + response_class = DebuggableHTTPSResponse + _debug_protocol = 'HTTPS' + + def __init__(self, host, port=None, key_file=None, cert_file=None, + ca_certs=None, **kwargs): + passed_args = {} + if 'timeout' in kwargs: + passed_args['timeout'] = kwargs['timeout'] + if 'debug' in kwargs: + passed_args['debug'] = kwargs['debug'] + DebuggableHTTPConnection.__init__(self, host, port, **passed_args) + + self.passwd = kwargs.get('passwd') + self.key_file = key_file + self.cert_file = cert_file + self.ca_certs = ca_certs + if 'user_agent' in kwargs: + self.user_agent = kwargs['user_agent'] + if self.ca_certs: + self.cert_reqs = ssl.CERT_REQUIRED + else: + self.cert_reqs = ssl.CERT_NONE + + def get_valid_hosts_for_cert(self, cert): + """ + Returns a list of valid hostnames for an SSL certificate + + :param cert: A dict from SSLSocket.getpeercert() + + :return: An array of hostnames + """ + + if 'subjectAltName' in cert: + return [x[1] for x in cert['subjectAltName'] + if x[0].lower() == 'dns'] + else: + return [x[0][1] for x in cert['subject'] + if x[0][0].lower() == 'commonname'] + + def validate_cert_host(self, cert, hostname): + """ + Checks if the cert is valid for the hostname + + :param cert: A dict from SSLSocket.getpeercert() + + :param hostname: A string hostname to check + + :return: A boolean if the cert is valid for the hostname + """ + + hosts = self.get_valid_hosts_for_cert(cert) + for host in hosts: + host_re = host.replace('.', '\.').replace('*', '[^.]*') + if re.search('^%s$' % (host_re,), hostname, re.I): + return True + return False + + def _tunnel(self): + """ + This custom _tunnel method allows us to read and print the debug + log for the whole response before throwing an error, and adds + support for proxy authentication + """ + + self._proxy_host = self.host + self._proxy_port = self.port + self._set_hostport(self._tunnel_host, self._tunnel_port) + + self._tunnel_headers['Host'] = u"%s:%s" % (self.host, self.port) + self._tunnel_headers['User-Agent'] = self.user_agent + self._tunnel_headers['Proxy-Connection'] = 'Keep-Alive' + + request = "CONNECT %s:%d HTTP/1.1\r\n" % (self.host, self.port) + for header, value in self._tunnel_headers.items(): + request += "%s: %s\r\n" % (header, value) + request += "\r\n" + + if sys.version_info >= (3,): + request = bytes(request, 'iso-8859-1') + + self.send(request) + + response = self.response_class(self.sock, method=self._method) + (version, code, message) = response._read_status() + + status_line = u"%s %s %s" % (version, code, message.rstrip()) + headers = [status_line] + + if self.debuglevel in [-1, 5]: + console_write(u'Urllib %s Debug Read' % self._debug_protocol, True) + console_write(u" %s" % status_line) + + content_length = 0 + close_connection = False + while True: + line = response.fp.readline() + + if sys.version_info >= (3,): + line = str(line, encoding='iso-8859-1') + + if line == '\r\n': + break + + headers.append(line.rstrip()) + + parts = line.rstrip().split(': ', 1) + name = parts[0].lower() + value = parts[1].lower().strip() + if name == 'content-length': + content_length = int(value) + + if name in ['connection', 'proxy-connection'] and value == 'close': + close_connection = True + + if self.debuglevel in [-1, 5]: + console_write(u" %s" % line.rstrip()) + + # Handle proxy auth for SSL connections since regular urllib punts on this + if code == 407 and self.passwd and 'Proxy-Authorization' not in self._tunnel_headers: + if content_length: + response._safe_read(content_length) + + supported_auth_methods = {} + for line in headers: + parts = line.split(': ', 1) + if parts[0].lower() != 'proxy-authenticate': + continue + details = parts[1].split(' ', 1) + supported_auth_methods[details[0].lower()] = details[1] if len(details) > 1 else '' + + username, password = self.passwd.find_user_password(None, "%s:%s" % ( + self._proxy_host, self._proxy_port)) + + if 'digest' in supported_auth_methods: + response_value = self.build_digest_response( + supported_auth_methods['digest'], username, password) + if response_value: + self._tunnel_headers['Proxy-Authorization'] = u"Digest %s" % response_value + + elif 'basic' in supported_auth_methods: + response_value = u"%s:%s" % (username, password) + response_value = base64.b64encode(response_value).strip() + self._tunnel_headers['Proxy-Authorization'] = u"Basic %s" % response_value + + if 'Proxy-Authorization' in self._tunnel_headers: + self.host = self._proxy_host + self.port = self._proxy_port + + # If the proxy wanted the connection closed, we need to make a new connection + if close_connection: + self.sock.close() + self.sock = socket.create_connection((self.host, self.port), self.timeout) + + return self._tunnel() + + if code != 200: + self.close() + raise socket.error("Tunnel connection failed: %d %s" % (code, + message.strip())) + + def build_digest_response(self, fields, username, password): + """ + Takes a Proxy-Authenticate: Digest header and creates a response + header + + :param fields: + The string portion of the Proxy-Authenticate header after + "Digest " + + :param username: + The username to use for the response + + :param password: + The password to use for the response + + :return: + None if invalid Proxy-Authenticate header, otherwise the + string of fields for the Proxy-Authorization: Digest header + """ + + fields = parse_keqv_list(parse_http_list(fields)) + + realm = fields.get('realm') + nonce = fields.get('nonce') + qop = fields.get('qop') + algorithm = fields.get('algorithm') + if algorithm: + algorithm = algorithm.lower() + opaque = fields.get('opaque') + + if algorithm in ['md5', None]: + def md5hash(string): + return hashlib.md5(string).hexdigest() + hash = md5hash + + elif algorithm == 'sha': + def sha1hash(string): + return hashlib.sha1(string).hexdigest() + hash = sha1hash + + else: + return None + + host_port = u"%s:%s" % (self.host, self.port) + + a1 = "%s:%s:%s" % (username, realm, password) + a2 = "CONNECT:%s" % host_port + ha1 = hash(a1) + ha2 = hash(a2) + + if qop == None: + response = hash(u"%s:%s:%s" % (ha1, nonce, ha2)) + elif qop == 'auth': + nc = '00000001' + cnonce = hash(os.urandom(8))[:8] + response = hash(u"%s:%s:%s:%s:%s:%s" % (ha1, nonce, nc, cnonce, qop, ha2)) + else: + return None + + response_fields = { + 'username': username, + 'realm': realm, + 'nonce': nonce, + 'response': response, + 'uri': host_port + } + if algorithm: + response_fields['algorithm'] = algorithm + if qop == 'auth': + response_fields['nc'] = nc + response_fields['cnonce'] = cnonce + response_fields['qop'] = qop + if opaque: + response_fields['opaque'] = opaque + + return ', '.join([u"%s=\"%s\"" % (field, response_fields[field]) for field in response_fields]) + + def connect(self): + """ + Adds debugging and SSL certification validation + """ + + if self.debuglevel == -1: + console_write(u"Urllib HTTPS Debug General", True) + console_write(u" Connecting to %s on port %s" % (self.host, self.port)) + + self.sock = socket.create_connection((self.host, self.port), self.timeout) + if self._tunnel_host: + self._tunnel() + + if self.debuglevel == -1: + console_write(u"Urllib HTTPS Debug General", True) + console_write(u" Connecting to %s on port %s" % (self.host, self.port)) + console_write(u" CA certs file at %s" % (self.ca_certs.decode(sys.getfilesystemencoding()))) + + self.sock = ssl.wrap_socket(self.sock, keyfile=self.key_file, + certfile=self.cert_file, cert_reqs=self.cert_reqs, + ca_certs=self.ca_certs) + + if self.debuglevel == -1: + console_write(u" Successfully upgraded connection to %s:%s with SSL" % ( + self.host, self.port)) + + # This debugs and validates the SSL certificate + if self.cert_reqs & ssl.CERT_REQUIRED: + cert = self.sock.getpeercert() + + if self.debuglevel == -1: + subjectMap = { + 'organizationName': 'O', + 'commonName': 'CN', + 'organizationalUnitName': 'OU', + 'countryName': 'C', + 'serialNumber': 'serialNumber', + 'commonName': 'CN', + 'localityName': 'L', + 'stateOrProvinceName': 'S' + } + subject_list = list(cert['subject']) + subject_list.reverse() + subject_parts = [] + for pair in subject_list: + if pair[0][0] in subjectMap: + field_name = subjectMap[pair[0][0]] + else: + field_name = pair[0][0] + subject_parts.append(field_name + '=' + pair[0][1]) + + console_write(u" Server SSL certificate:") + console_write(u" subject: " + ','.join(subject_parts)) + if 'subjectAltName' in cert: + console_write(u" common name: " + cert['subjectAltName'][0][1]) + if 'notAfter' in cert: + console_write(u" expire date: " + cert['notAfter']) + + hostname = self.host.split(':', 0)[0] + + if not self.validate_cert_host(cert, hostname): + if self.debuglevel == -1: + console_write(u" Certificate INVALID") + + raise InvalidCertificateException(hostname, cert, + 'hostname mismatch') + + if self.debuglevel == -1: + console_write(u" Certificate validated for %s" % hostname) + +except (ImportError): + pass diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/validating_https_handler.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/validating_https_handler.py new file mode 100644 index 0000000..5b02c7a --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/validating_https_handler.py @@ -0,0 +1,59 @@ +try: + # Python 3 + from urllib.error import URLError + import urllib.request as urllib_compat +except (ImportError): + # Python 2 + from urllib2 import URLError + import urllib2 as urllib_compat + + +# The following code is wrapped in a try because the Linux versions of Sublime +# Text do not include the ssl module due to the fact that different distros +# have different versions +try: + import ssl + + from .validating_https_connection import ValidatingHTTPSConnection + from .invalid_certificate_exception import InvalidCertificateException + from .persistent_handler import PersistentHandler + + if hasattr(urllib_compat, 'HTTPSHandler'): + class ValidatingHTTPSHandler(PersistentHandler, urllib_compat.HTTPSHandler): + """ + A urllib handler that validates SSL certificates for HTTPS requests + """ + + def __init__(self, **kwargs): + # This is a special value that will not trigger the standard debug + # functionality, but custom code where we can format the output + self._debuglevel = 0 + if 'debug' in kwargs and kwargs['debug']: + self._debuglevel = 5 + elif 'debuglevel' in kwargs: + self._debuglevel = kwargs['debuglevel'] + self._connection_args = kwargs + + def https_open(self, req): + def http_class_wrapper(host, **kwargs): + full_kwargs = dict(self._connection_args) + full_kwargs.update(kwargs) + return ValidatingHTTPSConnection(host, **full_kwargs) + + try: + return self.do_open(http_class_wrapper, req) + except URLError as e: + if type(e.reason) == ssl.SSLError and e.reason.args[0] == 1: + raise InvalidCertificateException(req.host, '', + e.reason.args[1]) + raise + + https_request = urllib_compat.AbstractHTTPHandler.do_request_ + else: + raise ImportError() + +except (ImportError) as e: + + class ValidatingHTTPSHandler(): + def __init__(self, **kwargs): + raise e diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http_cache.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http_cache.py new file mode 100644 index 0000000..2f6f3a2 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http_cache.py @@ -0,0 +1,75 @@ +import os +import time + +import sublime + +from .open_compat import open_compat, read_compat + + +class HttpCache(object): + """ + A data store for caching HTTP response data. + """ + + def __init__(self, ttl): + self.base_path = os.path.join(sublime.packages_path(), 'User', 'Package Control.cache') + if not os.path.exists(self.base_path): + os.mkdir(self.base_path) + self.clear(int(ttl)) + + def clear(self, ttl): + """ + Removes all cache entries older than the TTL + + :param ttl: + The number of seconds a cache entry should be valid for + """ + + ttl = int(ttl) + + for filename in os.listdir(self.base_path): + path = os.path.join(self.base_path, filename) + # There should not be any folders in the cache dir, but we + # ignore to prevent an exception + if os.path.isdir(path): + continue + mtime = os.stat(path).st_mtime + if mtime < time.time() - ttl: + os.unlink(path) + + def get(self, key): + """ + Returns a cached value + + :param key: + The key to fetch the cache for + + :return: + The (binary) cached value, or False + """ + + cache_file = os.path.join(self.base_path, key) + if not os.path.exists(cache_file): + return False + + with open_compat(cache_file, 'rb') as f: + return read_compat(f) + + def has(self, key): + cache_file = os.path.join(self.base_path, key) + return os.path.exists(cache_file) + + def set(self, key, content): + """ + Saves a value in the cache + + :param key: + The key to save the cache with + + :param content: + The (binary) content to cache + """ + + cache_file = os.path.join(self.base_path, key) + with open_compat(cache_file, 'wb') as f: + f.write(content) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/open_compat.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/open_compat.py new file mode 100644 index 0000000..b22f066 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/open_compat.py @@ -0,0 +1,27 @@ +import os +import sys + +from .file_not_found_error import FileNotFoundError + + +def open_compat(path, mode='r'): + if mode in ['r', 'rb'] and not os.path.exists(path): + raise FileNotFoundError(u"The file \"%s\" could not be found" % path) + + if sys.version_info >= (3,): + encoding = 'utf-8' + errors = 'replace' + if mode in ['rb', 'wb', 'ab']: + encoding = None + errors = None + return open(path, mode, encoding=encoding, errors=errors) + + else: + return open(path, mode) + + +def read_compat(file_obj): + if sys.version_info >= (3,): + return file_obj.read() + else: + return unicode(file_obj.read(), 'utf-8', errors='replace') diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_cleanup.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_cleanup.py new file mode 100644 index 0000000..352f4d4 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_cleanup.py @@ -0,0 +1,107 @@ +import threading +import os +import shutil + +import sublime + +from .show_error import show_error +from .console_write import console_write +from .unicode import unicode_from_os +from .clear_directory import clear_directory +from .automatic_upgrader import AutomaticUpgrader +from .package_manager import PackageManager +from .package_renamer import PackageRenamer +from .open_compat import open_compat +from .package_io import package_file_exists + + +class PackageCleanup(threading.Thread, PackageRenamer): + """ + Cleans up folders for packages that were removed, but that still have files + in use. + """ + + def __init__(self): + self.manager = PackageManager() + self.load_settings() + threading.Thread.__init__(self) + + def run(self): + found_pkgs = [] + installed_pkgs = list(self.installed_packages) + for package_name in os.listdir(sublime.packages_path()): + package_dir = os.path.join(sublime.packages_path(), package_name) + + # Cleanup packages that could not be removed due to in-use files + cleanup_file = os.path.join(package_dir, 'package-control.cleanup') + if os.path.exists(cleanup_file): + try: + shutil.rmtree(package_dir) + console_write(u'Removed old directory for package %s' % package_name, True) + + except (OSError) as e: + if not os.path.exists(cleanup_file): + open_compat(cleanup_file, 'w').close() + + error_string = (u'Unable to remove old directory for package ' + + u'%s - deferring until next start: %s') % ( + package_name, unicode_from_os(e)) + console_write(error_string, True) + + # Finish reinstalling packages that could not be upgraded due to + # in-use files + reinstall = os.path.join(package_dir, 'package-control.reinstall') + if os.path.exists(reinstall): + metadata_path = os.path.join(package_dir, 'package-metadata.json') + if not clear_directory(package_dir, [metadata_path]): + if not os.path.exists(reinstall): + open_compat(reinstall, 'w').close() + # Assigning this here prevents the callback from referencing the value + # of the "package_name" variable when it is executed + restart_message = (u'An error occurred while trying to ' + + u'finish the upgrade of %s. You will most likely need to ' + + u'restart your computer to complete the upgrade.') % package_name + + def show_still_locked(): + show_error(restart_message) + sublime.set_timeout(show_still_locked, 10) + else: + self.manager.install_package(package_name) + + # This adds previously installed packages from old versions of PC + if package_file_exists(package_name, 'package-metadata.json') and \ + package_name not in self.installed_packages: + installed_pkgs.append(package_name) + params = { + 'package': package_name, + 'operation': 'install', + 'version': \ + self.manager.get_metadata(package_name).get('version') + } + self.manager.record_usage(params) + + found_pkgs.append(package_name) + + if int(sublime.version()) >= 3000: + package_files = os.listdir(sublime.installed_packages_path()) + found_pkgs += [file.replace('.sublime-package', '') for file in package_files] + + sublime.set_timeout(lambda: self.finish(installed_pkgs, found_pkgs), 10) + + def finish(self, installed_pkgs, found_pkgs): + """ + A callback that can be run the main UI thread to perform saving of the + Package Control.sublime-settings file. Also fires off the + :class:`AutomaticUpgrader`. + + :param installed_pkgs: + A list of the string package names of all "installed" packages, + even ones that do not appear to be in the filesystem. + + :param found_pkgs: + A list of the string package names of all packages that are + currently installed on the filesystem. + """ + + self.save_packages(installed_pkgs) + AutomaticUpgrader(found_pkgs).start() diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_creator.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_creator.py new file mode 100644 index 0000000..47a3087 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_creator.py @@ -0,0 +1,39 @@ +import os + +from .show_error import show_error +from .package_manager import PackageManager + + +class PackageCreator(): + """ + Abstract class for commands that create .sublime-package files + """ + + def show_panel(self): + """ + Shows a list of packages that can be turned into a .sublime-package file + """ + + self.manager = PackageManager() + self.packages = self.manager.list_packages(unpacked_only=True) + if not self.packages: + show_error('There are no packages available to be packaged') + return + self.window.show_quick_panel(self.packages, self.on_done) + + def get_package_destination(self): + """ + Retrieves the destination for .sublime-package files + + :return: + A string - the path to the folder to save .sublime-package files in + """ + + destination = self.manager.settings.get('package_destination') + + # We check destination via an if statement instead of using + # the dict.get() method since the key may be set, but to a blank value + if not destination: + destination = os.path.join(os.path.expanduser('~'), 'Desktop') + + return destination diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_installer.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_installer.py new file mode 100644 index 0000000..9c8809c --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_installer.py @@ -0,0 +1,247 @@ +import os +import re +import threading + +import sublime + +from .preferences_filename import preferences_filename +from .thread_progress import ThreadProgress +from .package_manager import PackageManager +from .upgraders.git_upgrader import GitUpgrader +from .upgraders.hg_upgrader import HgUpgrader +from .versions import version_comparable + + +class PackageInstaller(): + """ + Provides helper functionality related to installing packages + """ + + def __init__(self): + self.manager = PackageManager() + + def make_package_list(self, ignore_actions=[], override_action=None, + ignore_packages=[]): + """ + Creates a list of packages and what operation would be performed for + each. Allows filtering by the applicable action or package name. + Returns the information in a format suitable for displaying in the + quick panel. + + :param ignore_actions: + A list of actions to ignore packages by. Valid actions include: + `install`, `upgrade`, `downgrade`, `reinstall`, `overwrite`, + `pull` and `none`. `pull` andd `none` are for Git and Hg + repositories. `pull` is present when incoming changes are detected, + where as `none` is selected if no commits are available. `overwrite` + is for packages that do not include version information via the + `package-metadata.json` file. + + :param override_action: + A string action name to override the displayed action for all listed + packages. + + :param ignore_packages: + A list of packages names that should not be returned in the list + + :return: + A list of lists, each containing three strings: + 0 - package name + 1 - package description + 2 - action; [extra info;] package url + """ + + packages = self.manager.list_available_packages() + installed_packages = self.manager.list_packages() + + package_list = [] + for package in sorted(iter(packages.keys()), key=lambda s: s.lower()): + if ignore_packages and package in ignore_packages: + continue + package_entry = [package] + info = packages[package] + download = info['download'] + + if package in installed_packages: + installed = True + metadata = self.manager.get_metadata(package) + if metadata.get('version'): + installed_version = metadata['version'] + else: + installed_version = None + else: + installed = False + + installed_version_name = 'v' + installed_version if \ + installed and installed_version else 'unknown version' + new_version = 'v' + download['version'] + + vcs = None + package_dir = self.manager.get_package_dir(package) + settings = self.manager.settings + + if override_action: + action = override_action + extra = '' + + else: + if os.path.exists(os.path.join(package_dir, '.git')): + if settings.get('ignore_vcs_packages'): + continue + vcs = 'git' + incoming = GitUpgrader(settings.get('git_binary'), + settings.get('git_update_command'), package_dir, + settings.get('cache_length'), settings.get('debug') + ).incoming() + elif os.path.exists(os.path.join(package_dir, '.hg')): + if settings.get('ignore_vcs_packages'): + continue + vcs = 'hg' + incoming = HgUpgrader(settings.get('hg_binary'), + settings.get('hg_update_command'), package_dir, + settings.get('cache_length'), settings.get('debug') + ).incoming() + + if installed: + if vcs: + if incoming: + action = 'pull' + extra = ' with ' + vcs + else: + action = 'none' + extra = '' + elif not installed_version: + action = 'overwrite' + extra = ' %s with %s' % (installed_version_name, + new_version) + else: + installed_version = version_comparable(installed_version) + download_version = version_comparable(download['version']) + if download_version > installed_version: + action = 'upgrade' + extra = ' to %s from %s' % (new_version, + installed_version_name) + elif download_version < installed_version: + action = 'downgrade' + extra = ' to %s from %s' % (new_version, + installed_version_name) + else: + action = 'reinstall' + extra = ' %s' % new_version + else: + action = 'install' + extra = ' %s' % new_version + extra += ';' + + if action in ignore_actions: + continue + + description = info.get('description') + if not description: + description = 'No description provided' + package_entry.append(description) + package_entry.append(action + extra + ' ' + + re.sub('^https?://', '', info['homepage'])) + package_list.append(package_entry) + return package_list + + def disable_packages(self, packages): + """ + Disables one or more packages before installing or upgrading to prevent + errors where Sublime Text tries to read files that no longer exist, or + read a half-written file. + + :param packages: The string package name, or an array of strings + """ + + if not isinstance(packages, list): + packages = [packages] + + # Don't disable Package Control so it does not get stuck disabled + if 'Package Control' in packages: + packages.remove('Package Control') + + disabled = [] + + settings = sublime.load_settings(preferences_filename()) + ignored = settings.get('ignored_packages') + if not ignored: + ignored = [] + for package in packages: + if not package in ignored: + ignored.append(package) + disabled.append(package) + settings.set('ignored_packages', ignored) + sublime.save_settings(preferences_filename()) + return disabled + + def reenable_package(self, package): + """ + Re-enables a package after it has been installed or upgraded + + :param package: The string package name + """ + + settings = sublime.load_settings(preferences_filename()) + ignored = settings.get('ignored_packages') + if not ignored: + return + if package in ignored: + settings.set('ignored_packages', + list(set(ignored) - set([package]))) + sublime.save_settings(preferences_filename()) + + def on_done(self, picked): + """ + Quick panel user selection handler - disables a package, installs or + upgrades it, then re-enables the package + + :param picked: + An integer of the 0-based package name index from the presented + list. -1 means the user cancelled. + """ + + if picked == -1: + return + name = self.package_list[picked][0] + + if name in self.disable_packages(name): + on_complete = lambda: self.reenable_package(name) + else: + on_complete = None + + thread = PackageInstallerThread(self.manager, name, on_complete) + thread.start() + ThreadProgress(thread, 'Installing package %s' % name, + 'Package %s successfully %s' % (name, self.completion_type)) + + +class PackageInstallerThread(threading.Thread): + """ + A thread to run package install/upgrade operations in so that the main + Sublime Text thread does not get blocked and freeze the UI + """ + + def __init__(self, manager, package, on_complete): + """ + :param manager: + An instance of :class:`PackageManager` + + :param package: + The string package name to install/upgrade + + :param on_complete: + A callback to run after installing/upgrading the package + """ + + self.package = package + self.manager = manager + self.on_complete = on_complete + threading.Thread.__init__(self) + + def run(self): + try: + self.result = self.manager.install_package(self.package) + finally: + if self.on_complete: + sublime.set_timeout(self.on_complete, 1) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_io.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_io.py new file mode 100644 index 0000000..14ab134 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_io.py @@ -0,0 +1,126 @@ +import os +import zipfile + +import sublime + +from .console_write import console_write +from .open_compat import open_compat, read_compat +from .unicode import unicode_from_os +from .file_not_found_error import FileNotFoundError + + +def read_package_file(package, relative_path, binary=False, debug=False): + package_dir = _get_package_dir(package) + file_path = os.path.join(package_dir, relative_path) + + if os.path.exists(package_dir): + result = _read_regular_file(package, relative_path, binary, debug) + if result != False: + return result + + if int(sublime.version()) >= 3000: + result = _read_zip_file(package, relative_path, binary, debug) + if result != False: + return result + + if debug: + console_write(u"Unable to find file %s in the package %s" % (relative_path, package), True) + return False + + +def package_file_exists(package, relative_path): + package_dir = _get_package_dir(package) + file_path = os.path.join(package_dir, relative_path) + + if os.path.exists(package_dir): + result = _regular_file_exists(package, relative_path) + if result: + return result + + if int(sublime.version()) >= 3000: + return _zip_file_exists(package, relative_path) + + return False + + +def _get_package_dir(package): + """:return: The full filesystem path to the package directory""" + + return os.path.join(sublime.packages_path(), package) + + +def _read_regular_file(package, relative_path, binary=False, debug=False): + package_dir = _get_package_dir(package) + file_path = os.path.join(package_dir, relative_path) + try: + with open_compat(file_path, ('rb' if binary else 'r')) as f: + return read_compat(f) + + except (FileNotFoundError) as e: + if debug: + console_write(u"Unable to find file %s in the package folder for %s" % (relative_path, package), True) + return False + + +def _read_zip_file(package, relative_path, binary=False, debug=False): + zip_path = os.path.join(sublime.installed_packages_path(), + package + '.sublime-package') + + if not os.path.exists(zip_path): + if debug: + console_write(u"Unable to find a sublime-package file for %s" % package, True) + return False + + try: + package_zip = zipfile.ZipFile(zip_path, 'r') + + except (zipfile.BadZipfile): + console_write(u'An error occurred while trying to unzip the sublime-package file for %s.' % package, True) + return False + + try: + contents = package_zip.read(relative_path) + if not binary: + contents = contents.decode('utf-8') + return contents + + except (KeyError) as e: + if debug: + console_write(u"Unable to find file %s in the sublime-package file for %s" % (relative_path, package), True) + + except (IOError) as e: + message = unicode_from_os(e) + console_write(u'Unable to read file from sublime-package file for %s due to an invalid filename' % package, True) + + except (UnicodeDecodeError): + console_write(u'Unable to read file from sublime-package file for %s due to an invalid filename or character encoding issue' % package, True) + + return False + + +def _regular_file_exists(package, relative_path): + package_dir = _get_package_dir(package) + file_path = os.path.join(package_dir, relative_path) + return os.path.exists(file_path) + + +def _zip_file_exists(package, relative_path): + zip_path = os.path.join(sublime.installed_packages_path(), + package + '.sublime-package') + + if not os.path.exists(zip_path): + return False + + try: + package_zip = zipfile.ZipFile(zip_path, 'r') + + except (zipfile.BadZipfile): + console_write(u'An error occurred while trying to unzip the sublime-package file for %s.' % package_name, True) + return False + + try: + package_zip.getinfo(relative_path) + return True + + except (KeyError) as e: + return False diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_manager.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_manager.py new file mode 100644 index 0000000..03e4120 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_manager.py @@ -0,0 +1,1024 @@ +import sys +import os +import re +import socket +import json +import time +import zipfile +import shutil +from fnmatch import fnmatch +import datetime +import tempfile +import locale + +try: + # Python 3 + from urllib.parse import urlencode, urlparse + import compileall + str_cls = str +except (ImportError): + # Python 2 + from urllib import urlencode + from urlparse import urlparse + str_cls = unicode + +import sublime + +from .show_error import show_error +from .console_write import console_write +from .open_compat import open_compat, read_compat +from .unicode import unicode_from_os +from .clear_directory import clear_directory +from .cache import (clear_cache, set_cache, get_cache, merge_cache_under_settings, + merge_cache_over_settings, set_cache_under_settings, set_cache_over_settings) +from .versions import version_comparable, version_sort +from .downloaders.background_downloader import BackgroundDownloader +from .downloaders.downloader_exception import DownloaderException +from .providers.provider_exception import ProviderException +from .clients.client_exception import ClientException +from .download_manager import downloader +from .providers.channel_provider import ChannelProvider +from .upgraders.git_upgrader import GitUpgrader +from .upgraders.hg_upgrader import HgUpgrader +from .package_io import read_package_file +from .providers import CHANNEL_PROVIDERS, REPOSITORY_PROVIDERS +from . import __version__ + + +class PackageManager(): + """ + Allows downloading, creating, installing, upgrading, and deleting packages + + Delegates metadata retrieval to the CHANNEL_PROVIDERS classes. + Uses VcsUpgrader-based classes for handling git and hg repositories in the + Packages folder. Downloader classes are utilized to fetch contents of URLs. + + Also handles displaying package messaging, and sending usage information to + the usage server. + """ + + def __init__(self): + # Here we manually copy the settings since sublime doesn't like + # code accessing settings from threads + self.settings = {} + settings = sublime.load_settings('Package Control.sublime-settings') + for setting in ['timeout', 'repositories', 'channels', + 'package_name_map', 'dirs_to_ignore', 'files_to_ignore', + 'package_destination', 'cache_length', 'auto_upgrade', + 'files_to_ignore_binary', 'files_to_keep', 'dirs_to_keep', + 'git_binary', 'git_update_command', 'hg_binary', + 'hg_update_command', 'http_proxy', 'https_proxy', + 'auto_upgrade_ignore', 'auto_upgrade_frequency', + 'submit_usage', 'submit_url', 'renamed_packages', + 'files_to_include', 'files_to_include_binary', 'certs', + 'ignore_vcs_packages', 'proxy_username', 'proxy_password', + 'debug', 'user_agent', 'http_cache', 'http_cache_length', + 'install_prereleases', 'openssl_binary']: + if settings.get(setting) == None: + continue + self.settings[setting] = settings.get(setting) + + # https_proxy will inherit from http_proxy unless it is set to a + # string value or false + no_https_proxy = self.settings.get('https_proxy') in ["", None] + if no_https_proxy and self.settings.get('http_proxy'): + self.settings['https_proxy'] = self.settings.get('http_proxy') + if self.settings.get('https_proxy') == False: + self.settings['https_proxy'] = '' + + self.settings['platform'] = sublime.platform() + self.settings['version'] = sublime.version() + + # Use the cache to see if settings have changed since the last + # time the package manager was created, and clearing any cached + # values if they have. + previous_settings = get_cache('filtered_settings', {}) + + # Reduce the settings down to exclude channel info since that will + # make the settings always different + filtered_settings = self.settings.copy() + for key in ['repositories', 'channels', 'package_name_map', 'cache']: + if key in filtered_settings: + del filtered_settings[key] + + if filtered_settings != previous_settings and previous_settings != {}: + console_write(u'Settings change detected, clearing cache', True) + clear_cache() + set_cache('filtered_settings', filtered_settings) + + def get_metadata(self, package): + """ + Returns the package metadata for an installed package + + :return: + A dict with the keys: + version + url + description + or an empty dict on error + """ + + try: + debug = self.settings.get('debug') + metadata_json = read_package_file(package, 'package-metadata.json', debug=debug) + if metadata_json: + return json.loads(metadata_json) + + except (IOError, ValueError) as e: + pass + + return {} + + def list_repositories(self): + """ + Returns a master list of all repositories pulled from all sources + + These repositories come from the channels specified in the + "channels" setting, plus any repositories listed in the + "repositories" setting. + + :return: + A list of all available repositories + """ + + cache_ttl = self.settings.get('cache_length') + + repositories = self.settings.get('repositories')[:] + channels = self.settings.get('channels') + for channel in channels: + channel = channel.strip() + + # Caches various info from channels for performance + cache_key = channel + '.repositories' + channel_repositories = get_cache(cache_key) + + merge_cache_under_settings(self, 'package_name_map', channel) + merge_cache_under_settings(self, 'renamed_packages', channel) + merge_cache_under_settings(self, 'unavailable_packages', channel, list_=True) + + # If any of the info was not retrieved from the cache, we need to + # grab the channel to get it + if channel_repositories == None: + + for provider_class in CHANNEL_PROVIDERS: + if provider_class.match_url(channel): + provider = provider_class(channel, self.settings) + break + + try: + channel_repositories = provider.get_repositories() + set_cache(cache_key, channel_repositories, cache_ttl) + + for repo in channel_repositories: + repo_packages = provider.get_packages(repo) + packages_cache_key = repo + '.packages' + set_cache(packages_cache_key, repo_packages, cache_ttl) + + # Have the local name map override the one from the channel + name_map = provider.get_name_map() + set_cache_under_settings(self, 'package_name_map', channel, name_map, cache_ttl) + + renamed_packages = provider.get_renamed_packages() + set_cache_under_settings(self, 'renamed_packages', channel, renamed_packages, cache_ttl) + + unavailable_packages = provider.get_unavailable_packages() + set_cache_under_settings(self, 'unavailable_packages', channel, unavailable_packages, cache_ttl, list_=True) + + provider_certs = provider.get_certs() + certs = self.settings.get('certs', {}).copy() + certs.update(provider_certs) + # Save the master list of certs, used by downloaders/cert_provider.py + set_cache('*.certs', certs, cache_ttl) + + except (DownloaderException, ClientException, ProviderException) as e: + console_write(e, True) + continue + + repositories.extend(channel_repositories) + return [repo.strip() for repo in repositories] + + def list_available_packages(self): + """ + Returns a master list of every available package from all sources + + :return: + A dict in the format: + { + 'Package Name': { + # Package details - see example-packages.json for format + }, + ... + } + """ + + if self.settings.get('debug'): + console_write(u"Fetching list of available packages", True) + console_write(u" Platform: %s-%s" % (sublime.platform(),sublime.arch())) + console_write(u" Sublime Text Version: %s" % sublime.version()) + console_write(u" Package Control Version: %s" % __version__) + + cache_ttl = self.settings.get('cache_length') + repositories = self.list_repositories() + packages = {} + bg_downloaders = {} + active = [] + repos_to_download = [] + name_map = self.settings.get('package_name_map', {}) + + # Repositories are run in reverse order so that the ones first + # on the list will overwrite those last on the list + for repo in repositories[::-1]: + cache_key = repo + '.packages' + repository_packages = get_cache(cache_key) + + if repository_packages != None: + packages.update(repository_packages) + + else: + domain = urlparse(repo).hostname + if domain not in bg_downloaders: + bg_downloaders[domain] = BackgroundDownloader( + self.settings, REPOSITORY_PROVIDERS) + bg_downloaders[domain].add_url(repo) + repos_to_download.append(repo) + + for bg_downloader in list(bg_downloaders.values()): + bg_downloader.start() + active.append(bg_downloader) + + # Wait for all of the downloaders to finish + while active: + bg_downloader = active.pop() + bg_downloader.join() + + # Grabs the results and stuff it all in the cache + for repo in repos_to_download: + domain = urlparse(repo).hostname + bg_downloader = bg_downloaders[domain] + provider = bg_downloader.get_provider(repo) + + # Allow name mapping of packages for schema version < 2.0 + repository_packages = {} + for name, info in provider.get_packages(): + name = name_map.get(name, name) + info['name'] = name + repository_packages[name] = info + + # Display errors we encountered while fetching package info + for url, exception in provider.get_failed_sources(): + console_write(exception, True) + for name, exception in provider.get_broken_packages(): + console_write(exception, True) + + cache_key = repo + '.packages' + set_cache(cache_key, repository_packages, cache_ttl) + packages.update(repository_packages) + + renamed_packages = provider.get_renamed_packages() + set_cache_under_settings(self, 'renamed_packages', repo, renamed_packages, cache_ttl) + + unavailable_packages = provider.get_unavailable_packages() + set_cache_under_settings(self, 'unavailable_packages', repo, unavailable_packages, cache_ttl, list_=True) + + return packages + + def list_packages(self, unpacked_only=False): + """ + :param unpacked_only: + Only list packages that are not inside of .sublime-package files + + :return: A list of all installed, non-default, package names + """ + + package_names = os.listdir(sublime.packages_path()) + package_names = [path for path in package_names if + os.path.isdir(os.path.join(sublime.packages_path(), path))] + + if int(sublime.version()) > 3000 and unpacked_only == False: + package_files = os.listdir(sublime.installed_packages_path()) + package_names += [f.replace('.sublime-package', '') for f in package_files if re.search('\.sublime-package$', f) != None] + + # Ignore things to be deleted + ignored = ['User'] + for package in package_names: + cleanup_file = os.path.join(sublime.packages_path(), package, + 'package-control.cleanup') + if os.path.exists(cleanup_file): + ignored.append(package) + + packages = list(set(package_names) - set(ignored) - + set(self.list_default_packages())) + packages = sorted(packages, key=lambda s: s.lower()) + + return packages + + def list_all_packages(self): + """ :return: A list of all installed package names, including default packages""" + + packages = self.list_default_packages() + self.list_packages() + packages = sorted(packages, key=lambda s: s.lower()) + return packages + + def list_default_packages(self): + """ :return: A list of all default package names""" + + if int(sublime.version()) > 3000: + bundled_packages_path = os.path.join(os.path.dirname(sublime.executable_path()), + 'Packages') + files = os.listdir(bundled_packages_path) + + else: + files = os.listdir(os.path.join(os.path.dirname( + sublime.packages_path()), 'Pristine Packages')) + files = list(set(files) - set(os.listdir( + sublime.installed_packages_path()))) + packages = [file.replace('.sublime-package', '') for file in files] + packages = sorted(packages, key=lambda s: s.lower()) + return packages + + def get_package_dir(self, package): + """:return: The full filesystem path to the package directory""" + + return os.path.join(sublime.packages_path(), package) + + def get_mapped_name(self, package): + """:return: The name of the package after passing through mapping rules""" + + return self.settings.get('package_name_map', {}).get(package, package) + + def create_package(self, package_name, package_destination, + binary_package=False): + """ + Creates a .sublime-package file from the running Packages directory + + :param package_name: + The package to create a .sublime-package file for + + :param package_destination: + The full filesystem path of the directory to save the new + .sublime-package file in. + + :param binary_package: + If the created package should follow the binary package include/ + exclude patterns from the settings. These normally include a setup + to exclude .py files and include .pyc files, but that can be + changed via settings. + + :return: bool if the package file was successfully created + """ + + package_dir = self.get_package_dir(package_name) + + if not os.path.exists(package_dir): + show_error(u'The folder for the package name specified, %s, does not exist in %s' % ( + package_name, sublime.packages_path())) + return False + + package_filename = package_name + '.sublime-package' + package_path = os.path.join(package_destination, + package_filename) + + if not os.path.exists(sublime.installed_packages_path()): + os.mkdir(sublime.installed_packages_path()) + + if os.path.exists(package_path): + os.remove(package_path) + + try: + package_file = zipfile.ZipFile(package_path, "w", + compression=zipfile.ZIP_DEFLATED) + except (OSError, IOError) as e: + show_error(u'An error occurred creating the package file %s in %s.\n\n%s' % ( + package_filename, package_destination, unicode_from_os(e))) + return False + + if int(sublime.version()) >= 3000: + compileall.compile_dir(package_dir, quiet=True, legacy=True, optimize=2) + + dirs_to_ignore = self.settings.get('dirs_to_ignore', []) + if not binary_package: + files_to_ignore = self.settings.get('files_to_ignore', []) + files_to_include = self.settings.get('files_to_include', []) + else: + files_to_ignore = self.settings.get('files_to_ignore_binary', []) + files_to_include = self.settings.get('files_to_include_binary', []) + + slash = '\\' if os.name == 'nt' else '/' + trailing_package_dir = package_dir + slash if package_dir[-1] != slash else package_dir + package_dir_regex = re.compile('^' + re.escape(trailing_package_dir)) + for root, dirs, files in os.walk(package_dir): + [dirs.remove(dir_) for dir_ in dirs if dir_ in dirs_to_ignore] + paths = dirs + paths.extend(files) + for path in paths: + full_path = os.path.join(root, path) + relative_path = re.sub(package_dir_regex, '', full_path) + + ignore_matches = [fnmatch(relative_path, p) for p in files_to_ignore] + include_matches = [fnmatch(relative_path, p) for p in files_to_include] + if any(ignore_matches) and not any(include_matches): + continue + + if os.path.isdir(full_path): + continue + package_file.write(full_path, relative_path) + + package_file.close() + + return True + + def install_package(self, package_name): + """ + Downloads and installs (or upgrades) a package + + Uses the self.list_available_packages() method to determine where to + retrieve the package file from. + + The install process consists of: + + 1. Finding the package + 2. Downloading the .sublime-package/.zip file + 3. Extracting the package file + 4. Showing install/upgrade messaging + 5. Submitting usage info + 6. Recording that the package is installed + + :param package_name: + The package to download and install + + :return: bool if the package was successfully installed + """ + + packages = self.list_available_packages() + + is_available = package_name in list(packages.keys()) + is_unavailable = package_name in self.settings.get('unavailable_packages', []) + + if is_unavailable and not is_available: + console_write(u'The package "%s" is not available on this platform.' % package_name, True) + return False + + if not is_available: + show_error(u'The package specified, %s, is not available' % package_name) + return False + + url = packages[package_name]['download']['url'] + package_filename = package_name + '.sublime-package' + + tmp_dir = tempfile.mkdtemp() + + try: + # This is refers to the zipfile later on, so we define it here so we can + # close the zip file if set during the finally clause + package_zip = None + + tmp_package_path = os.path.join(tmp_dir, package_filename) + + unpacked_package_dir = self.get_package_dir(package_name) + package_path = os.path.join(sublime.installed_packages_path(), + package_filename) + pristine_package_path = os.path.join(os.path.dirname( + sublime.packages_path()), 'Pristine Packages', package_filename) + + if os.path.exists(os.path.join(unpacked_package_dir, '.git')): + if self.settings.get('ignore_vcs_packages'): + show_error(u'Skipping git package %s since the setting ignore_vcs_packages is set to true' % package_name) + return False + return GitUpgrader(self.settings['git_binary'], + self.settings['git_update_command'], unpacked_package_dir, + self.settings['cache_length'], self.settings['debug']).run() + elif os.path.exists(os.path.join(unpacked_package_dir, '.hg')): + if self.settings.get('ignore_vcs_packages'): + show_error(u'Skipping hg package %s since the setting ignore_vcs_packages is set to true' % package_name) + return False + return HgUpgrader(self.settings['hg_binary'], + self.settings['hg_update_command'], unpacked_package_dir, + self.settings['cache_length'], self.settings['debug']).run() + + old_version = self.get_metadata(package_name).get('version') + is_upgrade = old_version != None + + # Download the sublime-package or zip file + try: + with downloader(url, self.settings) as manager: + package_bytes = manager.fetch(url, 'Error downloading package.') + except (DownloaderException) as e: + console_write(e, True) + show_error(u'Unable to download %s. Please view the console for more details.' % package_name) + return False + + with open_compat(tmp_package_path, "wb") as package_file: + package_file.write(package_bytes) + + # Try to open it as a zip file + try: + package_zip = zipfile.ZipFile(tmp_package_path, 'r') + except (zipfile.BadZipfile): + show_error(u'An error occurred while trying to unzip the package file for %s. Please try installing the package again.' % package_name) + return False + + # Scan through the root level of the zip file to gather some info + root_level_paths = [] + last_path = None + for path in package_zip.namelist(): + try: + if not isinstance(path, str_cls): + path = path.decode('utf-8', 'strict') + except (UnicodeDecodeError): + console_write(u'One or more of the zip file entries in %s is not encoded using UTF-8, aborting' % package_name, True) + return False + + last_path = path + + if path.find('/') in [len(path) - 1, -1]: + root_level_paths.append(path) + # Make sure there are no paths that look like security vulnerabilities + if path[0] == '/' or path.find('../') != -1 or path.find('..\\') != -1: + show_error(u'The package specified, %s, contains files outside of the package dir and cannot be safely installed.' % package_name) + return False + + if last_path and len(root_level_paths) == 0: + root_level_paths.append(last_path[0:last_path.find('/') + 1]) + + # If there is only a single directory at the top leve, the file + # is most likely a zip from BitBucket or GitHub and we need + # to skip the top-level dir when extracting + skip_root_dir = len(root_level_paths) == 1 and \ + root_level_paths[0].endswith('/') + + no_package_file_zip_path = '.no-sublime-package' + if skip_root_dir: + no_package_file_zip_path = root_level_paths[0] + no_package_file_zip_path + + # If we should extract unpacked or as a .sublime-package file + unpack = True + + # By default, ST3 prefers .sublime-package files since this allows + # overriding files in the Packages/{package_name}/ folder + if int(sublime.version()) >= 3000: + unpack = False + + # If the package maintainer doesn't want a .sublime-package + try: + package_zip.getinfo(no_package_file_zip_path) + unpack = True + except (KeyError): + pass + + # If we already have a package-metadata.json file in + # Packages/{package_name}/, the only way to successfully upgrade + # will be to unpack + unpacked_metadata_file = os.path.join(unpacked_package_dir, + 'package-metadata.json') + if os.path.exists(unpacked_metadata_file): + unpack = True + + # If we determined it should be unpacked, we extract directly + # into the Packages/{package_name}/ folder + if unpack: + self.backup_package_dir(package_name) + package_dir = unpacked_package_dir + + # Otherwise we go into a temp dir since we will be creating a + # new .sublime-package file later + else: + tmp_working_dir = os.path.join(tmp_dir, 'working') + os.mkdir(tmp_working_dir) + package_dir = tmp_working_dir + + package_metadata_file = os.path.join(package_dir, + 'package-metadata.json') + + if not os.path.exists(package_dir): + os.mkdir(package_dir) + + os.chdir(package_dir) + + # Here we don't use .extractall() since it was having issues on OS X + overwrite_failed = False + extracted_paths = [] + for path in package_zip.namelist(): + dest = path + + try: + if not isinstance(dest, str_cls): + dest = dest.decode('utf-8', 'strict') + except (UnicodeDecodeError): + console_write(u'One or more of the zip file entries in %s is not encoded using UTF-8, aborting' % package_name, True) + return False + + if os.name == 'nt': + regex = ':|\*|\?|"|<|>|\|' + if re.search(regex, dest) != None: + console_write(u'Skipping file from package named %s due to an invalid filename' % package_name, True) + continue + + # If there was only a single directory in the package, we remove + # that folder name from the paths as we extract entries + if skip_root_dir: + dest = dest[len(root_level_paths[0]):] + + if os.name == 'nt': + dest = dest.replace('/', '\\') + else: + dest = dest.replace('\\', '/') + + dest = os.path.join(package_dir, dest) + + def add_extracted_dirs(dir_): + while dir_ not in extracted_paths: + extracted_paths.append(dir_) + dir_ = os.path.dirname(dir_) + if dir_ == package_dir: + break + + if path.endswith('/'): + if not os.path.exists(dest): + os.makedirs(dest) + add_extracted_dirs(dest) + else: + dest_dir = os.path.dirname(dest) + if not os.path.exists(dest_dir): + os.makedirs(dest_dir) + add_extracted_dirs(dest_dir) + extracted_paths.append(dest) + try: + open_compat(dest, 'wb').write(package_zip.read(path)) + except (IOError) as e: + message = unicode_from_os(e) + if re.search('[Ee]rrno 13', message): + overwrite_failed = True + break + console_write(u'Skipping file from package named %s due to an invalid filename' % package_name, True) + + except (UnicodeDecodeError): + console_write(u'Skipping file from package named %s due to an invalid filename' % package_name, True) + + package_zip.close() + package_zip = None + + # If upgrading failed, queue the package to upgrade upon next start + if overwrite_failed: + reinstall_file = os.path.join(package_dir, 'package-control.reinstall') + open_compat(reinstall_file, 'w').close() + + # Don't delete the metadata file, that way we have it + # when the reinstall happens, and the appropriate + # usage info can be sent back to the server + clear_directory(package_dir, [reinstall_file, package_metadata_file]) + + show_error(u'An error occurred while trying to upgrade %s. Please restart Sublime Text to finish the upgrade.' % package_name) + return False + + # Here we clean out any files that were not just overwritten. It is ok + # if there is an error removing a file. The next time there is an + # upgrade, it should be cleaned out successfully then. + clear_directory(package_dir, extracted_paths) + + self.print_messages(package_name, package_dir, is_upgrade, old_version) + + with open_compat(package_metadata_file, 'w') as f: + metadata = { + "version": packages[package_name]['download']['version'], + "url": packages[package_name]['homepage'], + "description": packages[package_name]['description'] + } + json.dump(metadata, f) + + # Submit install and upgrade info + if is_upgrade: + params = { + 'package': package_name, + 'operation': 'upgrade', + 'version': packages[package_name]['download']['version'], + 'old_version': old_version + } + else: + params = { + 'package': package_name, + 'operation': 'install', + 'version': packages[package_name]['download']['version'] + } + self.record_usage(params) + + # Record the install in the settings file so that you can move + # settings across computers and have the same packages installed + def save_package(): + settings = sublime.load_settings('Package Control.sublime-settings') + installed_packages = settings.get('installed_packages', []) + if not installed_packages: + installed_packages = [] + installed_packages.append(package_name) + installed_packages = list(set(installed_packages)) + installed_packages = sorted(installed_packages, + key=lambda s: s.lower()) + settings.set('installed_packages', installed_packages) + sublime.save_settings('Package Control.sublime-settings') + sublime.set_timeout(save_package, 1) + + # If we didn't extract directly into the Packages/{package_name}/ + # folder, we need to create a .sublime-package file and install it + if not unpack: + try: + # Remove the downloaded file since we are going to overwrite it + os.remove(tmp_package_path) + package_zip = zipfile.ZipFile(tmp_package_path, "w", + compression=zipfile.ZIP_DEFLATED) + except (OSError, IOError) as e: + show_error(u'An error occurred creating the package file %s in %s.\n\n%s' % ( + package_filename, tmp_dir, unicode_from_os(e))) + return False + + package_dir_regex = re.compile('^' + re.escape(package_dir)) + for root, dirs, files in os.walk(package_dir): + paths = dirs + paths.extend(files) + for path in paths: + full_path = os.path.join(root, path) + relative_path = re.sub(package_dir_regex, '', full_path) + if os.path.isdir(full_path): + continue + package_zip.write(full_path, relative_path) + + package_zip.close() + package_zip = None + + if os.path.exists(package_path): + os.remove(package_path) + shutil.move(tmp_package_path, package_path) + + # We have to remove the pristine package too or else Sublime Text 2 + # will silently delete the package + if os.path.exists(pristine_package_path): + os.remove(pristine_package_path) + + os.chdir(sublime.packages_path()) + return True + + finally: + # We need to make sure the zipfile is closed to + # help prevent permissions errors on Windows + if package_zip: + package_zip.close() + + # Try to remove the tmp dir after a second to make sure + # a virus scanner is holding a reference to the zipfile + # after we close it. + def remove_tmp_dir(): + try: + shutil.rmtree(tmp_dir) + except (PermissionError): + # If we can't remove the tmp dir, don't let an uncaught exception + # fall through and break the install process + pass + sublime.set_timeout(remove_tmp_dir, 1000) + + def backup_package_dir(self, package_name): + """ + Does a full backup of the Packages/{package}/ dir to Backup/ + + :param package_name: + The name of the package to back up + + :return: + If the backup succeeded + """ + + package_dir = os.path.join(sublime.packages_path(), package_name) + if not os.path.exists(package_dir): + return True + + try: + backup_dir = os.path.join(os.path.dirname( + sublime.packages_path()), 'Backup', + datetime.datetime.now().strftime('%Y%m%d%H%M%S')) + if not os.path.exists(backup_dir): + os.makedirs(backup_dir) + package_backup_dir = os.path.join(backup_dir, package_name) + if os.path.exists(package_backup_dir): + console_write(u"FOLDER %s ALREADY EXISTS!" % package_backup_dir) + shutil.copytree(package_dir, package_backup_dir) + return True + + except (OSError, IOError) as e: + show_error(u'An error occurred while trying to backup the package directory for %s.\n\n%s' % ( + package_name, unicode_from_os(e))) + if os.path.exists(package_backup_dir): + shutil.rmtree(package_backup_dir) + return False + + def print_messages(self, package, package_dir, is_upgrade, old_version): + """ + Prints out package install and upgrade messages + + The functionality provided by this allows package maintainers to + show messages to the user when a package is installed, or when + certain version upgrade occur. + + :param package: + The name of the package the message is for + + :param package_dir: + The full filesystem path to the package directory + + :param is_upgrade: + If the install was actually an upgrade + + :param old_version: + The string version of the package before the upgrade occurred + """ + + messages_file = os.path.join(package_dir, 'messages.json') + if not os.path.exists(messages_file): + return + + messages_fp = open_compat(messages_file, 'r') + try: + message_info = json.loads(read_compat(messages_fp)) + except (ValueError): + console_write(u'Error parsing messages.json for %s' % package, True) + return + messages_fp.close() + + output = '' + if not is_upgrade and message_info.get('install'): + install_messages = os.path.join(package_dir, + message_info.get('install')) + message = '\n\n%s:\n%s\n\n ' % (package, + ('-' * len(package))) + with open_compat(install_messages, 'r') as f: + message += read_compat(f).replace('\n', '\n ') + output += message + '\n' + + elif is_upgrade and old_version: + upgrade_messages = list(set(message_info.keys()) - + set(['install'])) + upgrade_messages = version_sort(upgrade_messages, reverse=True) + old_version_cmp = version_comparable(old_version) + + for version in upgrade_messages: + if version_comparable(version) <= old_version_cmp: + break + if not output: + message = '\n\n%s:\n%s\n' % (package, + ('-' * len(package))) + output += message + upgrade_message_path = os.path.join(package_dir, + message_info.get(version)) + message = '\n ' + with open_compat(upgrade_message_path, 'r') as f: + message += read_compat(f).replace('\n', '\n ') + output += message + '\n' + + if not output: + return + + def print_to_panel(): + window = sublime.active_window() + + views = window.views() + view = None + for _view in views: + if _view.name() == 'Package Control Messages': + view = _view + break + + if not view: + view = window.new_file() + view.set_name('Package Control Messages') + view.set_scratch(True) + + def write(string): + view.run_command('package_message', {'string': string}) + + if not view.size(): + view.settings().set("word_wrap", True) + write('Package Control Messages\n' + + '========================') + + write(output) + sublime.set_timeout(print_to_panel, 1) + + def remove_package(self, package_name): + """ + Deletes a package + + The deletion process consists of: + + 1. Deleting the directory (or marking it for deletion if deletion fails) + 2. Submitting usage info + 3. Removing the package from the list of installed packages + + :param package_name: + The package to delete + + :return: bool if the package was successfully deleted + """ + + installed_packages = self.list_packages() + + if package_name not in installed_packages: + show_error(u'The package specified, %s, is not installed' % package_name) + return False + + os.chdir(sublime.packages_path()) + + # Give Sublime Text some time to ignore the package + time.sleep(1) + + package_filename = package_name + '.sublime-package' + installed_package_path = os.path.join(sublime.installed_packages_path(), + package_filename) + pristine_package_path = os.path.join(os.path.dirname( + sublime.packages_path()), 'Pristine Packages', package_filename) + package_dir = self.get_package_dir(package_name) + + version = self.get_metadata(package_name).get('version') + + try: + if os.path.exists(installed_package_path): + os.remove(installed_package_path) + except (OSError, IOError) as e: + show_error(u'An error occurred while trying to remove the installed package file for %s.\n\n%s' % ( + package_name, unicode_from_os(e))) + return False + + try: + if os.path.exists(pristine_package_path): + os.remove(pristine_package_path) + except (OSError, IOError) as e: + show_error(u'An error occurred while trying to remove the pristine package file for %s.\n\n%s' % ( + package_name, unicode_from_os(e))) + return False + + # We don't delete the actual package dir immediately due to a bug + # in sublime_plugin.py + can_delete_dir = True + if not clear_directory(package_dir): + # If there is an error deleting now, we will mark it for + # cleanup the next time Sublime Text starts + open_compat(os.path.join(package_dir, 'package-control.cleanup'), + 'w').close() + can_delete_dir = False + + params = { + 'package': package_name, + 'operation': 'remove', + 'version': version + } + self.record_usage(params) + + # Remove the package from the installed packages list + def clear_package(): + settings = sublime.load_settings('Package Control.sublime-settings') + installed_packages = settings.get('installed_packages', []) + if not installed_packages: + installed_packages = [] + installed_packages.remove(package_name) + settings.set('installed_packages', installed_packages) + sublime.save_settings('Package Control.sublime-settings') + sublime.set_timeout(clear_package, 1) + + if can_delete_dir and os.path.exists(package_dir): + os.rmdir(package_dir) + + return True + + def record_usage(self, params): + """ + Submits install, upgrade and delete actions to a usage server + + The usage information is currently displayed on the Package Control + community package list at http://wbond.net/sublime_packages/community + + :param params: + A dict of the information to submit + """ + + if not self.settings.get('submit_usage'): + return + params['package_control_version'] = \ + self.get_metadata('Package Control').get('version') + params['sublime_platform'] = self.settings.get('platform') + params['sublime_version'] = self.settings.get('version') + + # For Python 2, we need to explicitly encoding the params + for param in params: + if isinstance(params[param], str_cls): + params[param] = params[param].encode('utf-8') + + url = self.settings.get('submit_url') + '?' + urlencode(params) + + try: + with downloader(url, self.settings) as manager: + result = manager.fetch(url, 'Error submitting usage information.') + except (DownloaderException) as e: + console_write(e, True) + return + + try: + result = json.loads(result.decode('utf-8')) + if result['result'] != 'success': + raise ValueError() + except (ValueError): + console_write(u'Error submitting usage information for %s' % params['package'], True) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_renamer.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_renamer.py new file mode 100644 index 0000000..73e83fd --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_renamer.py @@ -0,0 +1,117 @@ +import os + +import sublime + +from .console_write import console_write +from .package_io import package_file_exists + + +class PackageRenamer(): + """ + Class to handle renaming packages via the renamed_packages setting + gathered from channels and repositories. + """ + + def load_settings(self): + """ + Loads the list of installed packages from the + Package Control.sublime-settings file. + """ + + self.settings_file = 'Package Control.sublime-settings' + self.settings = sublime.load_settings(self.settings_file) + self.installed_packages = self.settings.get('installed_packages', []) + if not isinstance(self.installed_packages, list): + self.installed_packages = [] + + def rename_packages(self, installer): + """ + Renames any installed packages that the user has installed. + + :param installer: + An instance of :class:`PackageInstaller` + """ + + # Fetch the packages since that will pull in the renamed packages list + installer.manager.list_available_packages() + renamed_packages = installer.manager.settings.get('renamed_packages', {}) + if not renamed_packages: + renamed_packages = {} + + # These are packages that have been tracked as installed + installed_pkgs = self.installed_packages + # There are the packages actually present on the filesystem + present_packages = installer.manager.list_packages() + + # Rename directories for packages that have changed names + for package_name in renamed_packages: + package_dir = os.path.join(sublime.packages_path(), package_name) + if not package_file_exists(package_name, 'package-metadata.json'): + continue + + new_package_name = renamed_packages[package_name] + new_package_dir = os.path.join(sublime.packages_path(), + new_package_name) + + changing_case = package_name.lower() == new_package_name.lower() + case_insensitive_fs = sublime.platform() in ['windows', 'osx'] + + # Since Windows and OSX use case-insensitive filesystems, we have to + # scan through the list of installed packages if the rename of the + # package is just changing the case of it. If we don't find the old + # name for it, we continue the loop since os.path.exists() will return + # true due to the case-insensitive nature of the filesystems. + if case_insensitive_fs and changing_case: + has_old = False + for present_package_name in present_packages: + if present_package_name == package_name: + has_old = True + break + if not has_old: + continue + + if not os.path.exists(new_package_dir) or (case_insensitive_fs and changing_case): + + # Windows will not allow you to rename to the same name with + # a different case, so we work around that with a temporary name + if os.name == 'nt' and changing_case: + temp_package_name = '__' + new_package_name + temp_package_dir = os.path.join(sublime.packages_path(), + temp_package_name) + os.rename(package_dir, temp_package_dir) + package_dir = temp_package_dir + + os.rename(package_dir, new_package_dir) + installed_pkgs.append(new_package_name) + + console_write(u'Renamed %s to %s' % (package_name, new_package_name), True) + + else: + installer.manager.remove_package(package_name) + message_string = u'Removed %s since package with new name (%s) already exists' % ( + package_name, new_package_name) + console_write(message_string, True) + + try: + installed_pkgs.remove(package_name) + except (ValueError): + pass + + sublime.set_timeout(lambda: self.save_packages(installed_pkgs), 10) + + def save_packages(self, installed_packages): + """ + Saves the list of installed packages (after having been appropriately + renamed) + + :param installed_packages: + The new list of installed packages + """ + + installed_packages = list(set(installed_packages)) + installed_packages = sorted(installed_packages, + key=lambda s: s.lower()) + + if installed_packages != self.installed_packages: + self.settings.set('installed_packages', installed_packages) + sublime.save_settings(self.settings_file) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/preferences_filename.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/preferences_filename.py new file mode 100644 index 0000000..7091dd9 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/preferences_filename.py @@ -0,0 +1,11 @@ +import sublime + + +def preferences_filename(): + """ + :return: The appropriate settings filename based on the version of Sublime Text + """ + + if int(sublime.version()) >= 2174: + return 'Preferences.sublime-settings' + return 'Global.sublime-settings' diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/__init__.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/__init__.py new file mode 100644 index 0000000..cfea3bd --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/__init__.py @@ -0,0 +1,12 @@ +from .bitbucket_repository_provider import BitBucketRepositoryProvider +from .github_repository_provider import GitHubRepositoryProvider +from .github_user_provider import GitHubUserProvider +from .repository_provider import RepositoryProvider + +from .channel_provider import ChannelProvider + + +REPOSITORY_PROVIDERS = [BitBucketRepositoryProvider, GitHubRepositoryProvider, + GitHubUserProvider, RepositoryProvider] + +CHANNEL_PROVIDERS = [ChannelProvider] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/bitbucket_repository_provider.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/bitbucket_repository_provider.py new file mode 100644 index 0000000..b5d603f --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/bitbucket_repository_provider.py @@ -0,0 +1,163 @@ +import re + +from ..clients.bitbucket_client import BitBucketClient +from ..downloaders.downloader_exception import DownloaderException +from ..clients.client_exception import ClientException +from .provider_exception import ProviderException + + +class BitBucketRepositoryProvider(): + """ + Allows using a public BitBucket repository as the source for a single package. + For legacy purposes, this can also be treated as the source for a Package + Control "repository". + + :param repo: + The public web URL to the BitBucket repository. Should be in the format + `https://bitbucket.org/user/package`. + + :param settings: + A dict containing at least the following fields: + `cache_length`, + `debug`, + `timeout`, + `user_agent` + Optional fields: + `http_proxy`, + `https_proxy`, + `proxy_username`, + `proxy_password`, + `query_string_params` + `install_prereleases` + """ + + def __init__(self, repo, settings): + self.cache = {} + self.repo = repo + self.settings = settings + self.failed_sources = {} + + @classmethod + def match_url(cls, repo): + """Indicates if this provider can handle the provided repo""" + + return re.search('^https?://bitbucket.org/([^/]+/[^/]+)/?$', repo) != None + + def prefetch(self): + """ + Go out and perform HTTP operations, caching the result + + :raises: + DownloaderException: when there is an issue download package info + ClientException: when there is an issue parsing package info + """ + + [name for name, info in self.get_packages()] + + def get_failed_sources(self): + """ + List of any URLs that could not be accessed while accessing this repository + + :return: + A generator of ("https://bitbucket.org/user/repo", Exception()) tuples + """ + + return self.failed_sources.items() + + def get_broken_packages(self): + """ + For API-compatibility with RepositoryProvider + """ + + return {}.items() + + def get_packages(self, invalid_sources=None): + """ + Uses the BitBucket API to construct necessary info for a package + + :param invalid_sources: + A list of URLs that should be ignored + + :raises: + DownloaderException: when there is an issue download package info + ClientException: when there is an issue parsing package info + + :return: + A generator of + ( + 'Package Name', + { + 'name': name, + 'description': description, + 'author': author, + 'homepage': homepage, + 'last_modified': last modified date, + 'download': { + 'url': url, + 'date': date, + 'version': version + }, + 'previous_names': [], + 'labels': [], + 'sources': [the repo URL], + 'readme': url, + 'issues': url, + 'donate': url, + 'buy': None + } + ) + tuples + """ + + if 'get_packages' in self.cache: + for key, value in self.cache['get_packages'].items(): + yield (key, value) + return + + client = BitBucketClient(self.settings) + + if invalid_sources != None and self.repo in invalid_sources: + raise StopIteration() + + try: + repo_info = client.repo_info(self.repo) + download = client.download_info(self.repo) + + name = repo_info['name'] + details = { + 'name': name, + 'description': repo_info['description'], + 'homepage': repo_info['homepage'], + 'author': repo_info['author'], + 'last_modified': download.get('date'), + 'download': download, + 'previous_names': [], + 'labels': [], + 'sources': [self.repo], + 'readme': repo_info['readme'], + 'issues': repo_info['issues'], + 'donate': repo_info['donate'], + 'buy': None + } + self.cache['get_packages'] = {name: details} + yield (name, details) + + except (DownloaderException, ClientException, ProviderException) as e: + self.failed_sources[self.repo] = e + self.cache['get_packages'] = {} + raise StopIteration() + + def get_renamed_packages(self): + """For API-compatibility with RepositoryProvider""" + + return {} + + def get_unavailable_packages(self): + """ + Method for compatibility with RepositoryProvider class. These providers + are based on API calls, and thus do not support different platform + downloads, making it impossible for there to be unavailable packages. + + :return: An empty list + """ + return [] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/channel_provider.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/channel_provider.py new file mode 100644 index 0000000..5543bdc --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/channel_provider.py @@ -0,0 +1,312 @@ +import json +import os +import re + +try: + # Python 3 + from urllib.parse import urlparse +except (ImportError): + # Python 2 + from urlparse import urlparse + +from ..console_write import console_write +from .release_selector import ReleaseSelector +from .provider_exception import ProviderException +from ..downloaders.downloader_exception import DownloaderException +from ..clients.client_exception import ClientException +from ..download_manager import downloader + + +class ChannelProvider(ReleaseSelector): + """ + Retrieves a channel and provides an API into the information + + The current channel/repository infrastructure caches repository info into + the channel to improve the Package Control client performance. This also + has the side effect of lessening the load on the GitHub and BitBucket APIs + and getting around not-infrequent HTTP 503 errors from those APIs. + + :param channel: + The URL of the channel + + :param settings: + A dict containing at least the following fields: + `cache_length`, + `debug`, + `timeout`, + `user_agent` + Optional fields: + `http_proxy`, + `https_proxy`, + `proxy_username`, + `proxy_password`, + `query_string_params` + `install_prereleases` + """ + + def __init__(self, channel, settings): + self.channel_info = None + self.schema_version = 0.0 + self.channel = channel + self.settings = settings + self.unavailable_packages = [] + + @classmethod + def match_url(cls, channel): + """Indicates if this provider can handle the provided channel""" + + return True + + def prefetch(self): + """ + Go out and perform HTTP operations, caching the result + + :raises: + ProviderException: when an error occurs trying to open a file + DownloaderException: when an error occurs trying to open a URL + """ + + self.fetch() + + def fetch(self): + """ + Retrieves and loads the JSON for other methods to use + + :raises: + ProviderException: when an error occurs with the channel contents + DownloaderException: when an error occurs trying to open a URL + """ + + if self.channel_info != None: + return + + if re.match('https?://', self.channel, re.I): + with downloader(self.channel, self.settings) as manager: + channel_json = manager.fetch(self.channel, + 'Error downloading channel.') + + # All other channels are expected to be filesystem paths + else: + if not os.path.exists(self.channel): + raise ProviderException(u'Error, file %s does not exist' % self.channel) + + if self.settings.get('debug'): + console_write(u'Loading %s as a channel' % self.channel, True) + + # We open as binary so we get bytes like the DownloadManager + with open(self.channel, 'rb') as f: + channel_json = f.read() + + try: + channel_info = json.loads(channel_json.decode('utf-8')) + except (ValueError): + raise ProviderException(u'Error parsing JSON from channel %s.' % self.channel) + + schema_error = u'Channel %s does not appear to be a valid channel file because ' % self.channel + + if 'schema_version' not in channel_info: + raise ProviderException(u'%s the "schema_version" JSON key is missing.' % schema_error) + + try: + self.schema_version = float(channel_info.get('schema_version')) + except (ValueError): + raise ProviderException(u'%s the "schema_version" is not a valid number.' % schema_error) + + if self.schema_version not in [1.0, 1.1, 1.2, 2.0]: + raise ProviderException(u'%s the "schema_version" is not recognized. Must be one of: 1.0, 1.1, 1.2 or 2.0.' % schema_error) + + self.channel_info = channel_info + + def get_name_map(self): + """ + :raises: + ProviderException: when an error occurs with the channel contents + DownloaderException: when an error occurs trying to open a URL + + :return: + A dict of the mapping for URL slug -> package name + """ + + self.fetch() + + if self.schema_version >= 2.0: + return {} + + return self.channel_info.get('package_name_map', {}) + + def get_renamed_packages(self): + """ + :raises: + ProviderException: when an error occurs with the channel contents + DownloaderException: when an error occurs trying to open a URL + + :return: + A dict of the packages that have been renamed + """ + + self.fetch() + + if self.schema_version >= 2.0: + return {} + + return self.channel_info.get('renamed_packages', {}) + + def get_repositories(self): + """ + :raises: + ProviderException: when an error occurs with the channel contents + DownloaderException: when an error occurs trying to open a URL + + :return: + A list of the repository URLs + """ + + self.fetch() + + if 'repositories' not in self.channel_info: + raise ProviderException(u'Channel %s does not appear to be a valid channel file because the "repositories" JSON key is missing.' % self.channel) + + # Determine a relative root so repositories can be defined + # relative to the location of the channel file. + if re.match('https?://', self.channel, re.I): + url_pieces = urlparse(self.channel) + domain = url_pieces.scheme + '://' + url_pieces.netloc + path = '/' if url_pieces.path == '' else url_pieces.path + if path[-1] != '/': + path = os.path.dirname(path) + relative_base = domain + path + else: + relative_base = os.path.dirname(self.channel) + '/' + + output = [] + repositories = self.channel_info.get('repositories', []) + for repository in repositories: + if re.match('^\./|\.\./', repository): + repository = os.path.normpath(relative_base + repository) + output.append(repository) + + return output + + def get_certs(self): + """ + Provides a secure way for distribution of SSL CA certificates + + Unfortunately Python does not include a bundle of CA certs with urllib + to perform SSL certificate validation. To circumvent this issue, + Package Control acts as a distributor of the CA certs for all HTTPS + URLs of package downloads. + + The default channel scrapes and caches info about all packages + periodically, and in the process it checks the CA certs for all of + the HTTPS URLs listed in the repositories. The contents of the CA cert + files are then hashed, and the CA cert is stored in a filename with + that hash. This is a fingerprint to ensure that Package Control has + the appropriate CA cert for a domain name. + + Next, the default channel file serves up a JSON object of the domain + names and the hashes of their current CA cert files. If Package Control + does not have the appropriate hash for a domain, it may retrieve it + from the channel server. To ensure that Package Control is talking to + a trusted authority to get the CA certs from, the CA cert for + sublime.wbond.net is bundled with Package Control. Then when downloading + the channel file, Package Control can ensure that the channel file's + SSL certificate is valid, thus ensuring the resulting CA certs are + legitimate. + + As a matter of optimization, the distribution of Package Control also + includes the current CA certs for all known HTTPS domains that are + included in the channel, as of the time when Package Control was + last released. + + :raises: + ProviderException: when an error occurs with the channel contents + DownloaderException: when an error occurs trying to open a URL + + :return: + A dict of {'Domain Name': ['cert_file_hash', 'cert_file_download_url']} + """ + + self.fetch() + + return self.channel_info.get('certs', {}) + + def get_packages(self, repo): + """ + Provides access to the repository info that is cached in a channel + + :param repo: + The URL of the repository to get the cached info of + + :raises: + ProviderException: when an error occurs with the channel contents + DownloaderException: when an error occurs trying to open a URL + + :return: + A dict in the format: + { + 'Package Name': { + 'name': name, + 'description': description, + 'author': author, + 'homepage': homepage, + 'last_modified': last modified date, + 'download': { + 'url': url, + 'date': date, + 'version': version + }, + 'previous_names': [old_name, ...], + 'labels': [label, ...], + 'readme': url, + 'issues': url, + 'donate': url, + 'buy': url + }, + ... + } + """ + + self.fetch() + + # The 2.0 channel schema renamed the key cached package info was + # stored under in order to be more clear to new users. + packages_key = 'packages_cache' if self.schema_version >= 2.0 else 'packages' + + if self.channel_info.get(packages_key, False) == False: + return {} + + if self.channel_info[packages_key].get(repo, False) == False: + return {} + + output = {} + for package in self.channel_info[packages_key][repo]: + copy = package.copy() + + # In schema version 2.0, we store a list of dicts containing info + # about all available releases. These include "version" and + # "platforms" keys that are used to pick the download for the + # current machine. + if self.schema_version >= 2.0: + copy = self.select_release(copy) + else: + copy = self.select_platform(copy) + + if not copy: + self.unavailable_packages.append(package['name']) + continue + + output[copy['name']] = copy + + return output + + def get_unavailable_packages(self): + """ + Provides a list of packages that are unavailable for the current + platform/architecture that Sublime Text is running on. + + This list will be empty unless get_packages() is called first. + + :return: A list of package names + """ + + return self.unavailable_packages diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/github_repository_provider.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/github_repository_provider.py new file mode 100644 index 0000000..158c850 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/github_repository_provider.py @@ -0,0 +1,169 @@ +import re + +from ..clients.github_client import GitHubClient +from ..downloaders.downloader_exception import DownloaderException +from ..clients.client_exception import ClientException +from .provider_exception import ProviderException + + +class GitHubRepositoryProvider(): + """ + Allows using a public GitHub repository as the source for a single package. + For legacy purposes, this can also be treated as the source for a Package + Control "repository". + + :param repo: + The public web URL to the GitHub repository. Should be in the format + `https://github.com/user/package` for the master branch, or + `https://github.com/user/package/tree/{branch_name}` for any other + branch. + + :param settings: + A dict containing at least the following fields: + `cache_length`, + `debug`, + `timeout`, + `user_agent` + Optional fields: + `http_proxy`, + `https_proxy`, + `proxy_username`, + `proxy_password`, + `query_string_params` + `install_prereleases` + """ + + def __init__(self, repo, settings): + self.cache = {} + # Clean off the trailing .git to be more forgiving + self.repo = re.sub('\.git$', '', repo) + self.settings = settings + self.failed_sources = {} + + @classmethod + def match_url(cls, repo): + """Indicates if this provider can handle the provided repo""" + + master = re.search('^https?://github.com/[^/]+/[^/]+/?$', repo) + branch = re.search('^https?://github.com/[^/]+/[^/]+/tree/[^/]+/?$', + repo) + return master != None or branch != None + + def prefetch(self): + """ + Go out and perform HTTP operations, caching the result + + :raises: + DownloaderException: when there is an issue download package info + ClientException: when there is an issue parsing package info + """ + + [name for name, info in self.get_packages()] + + def get_failed_sources(self): + """ + List of any URLs that could not be accessed while accessing this repository + + :return: + A generator of ("https://github.com/user/repo", Exception()) tuples + """ + + return self.failed_sources.items() + + def get_broken_packages(self): + """ + For API-compatibility with RepositoryProvider + """ + + return {}.items() + + def get_packages(self, invalid_sources=None): + """ + Uses the GitHub API to construct necessary info for a package + + :param invalid_sources: + A list of URLs that should be ignored + + :raises: + DownloaderException: when there is an issue download package info + ClientException: when there is an issue parsing package info + + :return: + A generator of + ( + 'Package Name', + { + 'name': name, + 'description': description, + 'author': author, + 'homepage': homepage, + 'last_modified': last modified date, + 'download': { + 'url': url, + 'date': date, + 'version': version + }, + 'previous_names': [], + 'labels': [], + 'sources': [the repo URL], + 'readme': url, + 'issues': url, + 'donate': url, + 'buy': None + } + ) + tuples + """ + + if 'get_packages' in self.cache: + for key, value in self.cache['get_packages'].items(): + yield (key, value) + return + + client = GitHubClient(self.settings) + + if invalid_sources != None and self.repo in invalid_sources: + raise StopIteration() + + try: + repo_info = client.repo_info(self.repo) + download = client.download_info(self.repo) + + name = repo_info['name'] + details = { + 'name': name, + 'description': repo_info['description'], + 'homepage': repo_info['homepage'], + 'author': repo_info['author'], + 'last_modified': download.get('date'), + 'download': download, + 'previous_names': [], + 'labels': [], + 'sources': [self.repo], + 'readme': repo_info['readme'], + 'issues': repo_info['issues'], + 'donate': repo_info['donate'], + 'buy': None + } + self.cache['get_packages'] = {name: details} + yield (name, details) + + except (DownloaderException, ClientException, ProviderException) as e: + self.failed_sources[self.repo] = e + self.cache['get_packages'] = {} + raise StopIteration() + + def get_renamed_packages(self): + """For API-compatibility with RepositoryProvider""" + + return {} + + def get_unavailable_packages(self): + """ + Method for compatibility with RepositoryProvider class. These providers + are based on API calls, and thus do not support different platform + downloads, making it impossible for there to be unavailable packages. + + :return: An empty list + """ + return [] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/github_user_provider.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/github_user_provider.py new file mode 100644 index 0000000..6af60be --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/github_user_provider.py @@ -0,0 +1,172 @@ +import re + +from ..clients.github_client import GitHubClient +from ..downloaders.downloader_exception import DownloaderException +from ..clients.client_exception import ClientException +from .provider_exception import ProviderException + + +class GitHubUserProvider(): + """ + Allows using a GitHub user/organization as the source for multiple packages, + or in Package Control terminology, a "repository". + + :param repo: + The public web URL to the GitHub user/org. Should be in the format + `https://github.com/user`. + + :param settings: + A dict containing at least the following fields: + `cache_length`, + `debug`, + `timeout`, + `user_agent`, + Optional fields: + `http_proxy`, + `https_proxy`, + `proxy_username`, + `proxy_password`, + `query_string_params` + `install_prereleases` + """ + + def __init__(self, repo, settings): + self.cache = {} + self.repo = repo + self.settings = settings + self.failed_sources = {} + + @classmethod + def match_url(cls, repo): + """Indicates if this provider can handle the provided repo""" + + return re.search('^https?://github.com/[^/]+/?$', repo) != None + + def prefetch(self): + """ + Go out and perform HTTP operations, caching the result + """ + + [name for name, info in self.get_packages()] + + def get_failed_sources(self): + """ + List of any URLs that could not be accessed while accessing this repository + + :raises: + DownloaderException: when there is an issue download package info + ClientException: when there is an issue parsing package info + + :return: + A generator of ("https://github.com/user/repo", Exception()) tuples + """ + + return self.failed_sources.items() + + def get_broken_packages(self): + """ + For API-compatibility with RepositoryProvider + """ + + return {}.items() + + def get_packages(self, invalid_sources=None): + """ + Uses the GitHub API to construct necessary info for all packages + + :param invalid_sources: + A list of URLs that should be ignored + + :raises: + DownloaderException: when there is an issue download package info + ClientException: when there is an issue parsing package info + + :return: + A generator of + ( + 'Package Name', + { + 'name': name, + 'description': description, + 'author': author, + 'homepage': homepage, + 'last_modified': last modified date, + 'download': { + 'url': url, + 'date': date, + 'version': version + }, + 'previous_names': [], + 'labels': [], + 'sources': [the user URL], + 'readme': url, + 'issues': url, + 'donate': url, + 'buy': None + } + ) + tuples + """ + + if 'get_packages' in self.cache: + for key, value in self.cache['get_packages'].items(): + yield (key, value) + return + + client = GitHubClient(self.settings) + + if invalid_sources != None and self.repo in invalid_sources: + raise StopIteration() + + try: + user_repos = client.user_info(self.repo) + except (DownloaderException, ClientException, ProviderException) as e: + self.failed_sources = [self.repo] + self.cache['get_packages'] = e + raise e + + output = {} + for repo_info in user_repos: + try: + name = repo_info['name'] + repo_url = 'https://github.com/' + repo_info['user_repo'] + + download = client.download_info(repo_url) + + details = { + 'name': name, + 'description': repo_info['description'], + 'homepage': repo_info['homepage'], + 'author': repo_info['author'], + 'last_modified': download.get('date'), + 'download': download, + 'previous_names': [], + 'labels': [], + 'sources': [self.repo], + 'readme': repo_info['readme'], + 'issues': repo_info['issues'], + 'donate': repo_info['donate'], + 'buy': None + } + output[name] = details + yield (name, details) + + except (DownloaderException, ClientException, ProviderException) as e: + self.failed_sources[repo_url] = e + + self.cache['get_packages'] = output + + def get_renamed_packages(self): + """For API-compatibility with RepositoryProvider""" + + return {} + + def get_unavailable_packages(self): + """ + Method for compatibility with RepositoryProvider class. These providers + are based on API calls, and thus do not support different platform + downloads, making it impossible for there to be unavailable packages. + + :return: An empty list + """ + return [] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/provider_exception.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/provider_exception.py new file mode 100644 index 0000000..e98295f --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/provider_exception.py @@ -0,0 +1,5 @@ +class ProviderException(Exception): + """If a provider could not return information""" + + def __str__(self): + return self.args[0] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/release_selector.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/release_selector.py new file mode 100644 index 0000000..5305468 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/release_selector.py @@ -0,0 +1,125 @@ +import re +import sublime + +from ..versions import version_sort, version_exclude_prerelease + + +class ReleaseSelector(): + """ + A base class for finding the best version of a package for the current machine + """ + + def select_release(self, package_info): + """ + Returns a modified package info dict for package from package schema version 2.0 + + :param package_info: + A package info dict with a "releases" key + + :return: + The package info dict with the "releases" key deleted, and a + "download" key added that contains a dict with "version", "url" and + "date" keys. + None if no compatible relases are available. + """ + + releases = version_sort(package_info['releases']) + if not self.settings.get('install_prereleases'): + releases = version_exclude_prerelease(releases) + + for release in releases: + platforms = release.get('platforms', '*') + if not isinstance(platforms, list): + platforms = [platforms] + + best_platform = self.get_best_platform(platforms) + if not best_platform: + continue + + if not self.is_compatible_version(release.get('sublime_text', '<3000')): + continue + + package_info['download'] = release + package_info['last_modified'] = release.get('date') + del package_info['releases'] + + return package_info + + return None + + def select_platform(self, package_info): + """ + Returns a modified package info dict for package from package schema version <= 1.2 + + :param package_info: + A package info dict with a "platforms" key + + :return: + The package info dict with the "platforms" key deleted, and a + "download" key added that contains a dict with "version" and "url" + keys. + None if no compatible platforms. + """ + platforms = list(package_info['platforms'].keys()) + best_platform = self.get_best_platform(platforms) + if not best_platform: + return None + + package_info['download'] = package_info['platforms'][best_platform][0] + package_info['download']['date'] = package_info.get('last_modified') + del package_info['platforms'] + + return package_info + + def get_best_platform(self, platforms): + """ + Returns the most specific platform that matches the current machine + + :param platforms: + An array of platform names for a package. E.g. ['*', 'windows', 'linux-x64'] + + :return: A string reprenting the most specific matching platform + """ + + ids = [sublime.platform() + '-' + sublime.arch(), sublime.platform(), + '*'] + + for id in ids: + if id in platforms: + return id + + return None + + def is_compatible_version(self, version_range): + min_version = float("-inf") + max_version = float("inf") + + if version_range == '*': + return True + + gt_match = re.match('>(\d+)$', version_range) + ge_match = re.match('>=(\d+)$', version_range) + lt_match = re.match('<(\d+)$', version_range) + le_match = re.match('<=(\d+)$', version_range) + range_match = re.match('(\d+) - (\d+)$', version_range) + + if gt_match: + min_version = int(gt_match.group(1)) + 1 + elif ge_match: + min_version = int(ge_match.group(1)) + elif lt_match: + max_version = int(lt_match.group(1)) - 1 + elif le_match: + max_version = int(le_match.group(1)) + elif range_match: + min_version = int(range_match.group(1)) + max_version = int(range_match.group(2)) + else: + return None + + if min_version > int(sublime.version()): + return False + if max_version < int(sublime.version()): + return False + + return True diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/repository_provider.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/repository_provider.py new file mode 100644 index 0000000..b94a9dd --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/repository_provider.py @@ -0,0 +1,454 @@ +import json +import re +import os +from itertools import chain + +try: + # Python 3 + from urllib.parse import urlparse +except (ImportError): + # Python 2 + from urlparse import urlparse + +from ..console_write import console_write +from .release_selector import ReleaseSelector +from .provider_exception import ProviderException +from ..downloaders.downloader_exception import DownloaderException +from ..clients.client_exception import ClientException +from ..clients.github_client import GitHubClient +from ..clients.bitbucket_client import BitBucketClient +from ..download_manager import downloader + + +class RepositoryProvider(ReleaseSelector): + """ + Generic repository downloader that fetches package info + + With the current channel/repository architecture where the channel file + caches info from all includes repositories, these package providers just + serve the purpose of downloading packages not in the default channel. + + The structure of the JSON a repository should contain is located in + example-packages.json. + + :param repo: + The URL of the package repository + + :param settings: + A dict containing at least the following fields: + `cache_length`, + `debug`, + `timeout`, + `user_agent` + Optional fields: + `http_proxy`, + `https_proxy`, + `proxy_username`, + `proxy_password`, + `query_string_params` + `install_prereleases` + """ + + def __init__(self, repo, settings): + self.cache = {} + self.repo_info = None + self.schema_version = 0.0 + self.repo = repo + self.settings = settings + self.unavailable_packages = [] + self.failed_sources = {} + self.broken_packages = {} + + @classmethod + def match_url(cls, repo): + """Indicates if this provider can handle the provided repo""" + + return True + + def prefetch(self): + """ + Go out and perform HTTP operations, caching the result + + :raises: + DownloaderException: when there is an issue download package info + ClientException: when there is an issue parsing package info + """ + + [name for name, info in self.get_packages()] + + def get_failed_sources(self): + """ + List of any URLs that could not be accessed while accessing this repository + + :return: + A generator of ("https://example.com", Exception()) tuples + """ + + return self.failed_sources.items() + + def get_broken_packages(self): + """ + List of package names for packages that are missing information + + :return: + A generator of ("Package Name", Exception()) tuples + """ + + return self.broken_packages.items() + + def fetch(self): + """ + Retrieves and loads the JSON for other methods to use + + :raises: + ProviderException: when an error occurs trying to open a file + DownloaderException: when an error occurs trying to open a URL + """ + + if self.repo_info != None: + return + + self.repo_info = self.fetch_location(self.repo) + + if 'includes' not in self.repo_info: + return + + # Allow repositories to include other repositories + if re.match('https?://', self.repo, re.I): + url_pieces = urlparse(self.repo) + domain = url_pieces.scheme + '://' + url_pieces.netloc + path = '/' if url_pieces.path == '' else url_pieces.path + if path[-1] != '/': + path = os.path.dirname(path) + relative_base = domain + path + else: + relative_base = os.path.dirname(self.repo) + '/' + + includes = self.repo_info.get('includes', []) + del self.repo_info['includes'] + for include in includes: + if re.match('^\./|\.\./', include): + include = os.path.normpath(relative_base + include) + include_info = self.fetch_location(include) + included_packages = include_info.get('packages', []) + self.repo_info['packages'].extend(included_packages) + + def fetch_location(self, location): + """ + Fetches the contents of a URL of file path + + :param location: + The URL or file path + + :raises: + ProviderException: when an error occurs trying to open a file + DownloaderException: when an error occurs trying to open a URL + + :return: + A dict of the parsed JSON + """ + + if re.match('https?://', self.repo, re.I): + with downloader(location, self.settings) as manager: + json_string = manager.fetch(location, 'Error downloading repository.') + + # Anything that is not a URL is expected to be a filesystem path + else: + if not os.path.exists(location): + raise ProviderException(u'Error, file %s does not exist' % location) + + if self.settings.get('debug'): + console_write(u'Loading %s as a repository' % location, True) + + # We open as binary so we get bytes like the DownloadManager + with open(location, 'rb') as f: + json_string = f.read() + + try: + return json.loads(json_string.decode('utf-8')) + except (ValueError): + raise ProviderException(u'Error parsing JSON from repository %s.' % location) + + def get_packages(self, invalid_sources=None): + """ + Provides access to the packages in this repository + + :param invalid_sources: + A list of URLs that are permissible to fetch data from + + :raises: + ProviderException: when an error occurs trying to open a file + DownloaderException: when there is an issue download package info + ClientException: when there is an issue parsing package info + + :return: + A generator of + ( + 'Package Name', + { + 'name': name, + 'description': description, + 'author': author, + 'homepage': homepage, + 'last_modified': last modified date, + 'download': { + 'url': url, + 'date': date, + 'version': version + }, + 'previous_names': [old_name, ...], + 'labels': [label, ...], + 'sources': [url, ...], + 'readme': url, + 'issues': url, + 'donate': url, + 'buy': url + } + ) + tuples + """ + + if 'get_packages' in self.cache: + for key, value in self.cache['get_packages'].items(): + yield (key, value) + return + + if invalid_sources != None and self.repo in invalid_sources: + raise StopIteration() + + try: + self.fetch() + except (DownloaderException, ProviderException) as e: + self.failed_sources[self.repo] = e + self.cache['get_packages'] = {} + return + + def fail(message): + exception = ProviderException(message) + self.failed_sources[self.repo] = exception + self.cache['get_packages'] = {} + return + schema_error = u'Repository %s does not appear to be a valid repository file because ' % self.repo + + if 'schema_version' not in self.repo_info: + error_string = u'%s the "schema_version" JSON key is missing.' % schema_error + fail(error_string) + return + + try: + self.schema_version = float(self.repo_info.get('schema_version')) + except (ValueError): + error_string = u'%s the "schema_version" is not a valid number.' % schema_error + fail(error_string) + return + + if self.schema_version not in [1.0, 1.1, 1.2, 2.0]: + error_string = u'%s the "schema_version" is not recognized. Must be one of: 1.0, 1.1, 1.2 or 2.0.' % schema_error + fail(error_string) + return + + if 'packages' not in self.repo_info: + error_string = u'%s the "packages" JSON key is missing.' % schema_error + fail(error_string) + return + + github_client = GitHubClient(self.settings) + bitbucket_client = BitBucketClient(self.settings) + + # Backfill the "previous_names" keys for old schemas + previous_names = {} + if self.schema_version < 2.0: + renamed = self.get_renamed_packages() + for old_name in renamed: + new_name = renamed[old_name] + if new_name not in previous_names: + previous_names[new_name] = [] + previous_names[new_name].append(old_name) + + output = {} + for package in self.repo_info['packages']: + info = { + 'sources': [self.repo] + } + + for field in ['name', 'description', 'author', 'last_modified', 'previous_names', + 'labels', 'homepage', 'readme', 'issues', 'donate', 'buy']: + if package.get(field): + info[field] = package.get(field) + + # Schema version 2.0 allows for grabbing details about a pacakge, or its + # download from "details" urls. See the GitHubClient and BitBucketClient + # classes for valid URLs. + if self.schema_version >= 2.0: + details = package.get('details') + releases = package.get('releases') + + # Try to grab package-level details from GitHub or BitBucket + if details: + if invalid_sources != None and details in invalid_sources: + continue + + info['sources'].append(details) + + try: + github_repo_info = github_client.repo_info(details) + bitbucket_repo_info = bitbucket_client.repo_info(details) + + # When grabbing details, prefer explicit field values over the values + # from the GitHub or BitBucket API + if github_repo_info: + info = dict(chain(github_repo_info.items(), info.items())) + elif bitbucket_repo_info: + info = dict(chain(bitbucket_repo_info.items(), info.items())) + else: + raise ProviderException(u'Invalid "details" value "%s" for one of the packages in the repository %s.' % (details, self.repo)) + + except (DownloaderException, ClientException, ProviderException) as e: + if 'name' in info: + self.broken_packages[info['name']] = e + self.failed_sources[details] = e + continue + + # If no releases info was specified, also grab the download info from GH or BB + if not releases and details: + releases = [{'details': details}] + + if not releases: + e = ProviderException(u'No "releases" value for one of the packages in the repository %s.' % self.repo) + if 'name' in info: + self.broken_packages[info['name']] = e + else: + self.failed_sources[self.repo] = e + continue + + # This allows developers to specify a GH or BB location to get releases from, + # especially tags URLs (https://github.com/user/repo/tags or + # https://bitbucket.org/user/repo#tags) + info['releases'] = [] + for release in releases: + download_details = None + download_info = {} + + # Make sure that explicit fields are copied over + for field in ['platforms', 'sublime_text', 'version', 'url', 'date']: + if field in release: + download_info[field] = release[field] + + if 'details' in release: + download_details = release['details'] + + try: + github_download = github_client.download_info(download_details) + bitbucket_download = bitbucket_client.download_info(download_details) + + # Overlay the explicit field values over values fetched from the APIs + if github_download: + download_info = dict(chain(github_download.items(), download_info.items())) + # No matching tags + elif github_download == False: + download_info = {} + elif bitbucket_download: + download_info = dict(chain(bitbucket_download.items(), download_info.items())) + # No matching tags + elif bitbucket_download == False: + download_info = {} + else: + raise ProviderException(u'Invalid "details" value "%s" under the "releases" key for the package "%s" in the repository %s.' % (download_details, info['name'], self.repo)) + + except (DownloaderException, ClientException, ProviderException) as e: + if 'name' in info: + self.broken_packages[info['name']] = e + self.failed_sources[download_details] = e + continue + + if download_info: + info['releases'].append(download_info) + + info = self.select_release(info) + + # Schema version 1.0, 1.1 and 1.2 just require that all values be + # explicitly specified in the package JSON + else: + info['platforms'] = package.get('platforms') + info = self.select_platform(info) + + if not info: + self.unavailable_packages.append(package['name']) + continue + + if 'download' not in info and 'releases' not in info: + self.broken_packages[info['name']] = ProviderException(u'No "releases" key for the package "%s" in the repository %s.' % (info['name'], self.repo)) + continue + + for field in ['previous_names', 'labels']: + if field not in info: + info[field] = [] + + for field in ['readme', 'issues', 'donate', 'buy']: + if field not in info: + info[field] = None + + if 'homepage' not in info: + info['homepage'] = self.repo + + if 'download' in info: + # Rewrites the legacy "zipball" URLs to the new "zip" format + info['download']['url'] = re.sub( + '^(https://nodeload.github.com/[^/]+/[^/]+/)zipball(/.*)$', + '\\1zip\\2', info['download']['url']) + + # Rewrites the legacy "nodeload" URLs to the new "codeload" subdomain + info['download']['url'] = info['download']['url'].replace( + 'nodeload.github.com', 'codeload.github.com') + + # Extract the date from the download + if 'last_modified' not in info: + info['last_modified'] = info['download']['date'] + + elif 'releases' in info and 'last_modified' not in info: + # Extract a date from the newest download + date = '1970-01-01 00:00:00' + for release in info['releases']: + if 'date' in release and release['date'] > date: + date = release['date'] + info['last_modified'] = date + + if info['name'] in previous_names: + info['previous_names'].extend(previous_names[info['name']]) + + output[info['name']] = info + yield (info['name'], info) + + self.cache['get_packages'] = output + + def get_renamed_packages(self): + """:return: A dict of the packages that have been renamed""" + + if self.schema_version < 2.0: + return self.repo_info.get('renamed_packages', {}) + + output = {} + for package in self.repo_info['packages']: + if 'previous_names' not in package: + continue + + previous_names = package['previous_names'] + if not isinstance(previous_names, list): + previous_names = [previous_names] + + for previous_name in previous_names: + output[previous_name] = package['name'] + + return output + + def get_unavailable_packages(self): + """ + Provides a list of packages that are unavailable for the current + platform/architecture that Sublime Text is running on. + + This list will be empty unless get_packages() is called first. + + :return: A list of package names + """ + + return self.unavailable_packages diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/reloader.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/reloader.py new file mode 100644 index 0000000..0696022 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/reloader.py @@ -0,0 +1,130 @@ +import sys + +import sublime + + +st_version = 2 +# With the way ST3 works, the sublime module is not "available" at startup +# which results in an empty version number +if sublime.version() == '' or int(sublime.version()) > 3000: + st_version = 3 + from imp import reload + + +# Python allows reloading modules on the fly, which allows us to do live upgrades. +# The only caveat to this is that you have to reload in the dependency order. +# +# Thus is module A depends on B and we don't reload B before A, when A is reloaded +# it will still have a reference to the old B. Thus we hard-code the dependency +# order of the various Package Control modules so they get reloaded properly. +# +# There are solutions for doing this all programatically, but this is much easier +# to understand. + +reload_mods = [] +for mod in sys.modules: + if mod[0:15].lower().replace(' ', '_') == 'package_control' and sys.modules[mod] != None: + reload_mods.append(mod) + +mod_prefix = 'package_control' +if st_version == 3: + mod_prefix = 'Package Control.' + mod_prefix + +mods_load_order = [ + '', + + '.sys_path', + '.cache', + '.http_cache', + '.ca_certs', + '.clear_directory', + '.cmd', + '.console_write', + '.preferences_filename', + '.show_error', + '.unicode', + '.thread_progress', + '.package_io', + '.semver', + '.versions', + + '.http', + '.http.invalid_certificate_exception', + '.http.debuggable_http_response', + '.http.debuggable_https_response', + '.http.debuggable_http_connection', + '.http.persistent_handler', + '.http.debuggable_http_handler', + '.http.validating_https_connection', + '.http.validating_https_handler', + + '.clients', + '.clients.client_exception', + '.clients.bitbucket_client', + '.clients.github_client', + '.clients.readme_client', + '.clients.json_api_client', + + '.providers', + '.providers.provider_exception', + '.providers.bitbucket_repository_provider', + '.providers.channel_provider', + '.providers.github_repository_provider', + '.providers.github_user_provider', + '.providers.repository_provider', + '.providers.release_selector', + + '.download_manager', + + '.downloaders', + '.downloaders.downloader_exception', + '.downloaders.rate_limit_exception', + '.downloaders.binary_not_found_error', + '.downloaders.non_clean_exit_error', + '.downloaders.non_http_error', + '.downloaders.caching_downloader', + '.downloaders.decoding_downloader', + '.downloaders.limiting_downloader', + '.downloaders.cert_provider', + '.downloaders.urllib_downloader', + '.downloaders.cli_downloader', + '.downloaders.curl_downloader', + '.downloaders.wget_downloader', + '.downloaders.wininet_downloader', + '.downloaders.background_downloader', + + '.upgraders', + '.upgraders.vcs_upgrader', + '.upgraders.git_upgrader', + '.upgraders.hg_upgrader', + + '.package_manager', + '.package_creator', + '.package_installer', + '.package_renamer', + + '.commands', + '.commands.add_channel_command', + '.commands.add_repository_command', + '.commands.create_binary_package_command', + '.commands.create_package_command', + '.commands.disable_package_command', + '.commands.discover_packages_command', + '.commands.enable_package_command', + '.commands.existing_packages_command', + '.commands.grab_certs_command', + '.commands.install_package_command', + '.commands.list_packages_command', + '.commands.package_message_command', + '.commands.remove_package_command', + '.commands.upgrade_all_packages_command', + '.commands.upgrade_package_command', + + '.package_cleanup', + '.automatic_upgrader' +] + +for suffix in mods_load_order: + mod = mod_prefix + suffix + if mod in reload_mods: + reload(sys.modules[mod]) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/semver.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/semver.py new file mode 100644 index 0000000..917fa77 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/semver.py @@ -0,0 +1,833 @@ +"""pysemver: Semantic Version comparing for Python. + +Provides comparing of semantic versions by using SemVer objects using rich comperations plus the +possibility to match a selector string against versions. Interesting for version dependencies. +Versions look like: "1.7.12+b.133" +Selectors look like: ">1.7.0 || 1.6.9+b.111 - 1.6.9+b.113" + +Example usages: + >>> SemVer(1, 2, 3, build=13) + SemVer("1.2.3+13") + >>> SemVer.valid("1.2.3.4") + False + >>> SemVer.clean("this is unimportant text 1.2.3-2 and will be stripped") + "1.2.3-2" + >>> SemVer("1.7.12+b.133").satisfies(">1.7.0 || 1.6.9+b.111 - 1.6.9+b.113") + True + >>> SemSel(">1.7.0 || 1.6.9+b.111 - 1.6.9+b.113").matches(SemVer("1.7.12+b.133"), + ... SemVer("1.6.9+b.112"), SemVer("1.6.10")) + [SemVer("1.7.12+b.133"), SemVer("1.6.9+b.112")] + >>> min(_) + SemVer("1.6.9+b.112") + >>> _.patch + 9 + +Exported classes: + * SemVer(collections.namedtuple()) + Parses semantic versions and defines methods for them. Supports rich comparisons. + * SemSel(tuple) + Parses semantic version selector strings and defines methods for them. + * SelParseError(Exception) + An error among others raised when parsing a semantic version selector failed. + +Other classes: + * SemComparator(object) + * SemSelAndChunk(list) + * SemSelOrChunk(list) + +Functions/Variables/Constants: + none + + +Copyright (c) 2013 Zachary King, FichteFoll + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: The above copyright notice and this +permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES +OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +import re +import sys +from collections import namedtuple # Python >=2.6 + + +__all__ = ('SemVer', 'SemSel', 'SelParseError') + + +if sys.version_info[0] == 3: + basestring = str + cmp = lambda a, b: (a > b) - (a < b) + + +# @functools.total_ordering would be nice here but was added in 2.7, __cmp__ is not Py3 +class SemVer(namedtuple("_SemVer", 'major, minor, patch, prerelease, build')): + """Semantic Version, consists of 3 to 5 components defining the version's adicity. + + See http://semver.org/ (2.0.0-rc.1) for the standard mainly used for this implementation, few + changes have been made. + + Information on this particular class and their instances: + - Immutable and hashable. + - Subclasses `collections.namedtuple`. + - Always `True` in boolean context. + - len() returns an int between 3 and 5; 4 when a pre-release is set and 5 when a build is + set. Note: Still returns 5 when build is set but not pre-release. + - Parts of the semantic version can be accessed by integer indexing, key (string) indexing, + slicing and getting an attribute. Returned slices are tuple. Leading '-' and '+' of + optional components are not stripped. Supported keys/attributes: + major, minor, patch, prerelease, build. + + Examples: + s = SemVer("1.2.3-4.5+6") + s[2] == 3 + s[:3] == (1, 2, 3) + s['build'] == '-4.5' + s.major == 1 + + Short information on semantic version structure: + + Semantic versions consist of: + * a major component (numeric) + * a minor component (numeric) + * a patch component (numeric) + * a pre-release component [optional] + * a build component [optional] + + The pre-release component is indicated by a hyphen '-' and followed by alphanumeric[1] sequences + separated by dots '.'. Sequences are compared numerically if applicable (both sequences of two + versions are numeric) or lexicographically. May also include hyphens. The existence of a + pre-release component lowers the actual version; the shorter pre-release component is considered + lower. An 'empty' pre-release component is considered to be the least version for this + major-minor-patch combination (e.g. "1.0.0-"). + + The build component may follow the optional pre-release component and is indicated by a plus '+' + followed by sequences, just as the pre-release component. Comparing works similarly. However the + existence of a build component raises the actual version and may also raise a pre-release. An + 'empty' build component is considered to be the highest version for this + major-minor-patch-prerelease combination (e.g. "1.2.3+"). + + + [1]: Regexp for a sequence: r'[0-9A-Za-z-]+'. + """ + + # Static class variables + _base_regex = r'''(?x) + (?P[0-9]+) + \.(?P[0-9]+) + \.(?P[0-9]+) + (?:\-(?P(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?))? + (?:\+(?P(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?))?''' + _search_regex = re.compile(_base_regex) + _match_regex = re.compile('^%s$' % _base_regex) # required because of $ anchor + + # "Constructor" + def __new__(cls, *args, **kwargs): + """There are two different constructor styles that are allowed: + - Option 1 allows specification of a semantic version as a string and the option to "clean" + the string before parsing it. + - Option 2 allows specification of each component separately as one parameter. + + Note that all the parameters specified in the following sections can be passed either as + positional or as named parameters while considering the usual Python rules for this. As + such, `SemVer(1, 2, minor=1)` will result in an exception and not in `SemVer("1.1.2")`. + + Option 1: + Constructor examples: + SemVer("1.0.1") + SemVer("this version 1.0.1-pre.1 here", True) + SemVer(ver="0.0.9-pre-alpha+34", clean=False) + + Parameters: + * ver (str) + The string containing the version. + * clean = `False` (bool; optional) + If this is true in boolean context, `SemVer.clean(ver)` is called before + parsing. + + Option 2: + Constructor examples: + SemVer(1, 0, 1) + SemVer(1, '0', prerelease='pre-alpha', patch=1, build=34) + SemVer(**dict(minor=2, major=1, patch=3)) + + Parameters: + * major (int, str, float ...) + * minor (...) + * patch (...) + Major to patch components must be an integer or convertable to an int (e.g. a + string or another number type). + + * prerelease = `None` (str, int, float ...; optional) + * build = `None` (...; optional) + Pre-release and build components should be a string (or number) type. + Will be passed to `str()` if not already a string but the final string must + match '^[0-9A-Za-z.-]*$' + + Raises: + * TypeError + Invalid parameter type(s) or combination (e.g. option 1 and 2). + * ValueError + Invalid semantic version or option 2 parameters unconvertable. + """ + ver, clean, comps = None, False, None + kw, l = kwargs.copy(), len(args) + len(kwargs) + + def inv(): + raise TypeError("Invalid parameter combination: args=%s; kwargs=%s" % (args, kwargs)) + + # Do validation and parse the parameters + if l == 0 or l > 5: + raise TypeError("SemVer accepts at least 1 and at most 5 arguments (%d given)" % l) + + elif l < 3: + if len(args) == 2: + ver, clean = args + else: + ver = args[0] if args else kw.pop('ver', None) + clean = kw.pop('clean', clean) + if kw: + inv() + + else: + comps = list(args) + [kw.pop(cls._fields[k], None) for k in range(len(args), 5)] + if kw or any(comps[i] is None for i in range(3)): + inv() + + typecheck = (int,) * 3 + (basestring,) * 2 + for i, (v, t) in enumerate(zip(comps, typecheck)): + if v is None: + continue + elif not isinstance(v, t): + try: + if i < 3: + v = typecheck[i](v) + else: # The real `basestring` can not be instatiated (Py2) + v = str(v) + except ValueError as e: + # Modify the exception message. I can't believe this actually works + e.args = ("Parameter #%d must be of type %s or convertable" + % (i, t.__name__),) + raise + else: + comps[i] = v + if t is basestring and not re.match(r"^[0-9A-Za-z.-]*$", v): + raise ValueError("Build and pre-release strings must match '^[0-9A-Za-z.-]*$'") + + # Final adjustments + if not comps: + if ver is None or clean is None: + inv() + ver = clean and cls.clean(ver) or ver + comps = cls._parse(ver) + + # Create the obj + return super(SemVer, cls).__new__(cls, *comps) + + # Magic methods + def __str__(self): + return ('.'.join(map(str, self[:3])) + + ('-' + self.prerelease if self.prerelease is not None else '') + + ('+' + self.build if self.build is not None else '')) + + def __repr__(self): + # Use the shortest representation - what would you prefer? + return 'SemVer("%s")' % str(self) + # return 'SemVer(%s)' % ', '.join('%s=%r' % (k, getattr(self, k)) for k in self._fields) + + def __len__(self): + return 3 + (self.build is not None and 2 or self.prerelease is not None) + + # Magic rich comparing methods + def __gt__(self, other): + return self._compare(other) == 1 if isinstance(other, SemVer) else NotImplemented + + def __eq__(self, other): + return self._compare(other) == 0 if isinstance(other, SemVer) else NotImplemented + + def __lt__(self, other): + return not (self > other or self == other) + + def __ge__(self, other): + return not (self < other) + + def __le__(self, other): + return not (self > other) + + def __ne__(self, other): + return not (self == other) + + # Utility (class-)methods + def satisfies(self, sel): + """Alias for `bool(sel.matches(self))` or `bool(SemSel(sel).matches(self))`. + + See `SemSel.__init__()` and `SemSel.matches(*vers)` for possible exceptions. + + Returns: + * bool: `True` if the version matches the passed selector, `False` otherwise. + """ + if not isinstance(sel, SemSel): + sel = SemSel(sel) # just "re-raise" exceptions + + return bool(sel.matches(self)) + + @classmethod + def valid(cls, ver): + """Check if `ver` is a valid semantic version. Classmethod. + + Parameters: + * ver (str) + The string that should be stripped. + + Raises: + * TypeError + Invalid parameter type. + + Returns: + * bool: `True` if it is valid, `False` otherwise. + """ + if not isinstance(ver, basestring): + raise TypeError("%r is not a string" % ver) + + if cls._match_regex.match(ver): + return True + else: + return False + + @classmethod + def clean(cls, vers): + """Remove everything before and after a valid version string. Classmethod. + + Parameters: + * vers (str) + The string that should be stripped. + + Raises: + * TypeError + Invalid parameter type. + + Returns: + * str: The stripped version string. Only the first version is matched. + * None: No version found in the string. + """ + if not isinstance(vers, basestring): + raise TypeError("%r is not a string" % vers) + m = cls._search_regex.search(vers) + if m: + return vers[m.start():m.end()] + else: + return None + + # Private (class-)methods + @classmethod + def _parse(cls, ver): + """Private. Do not touch. Classmethod. + """ + if not isinstance(ver, basestring): + raise TypeError("%r is not a string" % ver) + + match = cls._match_regex.match(ver) + + if match is None: + raise ValueError("'%s' is not a valid SemVer string" % ver) + + g = list(match.groups()) + for i in range(3): + g[i] = int(g[i]) + + return g # Will be passed as namedtuple(...)(*g) + + def _compare(self, other): + """Private. Do not touch. + self > other: 1 + self = other: 0 + self < other: -1 + """ + # Shorthand lambdas + cp_len = lambda t, i=0: cmp(len(t[i]), len(t[not i])) + + for i, (x1, x2) in enumerate(zip(self, other)): + if i > 2: + if x1 is None and x2 is None: + continue + + # self is greater when other has a prerelease but self doesn't + # self is less when other has a build but self doesn't + if x1 is None or x2 is None: + return int(2 * (i - 3.5)) * (1 - 2 * (x1 is None)) + + # self is less when other's build is empty + if i == 4 and (not x1 or not x2) and x1 != x2: + return 1 - 2 * bool(x1) + + # Split by '.' and use numeric comp or lexicographical order + t2 = [x1.split('.'), x2.split('.')] + for y1, y2 in zip(*t2): + if y1.isdigit() and y2.isdigit(): + y1 = int(y1) + y2 = int(y2) + if y1 > y2: + return 1 + elif y1 < y2: + return -1 + + # The "longer" sub-version is greater + d = cp_len(t2) + if d: + return d + else: + if x1 > x2: + return 1 + elif x1 < x2: + return -1 + + # The versions equal + return 0 + + +class SemComparator(object): + """Holds a SemVer object and a comparing operator and can match these against a given version. + + Constructor: SemComparator('<=', SemVer("1.2.3")) + + Methods: + * matches(ver) + """ + # Private properties + _ops = { + '>=': '__ge__', + '<=': '__le__', + '>': '__gt__', + '<': '__lt__', + '=': '__eq__', + '!=': '__ne__' + } + _ops_satisfy = ('~', '!') + + # Constructor + def __init__(self, op, ver): + """Constructor examples: + SemComparator('<=', SemVer("1.2.3")) + SemComparator('!=', SemVer("2.3.4")) + + Parameters: + * op (str, False, None) + One of [>=, <=, >, <, =, !=, !, ~] or evaluates to `False` which defaults to '~'. + '~' means a "satisfy" operation where pre-releases and builds are ignored. + '!' is a negative "~". + * ver (SemVer) + Holds the version to compare with. + + Raises: + * ValueError + Invalid `op` parameter. + * TypeError + Invalid `ver` parameter. + """ + super(SemComparator, self).__init__() + + if op and op not in self._ops_satisfy and op not in self._ops: + raise ValueError("Invalid value for `op` parameter.") + if not isinstance(ver, SemVer): + raise TypeError("`ver` parameter is not instance of SemVer.") + + # Default to '~' for versions with no build or pre-release + op = op or '~' + # Fallback to '=' and '!=' if len > 3 + if len(ver) != 3: + if op == '~': + op = '=' + if op == '!': + op = '!=' + + self.op = op + self.ver = ver + + # Magic methods + def __str__(self): + return (self.op or "") + str(self.ver) + + # Utility methods + def matches(self, ver): + """Match the internal version (constructor) against `ver`. + + Parameters: + * ver (SemVer) + + Raises: + * TypeError + Could not compare `ver` against the version passed in the constructor with the + passed operator. + + Returns: + * bool + `True` if the version matched the specified operator and internal version, `False` + otherwise. + """ + if self.op in self._ops_satisfy: + # Compare only the first three parts (which are tuples) and directly + return bool((self.ver[:3] == ver[:3]) + (self.op == '!') * -1) + ret = getattr(ver, self._ops[self.op])(self.ver) + if ret == NotImplemented: + raise TypeError("Unable to compare %r with operator '%s'" % (ver, self.op)) + return ret + + +class SemSelAndChunk(list): + """Extends list and defines a few methods used for matching versions. + + New elements should be added by calling `.add_child(op, ver)` which creates a SemComparator + instance and adds that to itself. + + Methods: + * matches(ver) + * add_child(op, ver) + """ + # Magic methods + def __str__(self): + return ' '.join(map(str, self)) + + # Utitlity methods + def matches(self, ver): + """Match all of the added children against `ver`. + + Parameters: + * ver (SemVer) + + Raises: + * TypeError + Invalid `ver` parameter. + + Returns: + * bool: + `True` if *all* of the SemComparator children match `ver`, `False` otherwise. + """ + if not isinstance(ver, SemVer): + raise TypeError("`ver` parameter is not instance of SemVer.") + return all(cp.matches(ver) for cp in self) + + def add_child(self, op, ver): + """Create a SemComparator instance with the given parameters and appends that to self. + + Parameters: + * op (str) + * ver (SemVer) + Both parameters are forwarded to `SemComparator.__init__`, see there for a more detailed + description. + + Raises: + Exceptions raised by `SemComparator.__init__`. + """ + self.append(SemComparator(op, SemVer(ver))) + + +class SemSelOrChunk(list): + """Extends list and defines a few methods used for matching versions. + + New elements should be added by calling `.new_child()` which returns a SemSelAndChunk + instance. + + Methods: + * matches(ver) + * new_child() + """ + # Magic methods + def __str__(self): + return ' || '.join(map(str, self)) + + # Utility methods + def matches(self, ver): + """Match all of the added children against `ver`. + + Parameters: + * ver (SemVer) + + Raises: + * TypeError + Invalid `ver` parameter. + + Returns: + * bool + `True` if *any* of the SemSelAndChunk children matches `ver`. + `False` otherwise. + """ + if not isinstance(ver, SemVer): + raise TypeError("`ver` parameter is not instance of SemVer.") + return any(ch.matches(ver) for ch in self) + + def new_child(self): + """Creates a new SemSelAndChunk instance, appends it to self and returns it. + + Returns: + * SemSelAndChunk: An empty instance. + """ + ch = SemSelAndChunk() + self.append(ch) + return ch + + +class SelParseError(Exception): + """An Exception raised when parsing a semantic selector failed. + """ + pass + + +# Subclass `tuple` because this is a somewhat simple method to make this immutable +class SemSel(tuple): + """A Semantic Version Selector, holds a selector and can match it against semantic versions. + + Information on this particular class and their instances: + - Immutable but not hashable because the content within might have changed. + - Subclasses `tuple` but does not behave like one. + - Always `True` in boolean context. + - len() returns the number of containing *and chunks* (see below). + - Iterable, iterates over containing *and chunks*. + + When talking about "versions" it refers to a semantic version (SemVer). For information on how + versions compare to one another, see SemVer's doc string. + + List for **comparators**: + "1.0.0" matches the version 1.0.0 and all its pre-release and build variants + "!1.0.0" matches any version that is not 1.0.0 or any of its variants + "=1.0.0" matches only the version 1.0.0 + "!=1.0.0" matches any version that is not 1.0.0 + ">=1.0.0" matches versions greater than or equal 1.0.0 + "<1.0.0" matches versions smaller than 1.0.0 + "1.0.0 - 1.0.3" matches versions greater than or equal 1.0.0 thru 1.0.3 + "~1.0" matches versions greater than or equal 1.0.0 thru 1.0.9999 (and more) + "~1", "1.x", "1.*" match versions greater than or equal 1.0.0 thru 1.9999.9999 (and more) + "~1.1.2" matches versions greater than or equal 1.1.2 thru 1.1.9999 (and more) + "~1.1.2+any" matches versions greater than or equal 1.1.2+any thru 1.1.9999 (and more) + "*", "~", "~x" match any version + + Multiple comparators can be combined by using ' ' spaces and every comparator must match to make + the **and chunk** match a version. + Multiple and chunks can be combined to **or chunks** using ' || ' and match if any of the and + chunks split by these matches. + + A complete example would look like: + ~1 || 0.0.3 || <0.0.2 >0.0.1+b.1337 || 2.0.x || 2.1.0 - 2.1.0+b.12 !=2.1.0+b.9 + + Methods: + * matches(*vers) + """ + # Private properties + _fuzzy_regex = re.compile(r'''(?x)^ + (?P[<>]=?|~>?=?)? + (?:(?P\d+) + (?:\.(?P\d+) + (?:\.(?P\d+) + (?P[-+][a-zA-Z0-9-+.]*)? + )? + )? + )?$''') + _xrange_regex = re.compile(r'''(?x)^ + (?P[<>]=?|~>?=?)? + (?:(?P\d+|[xX*]) + (?:\.(?P\d+|[xX*]) + (?:\.(?P\d+|[xX*]))? + )? + ) + (?P.*)$''') + _split_op_regex = re.compile(r'^(?P=|[<>!]=?)?(?P.*)$') + + # "Constructor" + def __new__(cls, sel): + """Constructor examples: + SemSel(">1.0.0") + SemSel("~1.2.9 !=1.2.12") + + Parameters: + * sel (str) + A version selector string. + + Raises: + * TypeError + `sel` parameter is not a string. + * ValueError + A version in the selector could not be matched as a SemVer. + * SemParseError + The version selector's syntax is unparsable; invalid ranges (fuzzy, xrange or + explicit range) or invalid '||' + """ + chunk = cls._parse(sel) + return super(SemSel, cls).__new__(cls, (chunk,)) + + # Magic methods + def __str__(self): + return str(self._chunk) + + def __repr__(self): + return 'SemSel("%s")' % self._chunk + + def __len__(self): + # What would you expect? + return len(self._chunk) + + def __iter__(self): + return iter(self._chunk) + + # Read-only (private) attributes + @property + def _chunk(self): + return self[0] + + # Utility methods + def matches(self, *vers): + """Match the selector against a selection of versions. + + Parameters: + * *vers (str, SemVer) + Versions can be passed as strings and SemVer objects will be created with them. + May also be a mixed list. + + Raises: + * TypeError + A version is not an instance of str (basestring) or SemVer. + * ValueError + A string version could not be parsed as a SemVer. + + Returns: + * list + A list with all the versions that matched, may be empty. Use `max()` to determine + the highest matching version, or `min()` for the lowest. + """ + ret = [] + for v in vers: + if isinstance(v, str): + t = self._chunk.matches(SemVer(v)) + elif isinstance(v, SemVer): + t = self._chunk.matches(v) + else: + raise TypeError("Invalid parameter type '%s': %s" % (v, type(v))) + if t: + ret.append(v) + + return ret + + # Private methods + @classmethod + def _parse(cls, sel): + """Private. Do not touch. + + 1. split by whitespace into tokens + a. start new and_chunk on ' || ' + b. parse " - " ranges + c. replace "xX*" ranges with "~" equivalent + d. parse "~" ranges + e. parse unmatched token as comparator + ~. append to current and_chunk + 2. return SemSelOrChunk + + Raises TypeError, ValueError or SelParseError. + """ + if not isinstance(sel, basestring): + raise TypeError("Selector must be a string") + if not sel: + raise ValueError("String must not be empty") + + # Split selector by spaces and crawl the tokens + tokens = sel.split() + i = -1 + or_chunk = SemSelOrChunk() + and_chunk = or_chunk.new_child() + + while i + 1 < len(tokens): + i += 1 + t = tokens[i] + + # Replace x ranges with ~ selector + m = cls._xrange_regex.match(t) + m = m and m.groups('') + if m and any(not x.isdigit() for x in m[1:4]) and not m[0].startswith('>'): + # (do not match '>1.0' or '>*') + if m[4]: + raise SelParseError("XRanges do not allow pre-release or build components") + + # Only use digit parts and fail if digit found after non-digit + mm, xran = [], False + for x in m[1:4]: + if x.isdigit(): + if xran: + raise SelParseError("Invalid fuzzy range or XRange '%s'" % tokens[i]) + mm.append(x) + else: + xran = True + t = m[0] + '.'.join(mm) # x for x in m[1:4] if x.isdigit()) + # Append "~" if not already present + if not t.startswith('~'): + t = '~' + t + + # switch t: + if t == '||': + if i == 0 or tokens[i - 1] == '||' or i + 1 == len(tokens): + raise SelParseError("OR range must not be empty") + # Start a new and_chunk + and_chunk = or_chunk.new_child() + + elif t == '-': + # ' - ' range + i += 1 + invalid = False + try: + # If these result in exceptions, you know you're doing it wrong + t = tokens[i] + c = and_chunk[-1] + except: + raise SelParseError("Invalid ' - ' range position") + + # If there is an op in front of one of the bound versions + invalid = (c.op not in ('=', '~') + or cls._split_op_regex.match(t).group(1) not in (None, '=')) + if invalid: + raise SelParseError("Invalid ' - ' range '%s - %s'" + % (tokens[i - 2], tokens[i])) + + c.op = ">=" + and_chunk.add_child('<=', t) + + elif t == '': + # Multiple spaces + pass + + elif t.startswith('~'): + m = cls._fuzzy_regex.match(t) + if not m: + raise SelParseError("Invalid fuzzy range or XRange '%s'" % tokens[i]) + + mm, m = m.groups('')[1:4], m.groupdict('') # mm: major to patch + + # Minimum requirement + min_ver = ('.'.join(x or '0' for x in mm) + '-' + if not m['other'] + else cls._split_op_regex(t[1:]).group('ver')) + and_chunk.add_child('>=', min_ver) + + if m['major']: + # Increase version before none (or second to last if '~1.2.3') + e = [0, 0, 0] + for j, d in enumerate(mm): + if not d or j == len(mm) - 1: + e[j - 1] = e[j - 1] + 1 + break + e[j] = int(d) + + and_chunk.add_child('<', '.'.join(str(x) for x in e) + '-') + + # else: just plain '~' or '*', or '~>X' which are already handled + + else: + # A normal comparator + m = cls._split_op_regex.match(t).groupdict() # this regex can't fail + and_chunk.add_child(**m) + + # Finally return the or_chunk + return or_chunk \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/show_error.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/show_error.py new file mode 100644 index 0000000..b8169c9 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/show_error.py @@ -0,0 +1,12 @@ +import sublime + + +def show_error(string): + """ + Displays an error message with a standard "Package Control" header + + :param string: + The error to display + """ + + sublime.error_message(u'Package Control\n\n%s' % string) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/sys_path.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/sys_path.py new file mode 100644 index 0000000..10daa3d --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/sys_path.py @@ -0,0 +1,27 @@ +import sys +import os + +if os.name == 'nt': + from ctypes import windll, create_unicode_buffer + +import sublime + + +def add_to_path(path): + # Python 2.x on Windows can't properly import from non-ASCII paths, so + # this code added the DOC 8.3 version of the lib folder to the path in + # case the user's username includes non-ASCII characters + if os.name == 'nt': + buf = create_unicode_buffer(512) + if windll.kernel32.GetShortPathNameW(path, buf, len(buf)): + path = buf.value + + if path not in sys.path: + sys.path.append(path) + + +lib_folder = os.path.join(sublime.packages_path(), 'Package Control', 'lib') +add_to_path(os.path.join(lib_folder, 'all')) + +if os.name == 'nt': + add_to_path(os.path.join(lib_folder, 'windows')) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/thread_progress.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/thread_progress.py new file mode 100644 index 0000000..b40c564 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/thread_progress.py @@ -0,0 +1,46 @@ +import sublime + + +class ThreadProgress(): + """ + Animates an indicator, [= ], in the status area while a thread runs + + :param thread: + The thread to track for activity + + :param message: + The message to display next to the activity indicator + + :param success_message: + The message to display once the thread is complete + """ + + def __init__(self, thread, message, success_message): + self.thread = thread + self.message = message + self.success_message = success_message + self.addend = 1 + self.size = 8 + sublime.set_timeout(lambda: self.run(0), 100) + + def run(self, i): + if not self.thread.is_alive(): + if hasattr(self.thread, 'result') and not self.thread.result: + sublime.status_message('') + return + sublime.status_message(self.success_message) + return + + before = i % self.size + after = (self.size - 1) - before + + sublime.status_message('%s [%s=%s]' % \ + (self.message, ' ' * before, ' ' * after)) + + if not after: + self.addend = -1 + if not before: + self.addend = 1 + i += self.addend + + sublime.set_timeout(lambda: self.run(i), 100) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/unicode.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/unicode.py new file mode 100644 index 0000000..f0464a2 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/unicode.py @@ -0,0 +1,49 @@ +import os +import locale +import sys + + +# Sublime Text on OS X does not seem to report the correct encoding +# so we hard-code that to UTF-8 +_encoding = 'utf-8' if sys.platform == 'darwin' else locale.getpreferredencoding() + +_fallback_encodings = ['utf-8', 'cp1252'] + + +def unicode_from_os(e): + """ + This is needed as some exceptions coming from the OS are + already encoded and so just calling unicode(e) will result + in an UnicodeDecodeError as the string isn't in ascii form. + + :param e: + The exception to get the value of + + :return: + The unicode version of the exception message + """ + + if sys.version_info >= (3,): + return str(e) + + try: + if isinstance(e, Exception): + e = e.message + + if isinstance(e, unicode): + return e + + if isinstance(e, int): + e = str(e) + + return unicode(e, _encoding) + + # If the "correct" encoding did not work, try some defaults, and then just + # obliterate characters that we can't seen to decode properly + except UnicodeDecodeError: + for encoding in _fallback_encodings: + try: + return unicode(e, encoding, errors='strict') + except: + pass + return unicode(e, errors='replace') diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/upgraders/__init__.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/upgraders/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/upgraders/git_upgrader.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/upgraders/git_upgrader.py new file mode 100644 index 0000000..878b1fd --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/upgraders/git_upgrader.py @@ -0,0 +1,106 @@ +import os + +from ..cache import set_cache, get_cache +from ..show_error import show_error +from .vcs_upgrader import VcsUpgrader + + +class GitUpgrader(VcsUpgrader): + """ + Allows upgrading a local git-repository-based package + """ + + cli_name = 'git' + + def retrieve_binary(self): + """ + Returns the path to the git executable + + :return: The string path to the executable or False on error + """ + + name = 'git' + if os.name == 'nt': + name += '.exe' + binary = self.find_binary(name) + if binary and os.path.isdir(binary): + full_path = os.path.join(binary, name) + if os.path.exists(full_path): + binary = full_path + if not binary: + show_error((u'Unable to find %s. Please set the git_binary setting by accessing the ' + + u'Preferences > Package Settings > Package Control > Settings \u2013 User menu entry. ' + + u'The Settings \u2013 Default entry can be used for reference, but changes to that will be ' + + u'overwritten upon next upgrade.') % name) + return False + + if os.name == 'nt': + tortoise_plink = self.find_binary('TortoisePlink.exe') + if tortoise_plink: + os.environ.setdefault('GIT_SSH', tortoise_plink) + return binary + + def get_working_copy_info(self): + binary = self.retrieve_binary() + if not binary: + return False + + # Get the current branch name + res = self.execute([binary, 'symbolic-ref', '-q', 'HEAD'], self.working_copy) + branch = res.replace('refs/heads/', '') + + # Figure out the remote and the branch name on the remote + remote = self.execute([binary, 'config', '--get', 'branch.%s.remote' % branch], self.working_copy) + res = self.execute([binary, 'config', '--get', 'branch.%s.merge' % branch], self.working_copy) + remote_branch = res.replace('refs/heads/', '') + + return { + 'branch': branch, + 'remote': remote, + 'remote_branch': remote_branch + } + + def run(self): + """ + Updates the repository with remote changes + + :return: False or error, or True on success + """ + + binary = self.retrieve_binary() + if not binary: + return False + + info = self.get_working_copy_info() + + args = [binary] + args.extend(self.update_command) + args.extend([info['remote'], info['remote_branch']]) + self.execute(args, self.working_copy) + return True + + def incoming(self): + """:return: bool if remote revisions are available""" + + cache_key = self.working_copy + '.incoming' + incoming = get_cache(cache_key) + if incoming != None: + return incoming + + binary = self.retrieve_binary() + if not binary: + return False + + info = self.get_working_copy_info() + + res = self.execute([binary, 'fetch', info['remote']], self.working_copy) + if res == False: + return False + + args = [binary, 'log'] + args.append('..%s/%s' % (info['remote'], info['remote_branch'])) + output = self.execute(args, self.working_copy) + incoming = len(output) > 0 + + set_cache(cache_key, incoming, self.cache_length) + return incoming diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/upgraders/hg_upgrader.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/upgraders/hg_upgrader.py new file mode 100644 index 0000000..36dfb48 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/upgraders/hg_upgrader.py @@ -0,0 +1,74 @@ +import os + +from ..cache import set_cache, get_cache +from ..show_error import show_error +from .vcs_upgrader import VcsUpgrader + + +class HgUpgrader(VcsUpgrader): + """ + Allows upgrading a local mercurial-repository-based package + """ + + cli_name = 'hg' + + def retrieve_binary(self): + """ + Returns the path to the hg executable + + :return: The string path to the executable or False on error + """ + + name = 'hg' + if os.name == 'nt': + name += '.exe' + binary = self.find_binary(name) + if binary and os.path.isdir(binary): + full_path = os.path.join(binary, name) + if os.path.exists(full_path): + binary = full_path + if not binary: + show_error((u'Unable to find %s. Please set the hg_binary setting by accessing the ' + + u'Preferences > Package Settings > Package Control > Settings \u2013 User menu entry. ' + + u'The Settings \u2013 Default entry can be used for reference, but changes to that will be ' + + u'overwritten upon next upgrade.') % name) + return False + return binary + + def run(self): + """ + Updates the repository with remote changes + + :return: False or error, or True on success + """ + + binary = self.retrieve_binary() + if not binary: + return False + args = [binary] + args.extend(self.update_command) + args.append('default') + self.execute(args, self.working_copy) + return True + + def incoming(self): + """:return: bool if remote revisions are available""" + + cache_key = self.working_copy + '.incoming' + incoming = get_cache(cache_key) + if incoming != None: + return incoming + + binary = self.retrieve_binary() + if not binary: + return False + + args = [binary, 'in', '-q', 'default'] + output = self.execute(args, self.working_copy) + if output == False: + return False + + incoming = len(output) > 0 + + set_cache(cache_key, incoming, self.cache_length) + return incoming diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/upgraders/vcs_upgrader.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/upgraders/vcs_upgrader.py new file mode 100644 index 0000000..d82abe7 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/upgraders/vcs_upgrader.py @@ -0,0 +1,27 @@ +from ..cmd import create_cmd, Cli + + +class VcsUpgrader(Cli): + """ + Base class for updating packages that are a version control repository on local disk + + :param vcs_binary: + The full filesystem path to the executable for the version control + system. May be set to None to allow the code to try and find it. + + :param update_command: + The command to pass to the version control executable to update the + repository. + + :param working_copy: + The local path to the working copy/package directory + + :param cache_length: + The lenth of time to cache if incoming changesets are available + """ + + def __init__(self, vcs_binary, update_command, working_copy, cache_length, debug): + self.update_command = update_command + self.working_copy = working_copy + self.cache_length = cache_length + super(VcsUpgrader, self).__init__(vcs_binary, debug) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/versions.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/versions.py new file mode 100644 index 0000000..90a5ef6 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/versions.py @@ -0,0 +1,81 @@ +import re + +from .semver import SemVer +from .console_write import console_write + + +def semver_compat(v): + if isinstance(v, SemVer): + return str(v) + + # Allowing passing in a dict containing info about a package + if isinstance(v, dict): + if 'version' not in v: + return '0' + v = v['version'] + + # Trim v off of the front + v = re.sub('^v', '', v) + + # We prepend 0 to all date-based version numbers so that developers + # may switch to explicit versioning from GitHub/BitBucket + # versioning based on commit dates. + # + # When translating dates into semver, the way to get each date + # segment into the version is to treat the year and month as + # minor and patch, and then the rest as a numeric build version + # with four different parts. The result looks like: + # 0.2012.11+10.31.23.59 + date_match = re.match('(\d{4})\.(\d{2})\.(\d{2})\.(\d{2})\.(\d{2})\.(\d{2})$', v) + if date_match: + v = '0.%s.%s+%s.%s.%s.%s' % date_match.groups() + + # This handles version that were valid pre-semver with 4+ dotted + # groups, such as 1.6.9.0 + four_plus_match = re.match('(\d+\.\d+\.\d+)[T\.](\d+(\.\d+)*)$', v) + if four_plus_match: + v = '%s+%s' % (four_plus_match.group(1), four_plus_match.group(2)) + + # Semver must have major, minor, patch + elif re.match('^\d+$', v): + v += '.0.0' + elif re.match('^\d+\.\d+$', v): + v += '.0' + return v + + +def version_comparable(string): + return SemVer(semver_compat(string)) + + +def version_exclude_prerelease(versions): + output = [] + for version in versions: + if SemVer(semver_compat(version)).prerelease != None: + continue + output.append(version) + return output + + +def version_filter(versions, allow_prerelease=False): + output = [] + for version in versions: + no_v_version = re.sub('^v', '', version) + if not SemVer.valid(no_v_version): + continue + if not allow_prerelease and SemVer(no_v_version).prerelease != None: + continue + output.append(version) + return output + + +def _version_sort_key(item): + return SemVer(semver_compat(item)) + + +def version_sort(sortable, **kwargs): + try: + return sorted(sortable, key=_version_sort_key, **kwargs) + except (ValueError) as e: + console_write(u"Error sorting versions - %s" % e, True) + return [] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/readme.creole b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/readme.creole index eb37565..50b3d69 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/readme.creole +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/readme.creole @@ -1,7 +1,7 @@ = Sublime Package Control -A Sublime Text 2 (http://www.sublimetext.com/2) package manager for easily -discovering, install, upgrading and removing packages. Also includes an +A Sublime Text 2/3 (http://www.sublimetext.com) package manager for easily +discovering, installing, upgrading and removing packages. Also includes an automatic updater and package creation tool. Packages can be installed from GitHub, BitBucket or custom package repositories. @@ -14,10 +14,12 @@ instructions, screenshots and documentation. == License -Sublime Package Control (except for the ntlm library) is licensed under the MIT -license. +Sublime Package Control is licensed under the MIT license. - Copyright (c) 2011-2012 Will Bond +All of the source code (except for package_control/semver.py), is under the +license: + + Copyright (c) 2011-2013 Will Bond Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -37,7 +39,24 @@ license. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -The ntlm library (for Windows) is from the python-ntlm project -(http://code.google.com/p/python-ntlm/) and is licensed under the GNU Lesser -General Public License (LGPL). Details can be found in the source files -located in lib/windows/ntlm/. \ No newline at end of file +package_control/semver.py is under the license: + + Copyright (c) 2013 Zachary King, FichteFoll + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/RecentActiveFiles/package-metadata.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/RecentActiveFiles/package-metadata.json index 5d0ccb2..7b01737 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/RecentActiveFiles/package-metadata.json +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/RecentActiveFiles/package-metadata.json @@ -1 +1 @@ -{"url": "https://github.com/jugyo/SublimeRecentActiveFiles", "version": "2013.01.17.03.59.02", "description": "Sublime Text 2 plugin that shows and opens recent activated files."} \ No newline at end of file +{"url": "https://github.com/jugyo/SublimeRecentActiveFiles", "version": "2013.01.17.08.59.02", "description": "Sublime Text 2 plugin that shows and opens recent activated files."} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Search Anywhere/package-metadata.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Search Anywhere/package-metadata.json index 0de4b03..9063051 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Search Anywhere/package-metadata.json +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Search Anywhere/package-metadata.json @@ -1 +1 @@ -{"url": "http://www.ericmartel.com/sublime-text-2-search-anywhere/", "version": "2012.03.20.16.35.46", "description": "Utility to quickly search on multiple search engines from the current selection or custom input. Default search engine configurable per file type. Search Engines defined through JSON including Google, Yahoo, Bing, Stack Overflow, PHP.net, sitepoint and caniuse.com "} \ No newline at end of file +{"url": "http://www.ericmartel.com/sublime-text-2-search-anywhere/", "version": "2012.03.20.20.35.46", "description": "Utility to quickly search on multiple search engines from the current selection or custom input. Default search engine configurable per file type. Search Engines defined through JSON including Google, Yahoo, Bing, Stack Overflow, PHP.net, sitepoint and caniuse.com "} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/Commands.sublime-commands b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/Commands.sublime-commands index dd290f6..9d0c087 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/Commands.sublime-commands +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/Commands.sublime-commands @@ -80,5 +80,9 @@ "caption": "File: Open In Browser - Production Server", "command": "side_bar_open_in_browser", "args":{"paths":[], "type":"production"} + }, + { + "caption": "Side Bar: Refresh", + "command": "refresh_folder_list" } ] \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/SideBar.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/SideBar.py index c0400dd..d3b783b 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/SideBar.py +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/SideBar.py @@ -14,22 +14,6 @@ from send2trash import send2trash if sublime.platform() == 'windows': import _winreg -def disable_default(): - default = sublime.packages_path()+'/Default/Side Bar.sublime-menu' - desired = sublime.packages_path()+'/SideBarEnhancements/disable_default/Side Bar.sublime-menu.txt' - if file(default, 'r').read() == file(desired, 'r').read(): - file(default, 'w+').write('[/*'+file(desired, 'r').read()+'*/]') - - default = sublime.packages_path()+'/Default/Side Bar Mount Point.sublime-menu' - desired = sublime.packages_path()+'/SideBarEnhancements/disable_default/Side Bar Mount Point.sublime-menu.txt' - if file(default, 'r').read() == file(desired, 'r').read(): - file(default, 'w+').write('[/*'+file(desired, 'r').read()+'*/]') - -try: - disable_default(); -except: - pass - def expand_vars(path): for k, v in os.environ.iteritems(): # dirty hack, this should be autofixed in python3 @@ -1240,9 +1224,7 @@ class SideBarProjectItemExcludeCommand(sublime_plugin.WindowCommand): class SideBarOpenInBrowserCommand(sublime_plugin.WindowCommand): def run(self, paths = [], type = False): - browser = s.get("default_browser") - if browser == '': - browser = 'firefox' + browser = s.get("default_browser", "") if type == False or type == 'testing': type = 'url_testing' @@ -1365,6 +1347,8 @@ class SideBarOpenInBrowserCommand(sublime_plugin.WindowCommand): items.extend([ '/usr/bin/chromium' ,'chromium' + ,'/usr/bin/chromium-browser' + ,'chromium-browser' ]) commands = ['-new-tab', url] elif browser == 'firefox': @@ -1430,8 +1414,11 @@ class SideBarOpenInBrowserCommand(sublime_plugin.WindowCommand): ]) commands = ['-new-tab', '-url', url] else: - sublime.error_message('Browser "'+browser+'" not found!\nUse any of the following: firefox, chrome, chromium, opera, safari') - return + if s.get('portable_browser') != '': + items.extend([s.get('portable_browser')]) + commands = ['-new-tab', url] + #sublime.error_message('Browser "'+browser+'" not found!\nUse any of the following: firefox, chrome, chromium, opera, safari') + #return for item in items: try: diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/package-metadata.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/package-metadata.json index 28bde61..7677438 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/package-metadata.json +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/package-metadata.json @@ -1 +1 @@ -{"url": "https://github.com/titoBouzout/SideBarEnhancements", "version": "2013.03.24.15.45.28", "description": "Enhancements to Sublime Text sidebar. Files and folders."} \ No newline at end of file +{"url": "https://github.com/titoBouzout/SideBarEnhancements", "version": "2013.08.28.13.28.32", "description": "Enhancements to Sublime Text sidebar. Files and folders."} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/readme.md b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/readme.md index 90e7f3d..85e1e0b 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/readme.md +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/readme.md @@ -1,193 +1,4 @@ -# Sublime Text 3 Compatibilty +# This branch "master" is deprecated. -Please see: https://github.com/titoBouzout/SideBarEnhancements/tree/st3atyourownrisk +For the faster and better, Sublime Text 3, please see: https://github.com/titoBouzout/SideBarEnhancements/tree/st3 -# Description - - -Provides enhancements to the operations on Side Bar of Files and Folders for Sublime Text 2. See: http://www.sublimetext.com/ - -Notably provides delete as "move to trash", open with.. a and clipboard. Close, move, open and restore buffers affected by a rename/move command. - -Provides the basics: new file/folder, edit, open/run, reveal, find in selected/parent/project, cut, copy, paste, paste in parent, rename, move, delete, refresh.... - -The not so basic: copy paths as URIs, URLs, content as UTF8, content as data:uri base64 ( nice for embedding into CSS! ), copy as tags img/a/script/style, duplicate - - -Preference to control if a buffer should be closed when affected by a deletion operation. - -Allows to display "file modified date" and "file size" on statusbar. - -All commands available for files and folders(when applicable) . - -[img]http://dl.dropbox.com/u/43596449/tito/sublime/SideBar/screenshot.png[/img] - - - -# F12 key - -F12 key allows you to open the current file in browser. -If you want to add a url to that feature: - - * Right click any file on sidebar and select: "Project -> Edit Projects Preview URLs" - * Edit this file, and add your paths and URLs with the following structure: - -``` -{ - "S:/www/domain.tld":{ - "url_testing":"http://testing", - "url_production":"http://domain.tld" - }, - "C:/Users/luna/some/domain2.tld":{ - "url_testing":"http://testing1", - "url_production":"http://productiontld2" - } -} -``` - -```url_testing``` allows you to set the url of your local server, opened via F12 - -```url_production``` allows you to set the url of your production server, opened via ALT+F12 - -# Notes on configuring the `Open With` menu: - -Definitions file: `User/SidebarEnhancements/Open With/Side Bar.sublime-menu` (note the extra subfolder levels). -To open it, right-click on any file in an open project and select `Open With > Edit Applications...` - -- On OSX, the 'application' property simply takes the *name* of an application, to which the file at hand's full path will be passed as if with `open ...`, e.g.: "application": "Google Chrome" -- On OSX, invoking *shell* commands is NOT supported. - -# Todo - - * Use a real clipboard integrated with the OS - -# Installation - -Install this repository via "Package Control" http://wbond.net/sublime_packages/package_control - -# FAQ - -Q: Uninstall? - - * Follow the instructions here: https://github.com/titoBouzout/SideBarEnhancements/issues/18 - -Q: Why the menu is not shown on `Open Files`? - -- It should be mentioned that the package's context menu is only available for files and folders **in a project (section `Folders` in the side bar)**, and _not_ on the open files listed at the top of the side bar, due to a limitation of ST2. - -# Using the External Libraries - - - * "getImageInfo" to get width and height for images from "bfg-pages". See: http://code.google.com/p/bfg-pages/ - * "desktop" to be able to open files with system handlers. See: http://pypi.python.org/pypi/desktop - * "send2trash" to be able to send to the trash instead of deleting for ever!. See: http://pypi.python.org/pypi/Send2Trash - * "hurry.filesize" to be able to format file sizes. See: http://pypi.python.org/pypi/hurry.filesize/ - -# Source-code - - -https://github.com/titoBouzout/SideBarEnhancements - -# Forum Thread - - -http://www.sublimetext.com/forum/viewtopic.php?f=5&t=3331 - -# Contributors: - - - Leif Ringstad - - Sven Axelsson - - Dalibor Simacek - - Stephen Horne - -# Update v1.2: - -* Improved: Feature "find advanced -> in paths containing" or CTRL+ALT+F now provides instant search, contribution by @ryecroft, thanks a lot! -* Fix: When only 1 tab is open and setting "close_windows_when_empty" is true. If the user renames or delete the current file will cause the application to close by itself (it will be perceived as a crash but is not). -* New: Add to the command palette useful commands as duplicate, reveal, move, open project file, open in browser, refresh, rename -* New: added keybindings F12 to open in local server, ALT+F12 to open in production server. -* New: Allows to copy the URL of the selected items. -* Improved: When renaming/moving remember the tab position and syntax. -* small fixes: -- Correct display of commands that are available only for projects -- Be sure to return None if there is no open project -- only display a message when using the clipboard if something was copied. - -# Update v1.1: - -* New: Add boolean preference "confirm_before_deleting" which controls if a the package should ask the user to delete files and folders -* New: When using copy, cut or paste the editor will ask for "replace items" when these items exists. Note: When a folder exists the package will merge the two as in the OS. - -# Update v1.0: - -* New: Add boolean preference "close_affected_buffers_when_deleting_even_if_dirty" which controls if a buffer should be closed when affected by a deletion operation- - -# Update v0.9: - - -* Minor tweaks and fixes. -* Fix: Re-enable move to trash for OSX -* New: Allow to display "file modified time" and "file size" on statusbar via preferences. -* Fix: Disable of built-in function is now automatic. -* On the way: exclude from project, promote as project folder. ( requires restart to apply changes, looks like there is no way to reload project files.) -* Fix: Many appends of same directory to "sys.path" - -# Update v0.8: - - -* Full review for when the user has selection of multiples items. -* New: Added support for bookmarks and marks for when a view is moved. - -# Update v0.7: - - -* New: After a rename of a file or folder, the affected views will update(reload) to reflect the new location keeping intact content, selections, folded regions and scroll position. -* New: File path search - -# Update v0.6: - - -* Fix: Paste was pasting on parent folder (Misinterpretation of boolean) -* Fix: "Open with" works on Linux -* Improved: Allow case change on Windows when renaming a file or folder -* Improved: Update to "find commands" for version 2134 - -# Update v0.5: - - -* Change: Removed "files" prefix from commands. -* New: Ability to copy a path relative to the current view -* New: Ability to "paste in parent" -* New: Ctrl+T will ask for a new file on same folder as current view -* Improved: Context menu open faster - -# Update v0.4: - - -* Fix: "Open / Run" fixed on Linux thanks to project [desktop](http://pypi.python.org/pypi/desktop ) -* Improved: "Paste" command copy permission bits, last access time, last modification time, and flags -* Improved: "Delete" command send files to trash thanks to [Send2Trash](http://pypi.python.org/pypi/Send2Trash ) . NOTE: If "Delete" fails to send to trash it will ask for "Permanently Delete" On confirmation it delete the item forever. - -# Update v0.3: - - -* Fixed: Open should run correctly with some strange characters on paths -* New: "Open with.." is enabled and allows to set custom applications for different file extensions. -* New: "Copy content as Data URI" ( handy for embedding images on CSS files ) -* Improved: Copy img tags now add attributes width and height thanks to project [bfg-pages](http://code.google.com/p/bfg-pages/ ) and suggestion from nobleach. - -# Update v0.2: - - - * Copy paths and names in various formats. - * Removed license to not conflict with sublime - -# Update v0.1: - - - * Tweaks here, tweaks there. - * Renamed repository - * New: "edit" will open the file with sublime text. - * New: "open" will call to the command line with the file path - * New: a disabled "open with" for future use - * Tweaks: ids to all context elements diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Sublime Files/README.md b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Sublime Files/README.md index bff337f..fe3f14e 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Sublime Files/README.md +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Sublime Files/README.md @@ -64,3 +64,9 @@ For example, Gnome Terminal and iTerm2 users respectively will want to change te - "term_command": "gnome-terminal --working-directory=" - "term_command" : "open -a iTerm\ 2 " + +---------- + +__Sublime Text 3__ + +Sublime Text 3 is not officially supported by Sublime Files at the moment. However, there is an experimental branch "py3" that has a python3 version of Sublime Files that should work with Sublime Text 3. diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Sublime Files/package-metadata.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Sublime Files/package-metadata.json index 2edf95f..9abe24b 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Sublime Files/package-metadata.json +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Sublime Files/package-metadata.json @@ -1 +1 @@ -{"url": "https://github.com/al63/SublimeFiles", "version": "2013.03.29.16.32.31", "description": "Sublime Text 2 plugin for keyboard driven file navigation"} \ No newline at end of file +{"url": "https://github.com/al63/SublimeFiles", "version": "2013.07.16.01.35.12", "description": "Sublime Text 2 plugin for keyboard driven file navigation"} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Sublime Files/sublime_files.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Sublime Files/sublime_files.py index 3136215..5411933 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Sublime Files/sublime_files.py +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Sublime Files/sublime_files.py @@ -11,6 +11,7 @@ class SublimeFilesCommand(sublime_plugin.WindowCommand): try: self.home except: + self.current_dir = "" # first time starting up. ugly, but works settings = sublime.load_settings('SublimeFiles.sublime-settings') if os.name == 'nt': @@ -18,9 +19,13 @@ class SublimeFilesCommand(sublime_plugin.WindowCommand): else: self.home = 'HOME' try: - os.chdir(os.path.dirname(sublime.active_window().active_view().file_name())) + self.current_dir = os.path.dirname(sublime.active_window().active_view().file_name()) + os.chdir(self.current_dir) except: - os.chdir(os.getenv(self.home)) + self.current_dir = os.getenv(self.home) + os.chdir(self.current_dir) + + self.project_root = None self.bookmark = None self.term_command = settings.get('term_command') self.ignore_list = settings.get('ignore_list') @@ -31,6 +36,8 @@ class SublimeFilesCommand(sublime_plugin.WindowCommand): # function for showing panel for changing directories / opening files def open_navigator(self): + self.check_project_root() + self.current_dir = os.getcwdu() self.dir_files = ['[' + os.getcwdu() + ']', bullet + ' Directory actions', '..' + os.sep, '~' + os.sep] @@ -57,14 +64,29 @@ class SublimeFilesCommand(sublime_plugin.WindowCommand): self.dir_files.append(element) self.dir_files = self.dir_files[:4] + sorted(self.dir_files[4:], key=sort_files) + if self.bookmark: self.dir_files.insert(2, bullet + ' To bookmark (' + self.bookmark + ')') if self.window.active_view() and self.window.active_view().file_name(): self.dir_files.insert(2, bullet + ' To current view') + self.window.show_quick_panel(self.dir_files, self.handle_navigator_option, sublime.MONOSPACE_FONT) + # checks if the user has opened up a folder, and if so automatically navigate to the root + def check_project_root(self): + folders = self.window.folders() + if len(folders) > 0: + # If not one yet present or if has changed + if not self.project_root or self.project_root != folders[0]: + self.project_root = folders[0] + os.chdir(self.project_root) + elif self.project_root: + # if folders is empty now and we had a root, let's clear it out + self.project_root = None + # handles user's selection from open_navigator def handle_navigator_option(self, call_value): + os.chdir(self.current_dir) if call_value != -1: option = self.dir_files[call_value] if call_value == 0: diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/README.md b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/README.md index 14f3ae4..709271e 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/README.md +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/README.md @@ -1,6 +1,6 @@ # Soda Theme -Dark and light custom UI themes for Sublime Text 2. +Dark and light custom UI themes for Sublime Text 2 and Sublime Text 3. Project site: [http://buymeasoda.github.com/soda-theme/](http://buymeasoda.github.com/soda-theme/) @@ -12,7 +12,7 @@ Project site: [http://buymeasoda.github.com/soda-theme/](http://buymeasoda.githu ## Installation -Soda theme is designed to work with the latest [development build](http://www.sublimetext.com/dev) of Sublime Text 2. +Soda theme is designed to work with the latest development builds of Sublime Text, including [Sublime Text 2](http://www.sublimetext.com/dev) and [Sublime Text 3](http://www.sublimetext.com/3dev). ### Using Sublime Package Control @@ -20,9 +20,9 @@ If you are using Will Bond's excellent [Sublime Package Control](http://wbond.ne ### Using Git -Alternatively, if you are a git user, you can install the theme and keep up to date by cloning the repo directly into your `Packages` directory in the Sublime Text 2 application settings area. +Alternatively, if you are a git user, you can install the theme and keep up to date by cloning the repo directly into your `Packages` directory in the Sublime Text application settings area. -You can locate your Sublime Text 2 `Packages` directory by using the menu item `Preferences -> Browse Packages...`. +You can locate your Sublime Text `Packages` directory by using the menu item `Preferences -> Browse Packages...`. While inside the `Packages` directory, clone the theme repository using the command below: @@ -32,21 +32,35 @@ While inside the `Packages` directory, clone the theme repository using the comm * Download the files using the GitHub .zip download option * Unzip the files and rename the folder to `Theme - Soda` -* Copy the folder to your Sublime Text 2 `Packages` directory +* Find your `Packages` directory using the menu item `Preferences -> Browse Packages...` +* Copy the folder into your Sublime Text `Packages` directory ## Activating the theme -To configure Sublime Text 2 to use the theme: +To configure Sublime Text to use the theme, follow the instructions below for your specific version. + +### Sublime Text 2 * Open your User Settings Preferences file `Sublime Text 2 -> Preferences -> Settings - User` * Add (or update) your theme entry to be `"theme": "Soda Light.sublime-theme"` or `"theme": "Soda Dark.sublime-theme"` -### Example User Settings +**Example Sublime Text 2 User Settings** { "theme": "Soda Light.sublime-theme" } +### Sublime Text 3 + +* Open your User Settings Preferences file `Sublime Text -> Preferences -> Settings - User` +* Add (or update) your theme entry to be `"theme": "Soda Light 3.sublime-theme"` or `"theme": "Soda Dark 3.sublime-theme"` + +**Example Sublime Text 3 User Settings** + + { + "theme": "Soda Light 3.sublime-theme" + } + ## Additional Features ### Alternate Tab Styles @@ -59,6 +73,16 @@ By default, a square tab style is used. If you'd prefer to use the original curv ![Soda Tab Styles](http://buymeasoda.github.com/soda-theme/images/features/multiple-tab-styles.png) +### Sidebar Folder Icons + +Soda Theme has the ability to use folder icons in the sidebar. + +If you'd like to use folder icons in the sidebar instead of the regular arrows, add the following custom setting to your `Settings - User` file: + + "soda_folder_icons": true + +![Soda Folder Icons](http://buymeasoda.github.com/soda-theme/images/features/sidebar-folder-icons.png) + ### Retina Resolution UI Soda Theme has been designed to take advantage of retina resolution (high-dpi) displays. Both Soda Light and Soda Dark support retina displays. @@ -67,7 +91,7 @@ Soda Theme has been designed to take advantage of retina resolution (high-dpi) d ### Theme Customisation -Sublime Text 2 provides an elegant way to tweak existing themes without having to duplicate or maintain a separate copy of the original theme. If there are aspects of Soda Theme that you would like to adjust, take a look at the [theme customisation](https://github.com/buymeasoda/soda-theme/wiki/Theme-customisation) wiki page. +Sublime Text provides an elegant way to tweak existing themes without having to duplicate or maintain a separate copy of the original theme. If there are aspects of Soda Theme that you would like to adjust, take a look at the [theme customisation](https://github.com/buymeasoda/soda-theme/wiki/Theme-customisation) wiki page. ## Bonus Options @@ -78,7 +102,7 @@ The Soda Light screenshot uses a modified version of Espresso Tutti Colori and t If you'd like to use the syntax highlighting schemes shown in the screenshots: * Download [colour-schemes.zip](http://buymeasoda.github.com/soda-theme/extras/colour-schemes.zip) -* Unzip and place the extracted `tmtheme` files in the Sublime Text 2 `Packages/User` folder +* Unzip and place the extracted `tmtheme` files in the Sublime Text `Packages/User` folder * Enable the colour scheme via `Preferences -> Color Scheme -> User` ### Code Font diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark 3.sublime-theme b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark 3.sublime-theme new file mode 100644 index 0000000..e090ed4 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark 3.sublime-theme @@ -0,0 +1,1154 @@ +[ + +// +// TABS (REGULAR) +// + + // Tab set + { + "class": "tabset_control", + "layer0.texture": "Theme - Soda/Soda Dark/tabset-background.png", + "layer0.inner_margin": [1, 7], + "layer0.opacity": 1.0, + "content_margin": [-4, 0, -4, 3], + "tab_overlap": 5, + "tab_width": 180, + "tab_min_width": 45, + "tab_height": 25, + "mouse_wheel_switch": false + }, + { + "class": "tabset_control", + "settings": ["enable_tab_scrolling"], + "content_margin": [3, 0, 3, 3] + }, + { + "class": "tabset_control", + "settings": ["mouse_wheel_switches_tabs"], + "mouse_wheel_switch": true + }, + // Tab element + { + "class": "tab_control", + "content_margin": [12, 3, 12, 3], + "max_margin_trim": 0, + "hit_test_level": 0.0, + "layer0.texture": "Theme - Soda/Soda Dark/tab-inactive.png", + "layer0.inner_margin": [5, 5], + "layer0.opacity": 1.0 + }, + // Tab close state + { + "class": "tab_control", + "settings": ["show_tab_close_buttons"], + "content_margin": [12, 3, 7, 3] + }, + // Tab hover state + { + "class": "tab_control", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Dark/tab-hover.png" + }, + // Tab active state + { + "class": "tab_control", + "attributes": ["selected"], + "layer0.texture": "Theme - Soda/Soda Dark/tab-active.png" + }, + // Tab dirty state (close button hidden) + { + "class": "tab_control", + "settings": ["!show_tab_close_buttons"], + "attributes": ["dirty"], + "content_margin": [12, 3, 7, 3] + }, + +// +// TABS (CLASSIC) +// + + // Tab set + { + "class": "tabset_control", + "settings": ["soda_classic_tabs"], + "content_margin": [3, 4, 3, 3], + "tab_overlap": 24, + "tab_height": 28 + }, + { + "class": "tabset_control", + "settings": ["soda_classic_tabs", "enable_tab_scrolling"], + "content_margin": [6, 4, 6, 3] + }, + // Tab element + { + "class": "tab_control", + "settings": ["soda_classic_tabs"], + "content_margin": [22, 6, 22, 4], + "hit_test_level": 0.5, + "layer0.texture": "Theme - Soda/Soda Dark/classic/tab-inactive.png", + "layer0.inner_margin": [18, 4] + }, + // Tab close state + { + "class": "tab_control", + "settings": ["soda_classic_tabs", "show_tab_close_buttons"], + "content_margin": [22, 6, 15, 4] + }, + // Tab hover state + { + "class": "tab_control", + "settings": ["soda_classic_tabs"], + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Dark/classic/tab-hover.png" + }, + // Tab active state + { + "class": "tab_control", + "settings": ["soda_classic_tabs"], + "attributes": ["selected"], + "layer0.texture": "Theme - Soda/Soda Dark/classic/tab-active.png" + }, + // Tab dirty state (close button hidden) + { + "class": "tab_control", + "settings": ["soda_classic_tabs", "!show_tab_close_buttons"], + "attributes": ["dirty"], + "content_margin": [22, 6, 15, 4] + }, + +// +// TAB BUTTONS +// + + // Tab close button + { + "class": "tab_close_button", + "content_margin": [0, 0], + "layer0.texture": "Theme - Soda/Soda Dark/tab-close-inactive.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": 0 + }, + { + "class": "tab_close_button", + "settings": ["show_tab_close_buttons"], + "content_margin": [8, 8] + }, + { + "class": "tab_close_button", + "parents": [{"class": "tab_control", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Dark/tab-close.png", + "layer0.opacity": 1.0 + }, + { + "class": "tab_close_button", + "parents": [{"class": "tab_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/tab-close.png", + "layer0.opacity": 1.0 + }, + // Tab dirty button + { + "class": "tab_close_button", + "parents": [{"class": "tab_control", "attributes": ["dirty"]}], + "layer0.texture": "Theme - Soda/Soda Dark/tab-dirty-inactive.png" + }, + { + "class": "tab_close_button", + "settings": ["!show_tab_close_buttons"], + "parents": [{"class": "tab_control", "attributes": ["dirty"]}], + "content_margin": [8, 8] + }, + { + "class": "tab_close_button", + "parents": [{"class": "tab_control", "attributes": ["dirty", "hover"]}], + "layer0.opacity": 1.0 + }, + { + "class": "tab_close_button", + "parents": [{"class": "tab_control", "attributes": ["dirty", "selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/tab-dirty.png" + }, + // Tab highlight button + { + "class": "tab_close_button", + "settings": ["highlight_modified_tabs"], + "parents": [{"class": "tab_control", "attributes": ["dirty"]}], + "layer0.texture": "Theme - Soda/Soda Dark/tab-highlight-inactive.png" + }, + { + "class": "tab_close_button", + "settings": ["highlight_modified_tabs"], + "parents": [{"class": "tab_control", "attributes": ["dirty", "selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/tab-highlight.png" + }, + // Tab close button hover + { + "class": "tab_close_button", + "settings": ["show_tab_close_buttons"], + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Dark/tab-close-hover.png" + }, + // Tab close button pressed + { + "class": "tab_close_button", + "settings": ["show_tab_close_buttons"], + "attributes": ["pressed"], + "layer0.texture": "Theme - Soda/Soda Dark/tab-close-pressed.png" + }, + +// +// TAB LABELS +// + + { + "class": "tab_label", + "fade": true, + "fg": [170, 170, 170], + "shadow_color": [25, 25, 25], + "shadow_offset": [0, -1], + "font.italic": false + }, + { + "class": "tab_label", + "parents": [{"class": "tab_control", "attributes": ["hover"]}], + "fg": [200, 200, 200], + "shadow_color": [30, 30, 30] + }, + { + "class": "tab_label", + "parents": [{"class": "tab_control", "attributes": ["selected"]}], + "fg": [230, 230, 230], + "shadow_color": [35, 35, 35] + }, + { + "class": "tab_label", + "attributes": ["transient"], + "font.italic": true + }, + +// +// TAB SCROLLING +// + + // Tab dropdown + { + "class": "show_tabs_dropdown_button", + "content_margin": [9, 7, 8, 6], + "layer0.texture": "Theme - Soda/Soda Dark/tabset-list.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": 0 + }, + { + "class": "show_tabs_dropdown_button", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Dark/tabset-list-hover.png" + }, + // Tab scroll left + { + "class": "scroll_tabs_left_button", + "content_margin": [9, 7, 8, 6], + "layer0.texture": "Theme - Soda/Soda Dark/tabset-left.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": 0 + }, + { + "class": "scroll_tabs_left_button", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Dark/tabset-left-hover.png" + }, + // Tab scroll right + { + "class": "scroll_tabs_right_button", + "content_margin": [9, 7, 8, 6], + "layer0.texture": "Theme - Soda/Soda Dark/tabset-right.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": 0 + }, + { + "class": "scroll_tabs_right_button", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Dark/tabset-right-hover.png" + }, + +// +// FOLD BUTTONS +// + + { + "class": "fold_button_control", + "layer0.texture": "Theme - Soda/Soda Dark/fold-closed.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": 0, + "content_margin": [8, 8] + }, + { + "class": "fold_button_control", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Dark/fold-closed-hover.png" + }, + { + "class": "fold_button_control", + "attributes": ["expanded"], + "layer0.texture": "Theme - Soda/Soda Dark/fold-open.png" + }, + { + "class": "fold_button_control", + "attributes": ["expanded", "hover"], + "layer0.texture": "Theme - Soda/Soda Dark/fold-open-hover.png" + }, + +// +// STANDARD SCROLLBARS +// + + // Standard vertical scroll bar + { + "class": "scroll_bar_control", + "layer0.texture": "Theme - Soda/Soda Dark/standard-scrollbar-vertical.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": [2, 6], + "blur": false + }, + // Standard horizontal scroll bar + { + "class": "scroll_bar_control", + "attributes": ["horizontal"], + "layer0.texture": "Theme - Soda/Soda Dark/standard-scrollbar-horizontal.png", + "layer0.inner_margin": [6, 2], + "blur": false + }, + // Standard scroll bar corner + { + "class": "scroll_corner_control", + "layer0.texture": "Theme - Soda/Soda Dark/standard-scrollbar-corner.png", + "layer0.inner_margin": [2, 2], + "layer0.opacity": 1.0 + }, + // Standard vertical scroll puck + { + "class": "puck_control", + "layer0.texture": "Theme - Soda/Soda Dark/standard-puck-vertical.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": [0, 10], + "content_margin": [8, 12], + "blur": false + }, + // Standard horizontal scroll puck + { + "class": "puck_control", + "attributes": ["horizontal"], + "layer0.texture": "Theme - Soda/Soda Dark/standard-puck-horizontal.png", + "layer0.inner_margin": [10, 0], + "content_margin": [12, 8], + "blur": false + }, + +// +// OVERLAY SCROLLBARS +// + + // Overlay toggle scroll bar + { + "class": "scroll_area_control", + "settings": ["overlay_scroll_bars"], + "overlay": true + }, + { + "class": "scroll_area_control", + "settings": ["!overlay_scroll_bars"], + "overlay": false + }, + // Overlay vertical scroll bar + { + "class": "scroll_bar_control", + "settings": ["overlay_scroll_bars"], + "layer0.texture": "Theme - Soda/Soda Dark/overlay-scrollbar-vertical.png", + "layer0.inner_margin": [0, 5], + "blur": true + }, + // Overlay horizontal scroll bar + { + "class": "scroll_bar_control", + "settings": ["overlay_scroll_bars"], + "attributes": ["horizontal"], + "layer0.texture": "Theme - Soda/Soda Dark/overlay-scrollbar-horizontal.png", + "layer0.inner_margin": [5, 0], + "blur": true + }, + // Overlay vertical puck + { + "class": "puck_control", + "settings": ["overlay_scroll_bars"], + "layer0.texture": "Theme - Soda/Soda Dark/overlay-puck-vertical.png", + "layer0.inner_margin": [0, 5], + "content_margin": [5, 20], + "blur": true + }, + // Overlay horizontal puck + { + "class": "puck_control", + "settings": ["overlay_scroll_bars"], + "attributes": ["horizontal"], + "layer0.texture": "Theme - Soda/Soda Dark/overlay-puck-horizontal.png", + "layer0.inner_margin": [5, 0], + "content_margin": [20, 5], + "blur": true + }, + // Overlay light puck (for dark content) + { + "class": "puck_control", + "settings": ["overlay_scroll_bars"], + "attributes": ["dark"], + "layer0.texture": "Theme - Soda/Soda Dark/overlay-dark-puck-vertical.png" + }, + // Overlay light horizontal puck (for dark content) + { + "class": "puck_control", + "settings": ["overlay_scroll_bars"], + "attributes": ["horizontal", "dark"], + "layer0.texture": "Theme - Soda/Soda Dark/overlay-dark-puck-horizontal.png" + }, + +// +// EMPTY WINDOW BACKGROUND +// + + { + "class": "sheet_container_control", + "layer0.tint": [25, 25, 25], + "layer0.opacity": 1.0 + }, + +// +// GRID LAYOUT +// + + { + "class": "grid_layout_control", + "border_size": 1, + "border_color": [70, 70, 70] + }, + +// +// MINI MAP +// + + { + "class": "minimap_control", + "settings": ["always_show_minimap_viewport"], + "viewport_color": [255, 255, 255, 35], + "viewport_opacity": 1.0 + }, + { + "class": "minimap_control", + "settings": ["!always_show_minimap_viewport"], + "viewport_color": [255, 255, 255, 35], + "viewport_opacity": { "target": 0.0, "speed": 10.0, "interpolation": "smoothstep" } + }, + { + "class": "minimap_control", + "attributes": ["hover"], + "settings": ["!always_show_minimap_viewport"], + "viewport_opacity": 1.0 + }, + +// +// DIALOG +// + + { + "class": "dialog", + "layer0.tint": [67, 67, 67], + "layer0.opacity": 1.0 + }, + +// +// PROGRESS BAR +// + + { + "class": "progress_bar_control", + "layer0.tint": [30, 30, 30], + "layer0.opacity": 1.0 + }, + { + "class": "progress_gauge_control", + "layer0.tint": [144, 144, 144], + "layer0.opacity": 1.0, + "content_margin": [0, 6] + }, + +// +// LABELS +// + + // General labels + { + "class": "label_control", + "color": [204, 204, 204] + }, + // Text field labels + { + "class": "label_control", + "parents": [{"class": "panel_control"}], + "shadow_color": [50, 50, 50], + "shadow_offset": [0, -1] + }, + // Button labels + { + "class": "label_control", + "parents": [{"class": "button_control"}], + "shadow_color": [55, 55, 55], + "shadow_offset": [0, -1] + }, + +// +// TOOLTIP +// + + // Tooltip container + { + "class": "tool_tip_control", + "layer0.texture": "Theme - Soda/Soda Dark/tooltip.png", + "layer0.inner_margin": [1, 1], + "layer0.opacity": 0.95, + "content_margin": [3, 3] + }, + // Tooltip content + { + "class": "tool_tip_label_control", + "color": [0, 0, 0] + }, + +// +// STATUS BAR +// + + // Status bar container + { + "class": "status_bar", + "layer0.texture": "Theme - Soda/Soda Dark/status-bar-background.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": [2, 2], + "content_margin": [8, 4, 8, 4] + }, + // Status bar button + { + "class": "status_button", + "min_size": [100, 0] + }, + // Status bar label + { + "class": "label_control", + "parents": [{"class": "status_bar"}], + "color": [150, 150, 150], + "shadow_color": [25, 25, 25], + "shadow_offset": [0, -1] + }, + +// +// SIDEBAR +// + + // Sidebar container + { + "class": "sidebar_container", + "layer0.texture": "Theme - Soda/Soda Dark/sidebar-bg.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": [1, 1, 2, 1], + "content_margin": [0, 0, 1, 0] + }, + // Sidebar tree + { + "class": "sidebar_tree", + "row_padding": [8, 3], + "indent": 15, + "indent_offset": 15, + "indent_top_level": false, + "dark_content": true + }, + // Sidebar rows + { + "class": "tree_row", + "layer0.texture": "Theme - Soda/Soda Dark/sidebar-row-selected.png", + "layer0.opacity": 0.0, + "layer0.inner_margin": [1, 1] + }, + // Sidebar row selected + { + "class": "tree_row", + "attributes": ["selected"], + "layer0.opacity": 1.0 + }, + // Sidebar heading + { + "class": "sidebar_heading", + "color": [210, 210, 210], + "font.bold": true, + "shadow_color": [0, 0, 0], + "shadow_offset": [0, -1] + }, + // Sidebar heading selected + { + "class": "sidebar_heading", + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "shadow_offset": [0, 1] + }, + // Sidebar entry + { + "class": "sidebar_label", + "color": [125, 125, 125], + "shadow_color": [0, 0, 0], + "shadow_offset": [0, -1], + "font.bold": false, + "font.italic": false + }, + // Sidebar folder entry + { + "class": "sidebar_label", + "parents": [{"class": "tree_row", "attributes": ["expandable"]}], + "color": [190, 190, 190] + }, + { + "class": "sidebar_label", + "parents": [{"class": "tree_row", "attributes": ["expandable", "hover"]}], + "color": [235, 235, 235] + }, + { + "class": "sidebar_label", + "parents": [{"class": "tree_row", "attributes": ["expandable"]}], + "settings": ["bold_folder_labels"], + "font.bold": true + }, + // Sidebar entry selected + { + "class": "sidebar_label", + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "color": [210, 210, 210], + "shadow_color": [0, 0, 0], + "shadow_offset": [0, 1] + }, + // Sidebar entry transient + { + "class": "sidebar_label", + "attributes": ["transient"], + "font.italic": true + }, + +// +// SIDEBAR - OPEN FILE ICONS +// + + // Sidebar file close + { + "class": "close_button", + "layer0.texture": "Theme - Soda/Soda Dark/file-close.png", + "layer0.opacity": 0.0, + "layer0.inner_margin": 0, + "content_margin": [8, 8] + }, + { + "class": "close_button", + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.opacity": 1.0 + }, + // Sidebar file dirty + { + "class": "close_button", + "attributes": ["dirty"], + "layer0.texture": "Theme - Soda/Soda Dark/file-dirty.png", + "layer0.opacity": 1.0 + }, + { + "class": "close_button", + "attributes": ["dirty"], + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/file-dirty-selected.png" + }, + { + "class": "close_button", + "attributes": ["dirty"], + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Dark/file-close.png" + }, + // Sidebar file close hover + { + "class": "close_button", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Dark/file-close-hover.png" + }, + { + "class": "close_button", + "parents": [{"class": "tree_row", "attributes": ["hover", "selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/file-close-selected.png" + }, + +// +// SIDEBAR - GENERAL FILE ICONS +// + + // Sidebar group closed + { + "class": "disclosure_button_control", + "content_margin": [8, 8], + "layer0.texture": "Theme - Soda/Soda Dark/group-closed.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": 0 + }, + { + "class": "disclosure_button_control", + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Dark/group-closed-hover.png" + }, + { + "class": "disclosure_button_control", + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/group-closed-selected.png" + }, + // Sidebar folder closed + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "layer0.texture": "Theme - Soda/Soda Dark/folder-closed.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Dark/folder-closed-hover.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/folder-closed-selected.png" + }, + // Sidebar group open + { + "class": "disclosure_button_control", + "attributes": ["expanded"], + "layer0.texture": "Theme - Soda/Soda Dark/group-open.png" + }, + { + "class": "disclosure_button_control", + "attributes": ["expanded"], + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Dark/group-open-hover.png" + }, + { + "class": "disclosure_button_control", + "attributes": ["expanded"], + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/group-open-selected.png" + }, + // Sidebar folder open + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "attributes": ["expanded"], + "layer0.texture": "Theme - Soda/Soda Dark/folder-open.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "attributes": ["expanded"], + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Dark/folder-open-hover.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "attributes": ["expanded"], + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/folder-open-selected.png" + }, + +// +// STANDARD TEXT BUTTONS +// + + // Default button state + { + "class": "button_control", + "content_margin": [6, 5, 6, 6], + "min_size": [75, 0], + "layer0.texture": "Theme - Soda/Soda Dark/btn-large.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": [6, 6] + }, + // Pressed button state + { + "class": "button_control", + "attributes": ["pressed"], + "layer0.texture": "Theme - Soda/Soda Dark/btn-large-on.png" + }, + +// +// TEXT INPUT FIELD +// + + // Text input field item + { + "class": "text_line_control", + "layer0.texture": "Theme - Soda/Soda Dark/text-field.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": [4, 5, 4, 3], + "content_margin": [3, 3, 6, 3] + }, + + // Text input dropdown + { + "class": "dropdown_button_control", + "content_margin": [8, 8], + "layer0.texture": "Theme - Soda/Soda Dark/text-field-list.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": [4, 4] + }, + { + "class": "dropdown_button_control", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Dark/text-field-list-hover.png" + }, + +// +// PANEL BACKGROUNDS +// + + // Bottom panel background + { + "class": "panel_control", + "layer0.texture": "Theme - Soda/Soda Dark/panel-background.png", + "layer0.inner_margin": [2, 2, 2, 2], + "layer0.opacity": 1.0, + "content_margin": [2, 3, 2, 1] + }, + // Quick panel background + { + "class": "overlay_control", + "settings": ["!soda_retina_fix"], + "layer0.texture": "Theme - Soda/Soda Dark/quick-panel-background.png", + "layer0.inner_margin": [12, 6, 12, 15], + "layer0.opacity": 1.0, + "layer1.texture": "Theme - Soda/Soda Dark/quick-panel-sections.png", + "layer1.inner_margin": [12, 40, 12, 19], + "layer1.opacity": 1.0, + "content_margin": [11, 8, 11, 17] + }, + // Quick panel background (Retina fix) + { + "class": "overlay_control", + "settings": ["soda_retina_fix"], + "layer0.tint": [67, 67, 67], + "layer0.opacity": 1.0, + "content_margin": [6, 8, 6, 6] + }, + +// +// QUICK PANEL +// + + { + "class": "quick_panel", + "row_padding": [5, 2], + "layer0.tint": [33, 33, 33], + "layer0.opacity": 1.0, + "dark_content": true + }, + { + "class": "quick_panel_row", + "layer0.texture": "Theme - Soda/Soda Dark/quick-panel-row.png", + "layer0.inner_margin": [2, 2, 2, 2], + "layer0.opacity": 1.0 + }, + { + "class": "quick_panel_row", + "attributes": ["selected"], + "layer0.texture": "Theme - Soda/Soda Dark/quick-panel-row-selected.png" + }, + { + "class": "quick_panel_label", + "fg": [210, 210, 210, 255], + "match_fg": [126, 199, 239, 255], + "selected_fg": [255, 255, 255, 255], + "selected_match_fg": [166, 229, 255, 255] + }, + { + "class": "quick_panel_path_label", + "fg": [130, 130, 130, 255], + "match_fg": [220, 220, 220, 255], + "selected_fg": [175, 175, 175, 255], + "selected_match_fg": [220, 220, 220, 255] + }, + { + "class": "quick_panel_score_label", + "fg": [126, 199, 239, 255], + "selected_fg": [166, 229, 255, 255] + }, + +// +// MINI QUICK PANEL +// + + { + "class": "mini_quick_panel_row", + "layer0.texture": "Theme - Soda/Soda Dark/quick-panel-row.png", + "layer0.inner_margin": [2, 2, 2, 2], + "layer0.opacity": 1.0 + }, + { + "class": "mini_quick_panel_row", + "attributes": ["selected"], + "layer0.texture": "Theme - Soda/Soda Dark/quick-panel-row-selected.png" + }, + +// +// CODE COMPLETION DROPDOWN +// + + { + "class": "popup_control", + "content_margin": [2, 2], + "layer0.tint": [30, 30, 30], + "layer0.opacity": 1.0 + }, + { + "class": "auto_complete", + "row_padding": [4, 2] + }, + { + "class": "auto_complete_label", + "fg": [140, 140, 140], + "match_fg": [220, 220, 220], + "selected_fg": [180, 180, 180], + "selected_match_fg": [245, 245, 245] + }, + { + "class": "table_row", + "layer0.texture": "Theme - Soda/Soda Dark/autocomplete-row-selected.png", + "layer0.opacity": 0.0, + "layer0.inner_margin": [3, 1] + }, + { + "class": "table_row", + "attributes": ["selected"], + "layer0.opacity": 1.0 + }, + +// +// BOTTOM PANEL BUTTONS +// + + // Button group middle + { + "class": "icon_button_control", + "layer0.texture": "Theme - Soda/Soda Dark/btn-group-middle.png", + "layer0.inner_margin": [6, 6], + "layer0.opacity": 1.0, + "content_margin": [3, 3] + }, + { + "class": "icon_button_control", + "attributes": ["selected"], + "layer0.texture": "Theme - Soda/Soda Dark/btn-group-middle-on.png" + }, + // Button group left + { + "class": "icon_button_control", + "attributes": ["left"], + "layer0.texture": "Theme - Soda/Soda Dark/btn-group-left.png", + "content_margin": [4, 3, 3, 3] + }, + { + "class": "icon_button_control", + "attributes": ["left", "selected"], + "layer0.texture": "Theme - Soda/Soda Dark/btn-group-left-on.png" + }, + // Button group right + { + "class": "icon_button_control", + "attributes": ["right"], + "layer0.texture": "Theme - Soda/Soda Dark/btn-group-right.png", + "content_margin": [3, 3, 4, 3] + }, + { + "class": "icon_button_control", + "attributes": ["right", "selected"], + "layer0.texture": "Theme - Soda/Soda Dark/btn-group-right-on.png" + }, + // Button single + { + "class": "icon_button_control", + "attributes": ["left", "right"], + "layer0.texture": "Theme - Soda/Soda Dark/btn-small.png", + "content_margin": [4, 3] + }, + { + "class": "icon_button_control", + "attributes": ["left", "right", "selected"], + "layer0.texture": "Theme - Soda/Soda Dark/btn-small-on.png" + }, + // Panel close button + { + "class": "panel_close_button", + "layer0.texture": "Theme - Soda/Soda Dark/panel-close.png", + "layer0.opacity": 0.85, + "content_margin": [8, 12] + }, + { + "class": "panel_close_button", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Dark/panel-close.png", + "layer0.opacity": 1.0 + }, + { + "class": "panel_close_button", + "attributes": ["pressed"], + "layer0.texture": "Theme - Soda/Soda Dark/panel-close-pressed.png" + }, + +// +// BOTTOM PANEL ICONS - GROUP 1 +// + + // Regex search button + { + "class": "icon_regex", + "layer0.texture": "Theme - Soda/Soda Dark/icon-regex-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_regex", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/icon-regex-on.png" + }, + // Case sensitive search button + { + "class": "icon_case", + "layer0.texture": "Theme - Soda/Soda Dark/icon-case-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_case", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/icon-case-on.png" + }, + // Match whole word search button + { + "class": "icon_whole_word", + "layer0.texture": "Theme - Soda/Soda Dark/icon-word-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_whole_word", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/icon-word-on.png" + }, + +// +// BOTTOM PANEL ICONS - GROUP 1 (EXTENDED: FIND IN FILES) +// + + // Show search context button + { + "class": "icon_context", + "layer0.texture": "Theme - Soda/Soda Dark/icon-context-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_context", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/icon-context-on.png" + }, + // Use search buffer + { + "class": "icon_use_buffer", + "layer0.texture": "Theme - Soda/Soda Dark/icon-buffer-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_use_buffer", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/icon-buffer-on.png" + }, + +// +// BOTTOM PANEL ICONS - GROUP 2 +// + + // Reverse search direction button + { + "class": "icon_reverse", + "layer0.texture": "Theme - Soda/Soda Dark/icon-reverse-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_reverse", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/icon-reverse-on.png" + }, + // Search wrap button + { + "class": "icon_wrap", + "layer0.texture": "Theme - Soda/Soda Dark/icon-wrap-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_wrap", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/icon-wrap-on.png" + }, + // Search in selection button + { + "class": "icon_in_selection", + "layer0.texture": "Theme - Soda/Soda Dark/icon-selection-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_in_selection", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/icon-selection-on.png" + }, + +// +// BOTTOM PANEL ICONS - GROUP 3 +// + + // Preserve case button + { + "class": "icon_preserve_case", + "layer0.texture": "Theme - Soda/Soda Dark/icon-preserve-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_preserve_case", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/icon-preserve-on.png" + }, + +// +// BOTTOM PANEL ICONS - GROUP 4 +// + + // Highlight results button + { + "class": "icon_highlight", + "layer0.texture": "Theme - Soda/Soda Dark/icon-highlight-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_highlight", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/icon-highlight-on.png" + } + +] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark.sublime-theme b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark.sublime-theme index 6c4a634..5b47e34 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark.sublime-theme +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark.sublime-theme @@ -488,6 +488,12 @@ "shadow_color": [0, 0, 0], "shadow_offset": [0, -1] }, + // Sidebar heading selected + { + "class": "sidebar_heading", + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "shadow_offset": [0, 1] + }, // Sidebar entry { "class": "sidebar_label", @@ -591,6 +597,24 @@ "parents": [{"class": "tree_row", "attributes": ["selected"]}], "layer0.texture": "Theme - Soda/Soda Dark/group-closed-selected.png" }, + // Sidebar folder closed + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "layer0.texture": "Theme - Soda/Soda Dark/folder-closed.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Dark/folder-closed-hover.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/folder-closed-selected.png" + }, // Sidebar group open { "class": "disclosure_button_control", @@ -609,6 +633,27 @@ "parents": [{"class": "tree_row", "attributes": ["selected"]}], "layer0.texture": "Theme - Soda/Soda Dark/group-open-selected.png" }, + // Sidebar folder open + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "attributes": ["expanded"], + "layer0.texture": "Theme - Soda/Soda Dark/folder-open.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "attributes": ["expanded"], + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Dark/folder-open-hover.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "attributes": ["expanded"], + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/folder-open-selected.png" + }, // // STANDARD TEXT BUTTONS diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/Widget - Soda Dark 3.sublime-settings b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/Widget - Soda Dark 3.sublime-settings new file mode 100644 index 0000000..d5787af --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/Widget - Soda Dark 3.sublime-settings @@ -0,0 +1,4 @@ +{ + "color_scheme": "Packages/Theme - Soda/Soda Dark/Widget - Soda Dark.stTheme", + "draw_shadows": false +} diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/Widget - Soda Dark.stTheme b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/Widget - Soda Dark.stTheme index 42cbb94..e106e4e 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/Widget - Soda Dark.stTheme +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/Widget - Soda Dark.stTheme @@ -21,8 +21,65 @@ #F8F8F8 invisibles #3B3B3B + lineHighlight + #333333 selection - #222222 + #666666 + selectionBorder + #66666600 + inactiveSelection + #4a4a4a + + + + name + Comment + scope + comment + settings + + fontStyle + italic + foreground + #505C63 + + + + name + Keyword + scope + keyword, storage + settings + + foreground + #A7DBD8 + + + + scope + constant + settings + + foreground + #E0E4CC + + + + scope + string + settings + + foreground + #E0E4CC + + + + scope + constant.character.escape + settings + + foreground + #e9903c diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-left-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-left-on.png index 3a25d0c..dd340bb 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-left-on.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-left-on.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-left-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-left-on@2x.png index d9ed060..b6b8a5a 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-left-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-left-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-left.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-left.png index 17bd507..67b3ccf 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-left.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-left.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-left@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-left@2x.png index 34efcdf..0e07796 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-left@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-left@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-middle-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-middle-on@2x.png index 268e9f5..f8f23c1 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-middle-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-middle-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-middle.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-middle.png index 046049c..dfca99b 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-middle.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-middle.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-middle@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-middle@2x.png index 2bd8146..0217ebd 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-middle@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-middle@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-right-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-right-on.png index 968db9b..12340b7 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-right-on.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-right-on.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-right-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-right-on@2x.png index 7c56ad0..57595b4 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-right-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-right-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-right.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-right.png index 60a7aa0..2897025 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-right.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-right.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-right@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-right@2x.png index 8ad4f40..43a6c14 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-right@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-right@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-large-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-large-on.png index b97be2d..a2e3ce0 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-large-on.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-large-on.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-large-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-large-on@2x.png index f4bddc9..b9e71bd 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-large-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-large-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-large.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-large.png index d4042c9..0c95e6e 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-large.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-large.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-large@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-large@2x.png index 046438a..3985eee 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-large@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-large@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-small-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-small-on.png index 92881b7..4763433 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-small-on.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-small-on.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-small-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-small-on@2x.png index 978b140..2b8fca2 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-small-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-small-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-small.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-small.png index d4042c9..0c95e6e 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-small.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-small.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-small@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-small@2x.png index 046438a..3985eee 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-small@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-small@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-active.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-active.png index ef782f9..5835373 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-active.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-active.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-active@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-active@2x.png index 17d475f..7b33a92 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-active@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-active@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-hover.png index c21168c..f62088a 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-hover.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-hover.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-hover@2x.png index 85d9832..af5e4fa 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-hover@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-hover@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-inactive.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-inactive.png index e078572..b228738 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-inactive.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-inactive.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-inactive@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-inactive@2x.png index b7ceb0c..f7debe5 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-inactive@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-inactive@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-close-selected.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-close-selected.png index 989c036..5e38529 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-close-selected.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-close-selected.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-close-selected@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-close-selected@2x.png index 90e2578..9852d56 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-close-selected@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-close-selected@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-close.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-close.png index f5b0ed6..0c342be 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-close.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-close.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-close@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-close@2x.png index 5181e70..f04cf86 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-close@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-close@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-dirty-selected.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-dirty-selected.png index 7513aef..f79a4a8 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-dirty-selected.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-dirty-selected.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-dirty-selected@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-dirty-selected@2x.png index 5914886..dddf043 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-dirty-selected@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-dirty-selected@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-closed-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-closed-hover.png index c2b1772..a2c3c03 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-closed-hover.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-closed-hover.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-closed-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-closed-hover@2x.png index ffc28bf..40539a3 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-closed-hover@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-closed-hover@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-closed.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-closed.png index 4b15636..9a22f7b 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-closed.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-closed.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-closed@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-closed@2x.png index f9092e7..32cbcf3 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-closed@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-closed@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-open-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-open-hover.png index cfcb481..55d88a8 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-open-hover.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-open-hover.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-open-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-open-hover@2x.png index b3da623..71cdf1d 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-open-hover@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-open-hover@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-open.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-open.png index 55c3a34..c695b22 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-open.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-open.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-open@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-open@2x.png index f7d4c89..113fc26 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-open@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-open@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed-hover.png new file mode 100644 index 0000000..8934679 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed-hover.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed-hover@2x.png new file mode 100644 index 0000000..a5e4694 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed-hover@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed-selected.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed-selected.png new file mode 100644 index 0000000..8089d03 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed-selected.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed-selected@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed-selected@2x.png new file mode 100644 index 0000000..13c2e24 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed-selected@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed.png new file mode 100644 index 0000000..4d2dfcf Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed@2x.png new file mode 100644 index 0000000..717d217 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open-hover.png new file mode 100644 index 0000000..702caa4 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open-hover.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open-hover@2x.png new file mode 100644 index 0000000..7a75ce3 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open-hover@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open-selected.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open-selected.png new file mode 100644 index 0000000..b8f9bd6 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open-selected.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open-selected@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open-selected@2x.png new file mode 100644 index 0000000..045460e Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open-selected@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open.png new file mode 100644 index 0000000..bf450fe Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open@2x.png new file mode 100644 index 0000000..27872ff Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed-hover.png index acc7e62..8daa831 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed-hover.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed-hover.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed-hover@2x.png index 0a20c3f..9cd434e 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed-hover@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed-hover@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed-selected.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed-selected.png index fbe4d30..6e9f19c 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed-selected.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed-selected.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed-selected@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed-selected@2x.png index 5e96a03..ed2726e 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed-selected@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed-selected@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed.png index a9b08c7..f85890a 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed@2x.png index 6fc2485..6d4e995 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open-hover.png index 996ece4..a58f50b 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open-hover.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open-hover.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open-hover@2x.png index 07234e5..d3298f4 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open-hover@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open-hover@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open-selected.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open-selected.png index 71446e4..70c63f4 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open-selected.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open-selected.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open-selected@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open-selected@2x.png index 0be0189..9d9c733 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open-selected@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open-selected@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open.png index 60e8735..96a2091 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open@2x.png index 0bdf757..57e5a99 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-buffer-off.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-buffer-off.png index c766f0b..c35b86e 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-buffer-off.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-buffer-off.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-buffer-off@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-buffer-off@2x.png index 3f08ebc..d43a110 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-buffer-off@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-buffer-off@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-buffer-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-buffer-on.png index 0ea1d87..c92b7d3 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-buffer-on.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-buffer-on.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-buffer-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-buffer-on@2x.png index 8fb36fb..b6e79fd 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-buffer-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-buffer-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-case-off.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-case-off.png index 98cfe0b..03e980d 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-case-off.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-case-off.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-case-off@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-case-off@2x.png index 1c27119..ee83912 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-case-off@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-case-off@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-case-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-case-on.png index 84813cd..70f33a5 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-case-on.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-case-on.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-case-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-case-on@2x.png index 05770df..6d4fcc0 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-case-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-case-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-context-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-context-on.png index 7447f94..4807bf1 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-context-on.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-context-on.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-context-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-context-on@2x.png index f04193b..06edcb4 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-context-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-context-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-highlight-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-highlight-on.png index 42cd598..7891125 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-highlight-on.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-highlight-on.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-highlight-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-highlight-on@2x.png index 28fde6b..b0e05cd 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-highlight-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-highlight-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-preserve-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-preserve-on.png index 1202888..632caa0 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-preserve-on.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-preserve-on.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-preserve-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-preserve-on@2x.png index 419010c..801023b 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-preserve-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-preserve-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-regex-off.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-regex-off.png index 1669cfc..ed11298 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-regex-off.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-regex-off.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-regex-off@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-regex-off@2x.png index ccac8ab..62a7ab7 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-regex-off@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-regex-off@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-regex-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-regex-on.png index 15a72cf..1feb2d6 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-regex-on.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-regex-on.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-regex-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-regex-on@2x.png index 4a500a3..5c2ee44 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-regex-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-regex-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-reverse-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-reverse-on.png index cadfdf7..5980b03 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-reverse-on.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-reverse-on.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-reverse-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-reverse-on@2x.png index b039826..c9d9738 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-reverse-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-reverse-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-selection-off@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-selection-off@2x.png index ec6f0fa..911c8b8 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-selection-off@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-selection-off@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-selection-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-selection-on.png index c71ae65..19aac38 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-selection-on.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-selection-on.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-selection-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-selection-on@2x.png index 303a33b..ae56d56 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-selection-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-selection-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-word-off.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-word-off.png index bdf69c0..3ac7ad7 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-word-off.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-word-off.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-word-off@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-word-off@2x.png index 160f44a..c643e1f 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-word-off@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-word-off@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-word-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-word-on.png index 3c880fd..91480ab 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-word-on.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-word-on.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-word-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-word-on@2x.png index 52f8b3b..e7c6f33 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-word-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-word-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-wrap-off.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-wrap-off.png index b9facd8..92a65d4 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-wrap-off.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-wrap-off.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-wrap-off@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-wrap-off@2x.png index d8d02dc..23a4c17 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-wrap-off@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-wrap-off@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-wrap-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-wrap-on.png index 5df3e46..a781406 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-wrap-on.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-wrap-on.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-wrap-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-wrap-on@2x.png index 447dccf..867d810 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-wrap-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-wrap-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/overlay-scrollbar-horizontal.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/overlay-scrollbar-horizontal.png index ff2ca6e..be64d27 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/overlay-scrollbar-horizontal.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/overlay-scrollbar-horizontal.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/overlay-scrollbar-horizontal@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/overlay-scrollbar-horizontal@2x.png index 2b5bb07..0bbd1cb 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/overlay-scrollbar-horizontal@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/overlay-scrollbar-horizontal@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/overlay-scrollbar-vertical.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/overlay-scrollbar-vertical.png index 5e981c3..70100d0 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/overlay-scrollbar-vertical.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/overlay-scrollbar-vertical.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/overlay-scrollbar-vertical@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/overlay-scrollbar-vertical@2x.png index 3d3ff15..e860a90 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/overlay-scrollbar-vertical@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/overlay-scrollbar-vertical@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/panel-close-pressed.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/panel-close-pressed.png new file mode 100644 index 0000000..3af0941 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/panel-close-pressed.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/panel-close-pressed@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/panel-close-pressed@2x.png new file mode 100644 index 0000000..19cc98e Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/panel-close-pressed@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/panel-close.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/panel-close.png new file mode 100644 index 0000000..9d53a68 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/panel-close.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/panel-close@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/panel-close@2x.png new file mode 100644 index 0000000..1f06ed6 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/panel-close@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/quick-panel-background-fix.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/quick-panel-background-fix.png index 816fe8e..ceb6c74 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/quick-panel-background-fix.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/quick-panel-background-fix.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/quick-panel-background-fix@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/quick-panel-background-fix@2x.png index d7b5ec0..e91a68c 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/quick-panel-background-fix@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/quick-panel-background-fix@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/quick-panel-background.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/quick-panel-background.png index eb38a68..398d444 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/quick-panel-background.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/quick-panel-background.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/quick-panel-background@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/quick-panel-background@2x.png index e6cd417..712178d 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/quick-panel-background@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/quick-panel-background@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/sidebar-row-selected.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/sidebar-row-selected.png index 53a5a67..e043cb1 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/sidebar-row-selected.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/sidebar-row-selected.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/sidebar-row-selected@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/sidebar-row-selected@2x.png index 4be24fd..959fe75 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/sidebar-row-selected@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/sidebar-row-selected@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-puck-horizontal.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-puck-horizontal.png index 2bf392d..318b08e 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-puck-horizontal.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-puck-horizontal.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-puck-horizontal@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-puck-horizontal@2x.png index 3f4de61..5dac081 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-puck-horizontal@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-puck-horizontal@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-corner.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-corner.png index 9fdedde..9148079 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-corner.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-corner.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-corner@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-corner@2x.png index 919474b..b9db402 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-corner@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-corner@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-horizontal.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-horizontal.png index a57c13f..80ea3df 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-horizontal.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-horizontal.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-horizontal@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-horizontal@2x.png index fc9f044..00c1a3b 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-horizontal@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-horizontal@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-vertical.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-vertical.png index 1c958be..dbf4dd0 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-vertical.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-vertical.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-vertical@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-vertical@2x.png index f560c3f..d604dac 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-vertical@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-vertical@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-close-inactive.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-close-inactive.png index 865956a..c45c727 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-close-inactive.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-close-inactive.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-close-inactive@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-close-inactive@2x.png index 0e71145..34ccdf5 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-close-inactive@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-close-inactive@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-close.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-close.png index 865956a..c45c727 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-close.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-close.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-close@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-close@2x.png index 0e71145..34ccdf5 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-close@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-close@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-inactive.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-inactive.png index 3ff4773..00ccf2d 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-inactive.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-inactive.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-inactive@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-inactive@2x.png index 92ba53e..c20d292 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-inactive@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-inactive@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-background.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-background.png index eaa87b5..04e7b5a 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-background.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-background.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-background@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-background@2x.png index cc6999d..c2bdeba 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-background@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-background@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-left-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-left-hover.png new file mode 100644 index 0000000..fd585a5 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-left-hover.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-left-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-left-hover@2x.png new file mode 100644 index 0000000..d335314 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-left-hover@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-left.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-left.png new file mode 100644 index 0000000..58e1d80 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-left.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-left@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-left@2x.png new file mode 100644 index 0000000..2f70fb6 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-left@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-list-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-list-hover.png new file mode 100644 index 0000000..16f0b8d Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-list-hover.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-list-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-list-hover@2x.png new file mode 100644 index 0000000..eff9636 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-list-hover@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-list.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-list.png new file mode 100644 index 0000000..67e28bd Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-list.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-list@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-list@2x.png new file mode 100644 index 0000000..c11f5bc Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-list@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-right-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-right-hover.png new file mode 100644 index 0000000..a9fa486 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-right-hover.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-right-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-right-hover@2x.png new file mode 100644 index 0000000..cc34076 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-right-hover@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-right.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-right.png new file mode 100644 index 0000000..16f47a1 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-right.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-right@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-right@2x.png new file mode 100644 index 0000000..9c25eb2 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-right@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/text-field-list-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/text-field-list-hover.png new file mode 100644 index 0000000..de44bc9 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/text-field-list-hover.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/text-field-list-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/text-field-list-hover@2x.png new file mode 100644 index 0000000..9825e0a Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/text-field-list-hover@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/text-field-list.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/text-field-list.png new file mode 100644 index 0000000..03762fd Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/text-field-list.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/text-field-list@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/text-field-list@2x.png new file mode 100644 index 0000000..f16b2c0 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/text-field-list@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/text-field.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/text-field.png index f2f3ea3..c85556e 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/text-field.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/text-field.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/text-field@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/text-field@2x.png index 7dbb058..cc19f14 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/text-field@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/text-field@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light 3.sublime-theme b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light 3.sublime-theme new file mode 100644 index 0000000..3fe7ff0 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light 3.sublime-theme @@ -0,0 +1,1160 @@ +[ + +// +// TABS (REGULAR) +// + + // Tab set + { + "class": "tabset_control", + "layer0.texture": "Theme - Soda/Soda Light/tabset-background.png", + "layer0.inner_margin": [1, 7], + "layer0.opacity": 1.0, + "content_margin": [-4, 0, -4, 3], + "tab_overlap": 5, + "tab_width": 180, + "tab_min_width": 45, + "tab_height": 25, + "mouse_wheel_switch": false + }, + { + "class": "tabset_control", + "settings": ["enable_tab_scrolling"], + "content_margin": [3, 0, 3, 3] + }, + { + "class": "tabset_control", + "settings": ["mouse_wheel_switches_tabs"], + "mouse_wheel_switch": true + }, + // Tab element + { + "class": "tab_control", + "content_margin": [12, 3, 12, 3], + "max_margin_trim": 0, + "hit_test_level": 0.0, + "layer0.texture": "Theme - Soda/Soda Light/tab-inactive.png", + "layer0.inner_margin": [5, 5], + "layer0.opacity": 1.0 + }, + // Tab close state + { + "class": "tab_control", + "settings": ["show_tab_close_buttons"], + "content_margin": [12, 3, 7, 3] + }, + // Tab hover state + { + "class": "tab_control", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Light/tab-hover.png" + }, + // Tab active state + { + "class": "tab_control", + "attributes": ["selected"], + "layer0.texture": "Theme - Soda/Soda Light/tab-active.png" + }, + // Tab dirty state (close button hidden) + { + "class": "tab_control", + "settings": ["!show_tab_close_buttons"], + "attributes": ["dirty"], + "content_margin": [12, 3, 7, 3] + }, + +// +// TABS (CLASSIC) +// + + // Tab set + { + "class": "tabset_control", + "settings": ["soda_classic_tabs"], + "content_margin": [3, 4, 3, 3], + "tab_overlap": 24, + "tab_height": 28 + }, + { + "class": "tabset_control", + "settings": ["soda_classic_tabs", "enable_tab_scrolling"], + "content_margin": [6, 4, 6, 3] + }, + // Tab element + { + "class": "tab_control", + "settings": ["soda_classic_tabs"], + "content_margin": [22, 6, 22, 4], + "hit_test_level": 0.5, + "layer0.texture": "Theme - Soda/Soda Light/classic/tab-inactive.png", + "layer0.inner_margin": [18, 4] + }, + // Tab close state + { + "class": "tab_control", + "settings": ["soda_classic_tabs", "show_tab_close_buttons"], + "content_margin": [22, 6, 15, 4] + }, + // Tab hover state + { + "class": "tab_control", + "settings": ["soda_classic_tabs"], + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Light/classic/tab-hover.png" + }, + // Tab active state + { + "class": "tab_control", + "settings": ["soda_classic_tabs"], + "attributes": ["selected"], + "layer0.texture": "Theme - Soda/Soda Light/classic/tab-active.png" + }, + // Tab dirty state (close button hidden) + { + "class": "tab_control", + "settings": ["soda_classic_tabs", "!show_tab_close_buttons"], + "attributes": ["dirty"], + "content_margin": [22, 6, 15, 4] + }, + +// +// TAB BUTTONS +// + + // Tab close button + { + "class": "tab_close_button", + "content_margin": [0, 0], + "layer0.texture": "Theme - Soda/Soda Light/tab-close-inactive.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": 0 + }, + { + "class": "tab_close_button", + "settings": ["show_tab_close_buttons"], + "content_margin": [8, 8] + }, + { + "class": "tab_close_button", + "parents": [{"class": "tab_control", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Light/tab-close.png", + "layer0.opacity": 0.85 + }, + { + "class": "tab_close_button", + "parents": [{"class": "tab_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/tab-close.png", + "layer0.opacity": 1.0 + }, + // Tab dirty button + { + "class": "tab_close_button", + "parents": [{"class": "tab_control", "attributes": ["dirty"]}], + "layer0.texture": "Theme - Soda/Soda Light/tab-dirty-inactive.png" + }, + { + "class": "tab_close_button", + "settings": ["!show_tab_close_buttons"], + "parents": [{"class": "tab_control", "attributes": ["dirty"]}], + "content_margin": [8, 8] + }, + { + "class": "tab_close_button", + "parents": [{"class": "tab_control", "attributes": ["dirty", "hover"]}], + "layer0.opacity": 1.0 + }, + { + "class": "tab_close_button", + "parents": [{"class": "tab_control", "attributes": ["dirty", "selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/tab-dirty.png" + }, + // Tab highlight button + { + "class": "tab_close_button", + "settings": ["highlight_modified_tabs"], + "parents": [{"class": "tab_control", "attributes": ["dirty"]}], + "layer0.texture": "Theme - Soda/Soda Light/tab-highlight-inactive.png" + }, + { + "class": "tab_close_button", + "settings": ["highlight_modified_tabs"], + "parents": [{"class": "tab_control", "attributes": ["dirty", "selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/tab-highlight.png" + }, + // Tab close button hover + { + "class": "tab_close_button", + "settings": ["show_tab_close_buttons"], + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Light/tab-close-hover.png" + }, + // Tab close button pressed + { + "class": "tab_close_button", + "settings": ["show_tab_close_buttons"], + "attributes": ["pressed"], + "layer0.texture": "Theme - Soda/Soda Light/tab-close-pressed.png" + }, + +// +// TAB LABELS +// + + { + "class": "tab_label", + "fade": true, + "fg": [50, 50, 50], + "shadow_color": [235, 235, 235], + "shadow_offset": [0, 1], + "font.italic": false + }, + { + "class": "tab_label", + "parents": [{"class": "tab_control", "attributes": ["hover"]}], + "fg": [25, 25, 25], + "shadow_color": [245, 245, 245] + }, + { + "class": "tab_label", + "parents": [{"class": "tab_control", "attributes": ["selected"]}], + "fg": [0, 0, 0], + "shadow_color": [255, 255, 255] + }, + { + "class": "tab_label", + "attributes": ["transient"], + "font.italic": true + }, + +// +// TAB SCROLLING +// + + // Tab dropdown + { + "class": "show_tabs_dropdown_button", + "content_margin": [9, 7, 8, 6], + "layer0.texture": "Theme - Soda/Soda Light/tabset-list.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": 0 + }, + { + "class": "show_tabs_dropdown_button", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Light/tabset-list-hover.png" + }, + // Tab scroll left + { + "class": "scroll_tabs_left_button", + "content_margin": [9, 7, 8, 6], + "layer0.texture": "Theme - Soda/Soda Light/tabset-left.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": 0 + }, + { + "class": "scroll_tabs_left_button", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Light/tabset-left-hover.png" + }, + // Tab scroll right + { + "class": "scroll_tabs_right_button", + "content_margin": [9, 7, 8, 6], + "layer0.texture": "Theme - Soda/Soda Light/tabset-right.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": 0 + }, + { + "class": "scroll_tabs_right_button", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Light/tabset-right-hover.png" + }, + +// +// FOLD BUTTONS +// + + { + "class": "fold_button_control", + "layer0.texture": "Theme - Soda/Soda Light/fold-closed.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": 0, + "content_margin": [8, 8] + }, + { + "class": "fold_button_control", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Light/fold-closed-hover.png" + }, + { + "class": "fold_button_control", + "attributes": ["expanded"], + "layer0.texture": "Theme - Soda/Soda Light/fold-open.png" + }, + { + "class": "fold_button_control", + "attributes": ["expanded", "hover"], + "layer0.texture": "Theme - Soda/Soda Light/fold-open-hover.png" + }, + +// +// STANDARD SCROLLBARS +// + + // Standard vertical scroll bar + { + "class": "scroll_bar_control", + "layer0.texture": "Theme - Soda/Soda Light/standard-scrollbar-vertical.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": [2, 6], + "blur": false + }, + // Standard horizontal scroll bar + { + "class": "scroll_bar_control", + "attributes": ["horizontal"], + "layer0.texture": "Theme - Soda/Soda Light/standard-scrollbar-horizontal.png", + "layer0.inner_margin": [6, 2], + "blur": false + }, + // Standard scroll bar corner + { + "class": "scroll_corner_control", + "layer0.texture": "Theme - Soda/Soda Light/standard-scrollbar-corner.png", + "layer0.inner_margin": [2, 2], + "layer0.opacity": 1.0 + }, + // Standard vertical scroll puck + { + "class": "puck_control", + "layer0.texture": "Theme - Soda/Soda Light/standard-puck-vertical.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": [0, 10], + "content_margin": [8, 12], + "blur": false + }, + // Standard horizontal scroll puck + { + "class": "puck_control", + "attributes": ["horizontal"], + "layer0.texture": "Theme - Soda/Soda Light/standard-puck-horizontal.png", + "layer0.inner_margin": [10, 0], + "content_margin": [12, 8], + "blur": false + }, + +// +// OVERLAY SCROLLBARS +// + + // Overlay toggle scroll bar + { + "class": "scroll_area_control", + "settings": ["overlay_scroll_bars"], + "overlay": true + }, + { + "class": "scroll_area_control", + "settings": ["!overlay_scroll_bars"], + "overlay": false + }, + // Overlay vertical scroll bar + { + "class": "scroll_bar_control", + "settings": ["overlay_scroll_bars"], + "layer0.texture": "Theme - Soda/Soda Light/overlay-scrollbar-vertical.png", + "layer0.inner_margin": [0, 5], + "blur": true + }, + // Overlay horizontal scroll bar + { + "class": "scroll_bar_control", + "settings": ["overlay_scroll_bars"], + "attributes": ["horizontal"], + "layer0.texture": "Theme - Soda/Soda Light/overlay-scrollbar-horizontal.png", + "layer0.inner_margin": [5, 0], + "blur": true + }, + // Overlay vertical puck + { + "class": "puck_control", + "settings": ["overlay_scroll_bars"], + "layer0.texture": "Theme - Soda/Soda Light/overlay-puck-vertical.png", + "layer0.inner_margin": [0, 5], + "content_margin": [5, 20], + "blur": true + }, + // Overlay horizontal puck + { + "class": "puck_control", + "settings": ["overlay_scroll_bars"], + "attributes": ["horizontal"], + "layer0.texture": "Theme - Soda/Soda Light/overlay-puck-horizontal.png", + "layer0.inner_margin": [5, 0], + "content_margin": [20, 5], + "blur": true + }, + // Overlay light puck (for dark content) + { + "class": "puck_control", + "settings": ["overlay_scroll_bars"], + "attributes": ["dark"], + "layer0.texture": "Theme - Soda/Soda Light/overlay-dark-puck-vertical.png" + }, + // Overlay light horizontal puck (for dark content) + { + "class": "puck_control", + "settings": ["overlay_scroll_bars"], + "attributes": ["horizontal", "dark"], + "layer0.texture": "Theme - Soda/Soda Light/overlay-dark-puck-horizontal.png" + }, + +// +// EMPTY WINDOW BACKGROUND +// + + { + "class": "sheet_container_control", + "layer0.tint": [255, 255, 255], + "layer0.opacity": 1.0 + }, + +// +// GRID LAYOUT +// + + { + "class": "grid_layout_control", + "border_size": 1, + "border_color": [189, 189, 189] + }, + +// +// MINI MAP +// + + { + "class": "minimap_control", + "settings": ["always_show_minimap_viewport"], + "viewport_color": [0, 0, 0, 35], + "viewport_opacity": 1.0 + }, + { + "class": "minimap_control", + "settings": ["!always_show_minimap_viewport"], + "viewport_color": [0, 0, 0, 35], + "viewport_opacity": { "target": 0.0, "speed": 10.0, "interpolation": "smoothstep" } + }, + { + "class": "minimap_control", + "attributes": ["hover"], + "settings": ["!always_show_minimap_viewport"], + "viewport_opacity": 1.0 + }, + +// +// DIALOG +// + + { + "class": "dialog", + "layer0.tint": [238, 238, 238], + "layer0.opacity": 1.0 + }, + +// +// PROGRESS BAR +// + + { + "class": "progress_bar_control", + "layer0.tint": [193, 193, 193], + "layer0.opacity": 1.0 + }, + { + "class": "progress_gauge_control", + "layer0.tint": [70, 160, 225], + "layer0.opacity": 1.0, + "content_margin": [0, 6] + }, + +// +// LABELS +// + + // General labels + { + "class": "label_control", + "color": [0, 0, 0] + }, + // Text field labels + { + "class": "label_control", + "parents": [{"class": "panel_control"}], + "shadow_color": [250, 250, 250], + "shadow_offset": [0, 1] + }, + // Button labels + { + "class": "label_control", + "parents": [{"class": "button_control"}], + "shadow_color": [245, 245, 245], + "shadow_offset": [0, 1] + }, + +// +// TOOLTIP +// + + // Tooltip container + { + "class": "tool_tip_control", + "layer0.texture": "Theme - Soda/Soda Light/tooltip.png", + "layer0.inner_margin": [1, 1], + "layer0.opacity": 0.95, + "content_margin": [3, 3] + }, + // Tooltip content + { + "class": "tool_tip_label_control", + "color": [0, 0, 0] + }, + +// +// STATUS BAR +// + + // Status bar container + { + "class": "status_bar", + "layer0.texture": "Theme - Soda/Soda Light/status-bar-background.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": [2, 2], + "content_margin": [8, 4, 8, 4] + }, + // Status bar button + { + "class": "status_button", + "min_size": [100, 0] + }, + // Status bar label + { + "class": "label_control", + "parents": [{"class": "status_bar"}], + "color": [47, 47, 47], + "shadow_color": [220, 220, 220], + "shadow_offset": [0, 1] + }, + +// +// SIDEBAR +// + + // Sidebar container + { + "class": "sidebar_container", + "layer0.texture": "Theme - Soda/Soda Light/sidebar-bg.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": [1, 1, 2, 1], + "content_margin": [0, 0, 1, 0] + }, + // Sidebar tree + { + "class": "sidebar_tree", + "row_padding": [8, 3], + "indent": 15, + "indent_offset": 15, + "indent_top_level": false + }, + // Sidebar rows + { + "class": "tree_row", + "layer0.texture": "Theme - Soda/Soda Light/sidebar-row-selected.png", + "layer0.opacity": 0.0, + "layer0.inner_margin": [1, 1] + }, + // Sidebar row selected + { + "class": "tree_row", + "attributes": ["selected"], + "layer0.opacity": 1.0 + }, + // Sidebar heading + { + "class": "sidebar_heading", + "color": [110, 126, 141], + "font.bold": true, + "shadow_color": [249, 250, 252], + "shadow_offset": [0, 1] + }, + // Sidebar heading selected + { + "class": "sidebar_heading", + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "color": [255, 255, 255], + "shadow_color": [34, 94, 145] + }, + // Sidebar entry + { + "class": "sidebar_label", + "color": [0, 0, 0], + "shadow_color": [241, 244, 247], + "shadow_offset": [0, 1], + "font.bold": false, + "font.italic": false + }, + // Sidebar folder entry + { + "class": "sidebar_label", + "parents": [{"class": "tree_row", "attributes": ["expandable"]}], + "color": [70, 86, 102] + }, + { + "class": "sidebar_label", + "parents": [{"class": "tree_row", "attributes": ["expandable", "hover"]}], + "color": [45, 56, 68] + }, + { + "class": "sidebar_label", + "parents": [{"class": "tree_row", "attributes": ["expandable"]}], + "settings": ["bold_folder_labels"], + "color": [110, 126, 141], + "font.bold": true + }, + { + "class": "sidebar_label", + "parents": [{"class": "tree_row", "attributes": ["expandable", "hover"]}], + "settings": ["bold_folder_labels"], + "color": [81, 92, 103] + }, + // Sidebar entry selected + { + "class": "sidebar_label", + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "color": [255, 255, 255], + "shadow_color": [34, 94, 145], + "shadow_offset": [0, 1] + }, + // Sidebar entry transient + { + "class": "sidebar_label", + "attributes": ["transient"], + "font.italic": true + }, + +// +// SIDEBAR - OPEN FILE ICONS +// + + // Sidebar file close + { + "class": "close_button", + "layer0.texture": "Theme - Soda/Soda Light/file-close.png", + "layer0.opacity": 0.0, + "layer0.inner_margin": 0, + "content_margin": [8, 8] + }, + { + "class": "close_button", + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.opacity": 1.0 + }, + // Sidebar file dirty + { + "class": "close_button", + "attributes": ["dirty"], + "layer0.texture": "Theme - Soda/Soda Light/file-dirty.png", + "layer0.opacity": 1.0 + }, + { + "class": "close_button", + "attributes": ["dirty"], + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/file-dirty-selected.png" + }, + { + "class": "close_button", + "attributes": ["dirty"], + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Light/file-close.png" + }, + // Sidebar file close hover + { + "class": "close_button", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Light/file-close-hover.png" + }, + { + "class": "close_button", + "parents": [{"class": "tree_row", "attributes": ["hover", "selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/file-close-selected.png" + }, + +// +// SIDEBAR - GENERAL FILE ICONS +// + + // Sidebar group closed + { + "class": "disclosure_button_control", + "content_margin": [8, 8], + "layer0.texture": "Theme - Soda/Soda Light/group-closed.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": 0 + }, + { + "class": "disclosure_button_control", + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Light/group-closed-hover.png" + }, + { + "class": "disclosure_button_control", + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/group-closed-selected.png" + }, + // Sidebar folder closed + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "layer0.texture": "Theme - Soda/Soda Light/folder-closed.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Light/folder-closed-hover.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/folder-closed-selected.png" + }, + // Sidebar group open + { + "class": "disclosure_button_control", + "attributes": ["expanded"], + "layer0.texture": "Theme - Soda/Soda Light/group-open.png" + }, + { + "class": "disclosure_button_control", + "attributes": ["expanded"], + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Light/group-open-hover.png" + }, + { + "class": "disclosure_button_control", + "attributes": ["expanded"], + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/group-open-selected.png" + }, + // Sidebar folder open + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "attributes": ["expanded"], + "layer0.texture": "Theme - Soda/Soda Light/folder-open.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "attributes": ["expanded"], + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Light/folder-open-hover.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "attributes": ["expanded"], + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/folder-open-selected.png" + }, + +// +// STANDARD TEXT BUTTONS +// + + // Default button state + { + "class": "button_control", + "content_margin": [6, 5, 6, 6], + "min_size": [75, 0], + "layer0.texture": "Theme - Soda/Soda Light/btn-large.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": [6, 6] + }, + // Pressed button state + { + "class": "button_control", + "attributes": ["pressed"], + "layer0.texture": "Theme - Soda/Soda Light/btn-large-on.png" + }, + +// +// TEXT INPUT FIELD +// + + // Text input field item + { + "class": "text_line_control", + "layer0.texture": "Theme - Soda/Soda Light/text-field.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": [4, 5, 4, 3], + "content_margin": [3, 3, 6, 3] + }, + + // Text input dropdown + { + "class": "dropdown_button_control", + "content_margin": [8, 8], + "layer0.texture": "Theme - Soda/Soda Light/text-field-list.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": [4, 4] + }, + { + "class": "dropdown_button_control", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Light/text-field-list-hover.png" + }, + +// +// PANEL BACKGROUNDS +// + + // Bottom panel background + { + "class": "panel_control", + "layer0.texture": "Theme - Soda/Soda Light/panel-background.png", + "layer0.inner_margin": [2, 2, 2, 2], + "layer0.opacity": 1.0, + "content_margin": [2, 3, 2, 1] + }, + // Quick panel background + { + "class": "overlay_control", + "settings": ["!soda_retina_fix"], + "layer0.texture": "Theme - Soda/Soda Light/quick-panel-background.png", + "layer0.inner_margin": [12, 6, 12, 15], + "layer0.opacity": 1.0, + "layer1.texture": "Theme - Soda/Soda Light/quick-panel-sections.png", + "layer1.inner_margin": [12, 40, 12, 19], + "layer1.opacity": 1.0, + "content_margin": [11, 8, 11, 17] + }, + // Quick panel background (Retina fix) + { + "class": "overlay_control", + "settings": ["soda_retina_fix"], + "layer0.tint": [238, 238, 238], + "layer0.opacity": 1.0, + "content_margin": [6, 8, 6, 6] + }, + +// +// QUICK PANEL +// + + { + "class": "quick_panel", + "row_padding": [5, 2], + "layer0.tint": [252, 252, 252], + "layer0.opacity": 1.0 + }, + { + "class": "quick_panel_row", + "layer0.texture": "Theme - Soda/Soda Light/quick-panel-row.png", + "layer0.inner_margin": [2, 2, 2, 2], + "layer0.opacity": 1.0 + }, + { + "class": "quick_panel_row", + "attributes": ["selected"], + "layer0.texture": "Theme - Soda/Soda Light/quick-panel-row-selected.png" + }, + { + "class": "quick_panel_label", + "fg": [50, 50, 50, 255], + "match_fg": [0, 0, 0, 255], + "selected_fg": [25, 25, 25, 255], + "selected_match_fg": [0, 0, 0, 255] + }, + { + "class": "quick_panel_path_label", + "fg": [150, 150, 150, 255], + "match_fg": [90, 90, 90, 255], + "selected_fg": [120, 120, 120, 255], + "selected_match_fg": [90, 90, 90, 255] + }, + { + "class": "quick_panel_score_label", + "fg": [72, 139, 211, 255], + "selected_fg": [72, 139, 211, 255] + }, + +// +// MINI QUICK PANEL +// + + { + "class": "mini_quick_panel_row", + "layer0.texture": "Theme - Soda/Soda Light/quick-panel-row.png", + "layer0.inner_margin": [2, 2, 2, 2], + "layer0.opacity": 1.0 + }, + { + "class": "mini_quick_panel_row", + "attributes": ["selected"], + "layer0.texture": "Theme - Soda/Soda Light/quick-panel-row-selected.png" + }, + +// +// CODE COMPLETION DROPDOWN +// + + { + "class": "popup_control", + "content_margin": [2, 2], + "layer0.tint": [255, 255, 255], + "layer0.opacity": 1.0 + }, + { + "class": "auto_complete", + "row_padding": [4, 2] + }, + { + "class": "auto_complete_label", + "fg": [51, 51, 51], + "match_fg": [0, 0, 0], + "selected_fg": [205, 226, 243], + "selected_match_fg": [255, 255, 255] + }, + { + "class": "table_row", + "layer0.texture": "Theme - Soda/Soda Light/autocomplete-row-selected.png", + "layer0.opacity": 0.0, + "layer0.inner_margin": [3, 1] + }, + { + "class": "table_row", + "attributes": ["selected"], + "layer0.opacity": 1.0 + }, + +// +// BOTTOM PANEL BUTTONS +// + + // Button group middle + { + "class": "icon_button_control", + "layer0.texture": "Theme - Soda/Soda Light/btn-group-middle.png", + "layer0.inner_margin": [6, 6], + "layer0.opacity": 1.0, + "content_margin": [3, 3] + }, + { + "class": "icon_button_control", + "attributes": ["selected"], + "layer0.texture": "Theme - Soda/Soda Light/btn-group-middle-on.png" + }, + // Button group left + { + "class": "icon_button_control", + "attributes": ["left"], + "layer0.texture": "Theme - Soda/Soda Light/btn-group-left.png", + "content_margin": [4, 3, 3, 3] + }, + { + "class": "icon_button_control", + "attributes": ["left", "selected"], + "layer0.texture": "Theme - Soda/Soda Light/btn-group-left-on.png" + }, + // Button group right + { + "class": "icon_button_control", + "attributes": ["right"], + "layer0.texture": "Theme - Soda/Soda Light/btn-group-right.png", + "content_margin": [3, 3, 4, 3] + }, + { + "class": "icon_button_control", + "attributes": ["right", "selected"], + "layer0.texture": "Theme - Soda/Soda Light/btn-group-right-on.png" + }, + // Button single + { + "class": "icon_button_control", + "attributes": ["left", "right"], + "layer0.texture": "Theme - Soda/Soda Light/btn-small.png", + "content_margin": [4, 3] + }, + { + "class": "icon_button_control", + "attributes": ["left", "right", "selected"], + "layer0.texture": "Theme - Soda/Soda Light/btn-small-on.png" + }, + // Panel close button + { + "class": "panel_close_button", + "layer0.texture": "Theme - Soda/Soda Light/panel-close.png", + "layer0.opacity": 0.85, + "content_margin": [8, 12] + }, + { + "class": "panel_close_button", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Light/panel-close.png", + "layer0.opacity": 1.0 + }, + { + "class": "panel_close_button", + "attributes": ["pressed"], + "layer0.texture": "Theme - Soda/Soda Light/panel-close-pressed.png" + }, + +// +// BOTTOM PANEL ICONS - GROUP 1 +// + + // Regex search button + { + "class": "icon_regex", + "layer0.texture": "Theme - Soda/Soda Light/icon-regex-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_regex", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/icon-regex-on.png" + }, + // Case sensitive search button + { + "class": "icon_case", + "layer0.texture": "Theme - Soda/Soda Light/icon-case-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_case", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/icon-case-on.png" + }, + // Match whole word search button + { + "class": "icon_whole_word", + "layer0.texture": "Theme - Soda/Soda Light/icon-word-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_whole_word", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/icon-word-on.png" + }, + +// +// BOTTOM PANEL ICONS - GROUP 1 (EXTENDED: FIND IN FILES) +// + + // Show search context button + { + "class": "icon_context", + "layer0.texture": "Theme - Soda/Soda Light/icon-context-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_context", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/icon-context-on.png" + }, + // Use search buffer + { + "class": "icon_use_buffer", + "layer0.texture": "Theme - Soda/Soda Light/icon-buffer-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_use_buffer", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/icon-buffer-on.png" + }, + +// +// BOTTOM PANEL ICONS - GROUP 2 +// + + // Reverse search direction button + { + "class": "icon_reverse", + "layer0.texture": "Theme - Soda/Soda Light/icon-reverse-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_reverse", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/icon-reverse-on.png" + }, + // Search wrap button + { + "class": "icon_wrap", + "layer0.texture": "Theme - Soda/Soda Light/icon-wrap-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_wrap", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/icon-wrap-on.png" + }, + // Search in selection button + { + "class": "icon_in_selection", + "layer0.texture": "Theme - Soda/Soda Light/icon-selection-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_in_selection", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/icon-selection-on.png" + }, + +// +// BOTTOM PANEL ICONS - GROUP 3 +// + + // Preserve case button + { + "class": "icon_preserve_case", + "layer0.texture": "Theme - Soda/Soda Light/icon-preserve-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_preserve_case", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/icon-preserve-on.png" + }, + +// +// BOTTOM PANEL ICONS - GROUP 4 +// + + // Highlight results button + { + "class": "icon_highlight", + "layer0.texture": "Theme - Soda/Soda Light/icon-highlight-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_highlight", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/icon-highlight-on.png" + } + +] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light.sublime-theme b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light.sublime-theme index 5c1e2ee..936a573 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light.sublime-theme +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light.sublime-theme @@ -487,6 +487,13 @@ "shadow_color": [249, 250, 252], "shadow_offset": [0, 1] }, + // Sidebar heading selected + { + "class": "sidebar_heading", + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "color": [255, 255, 255], + "shadow_color": [34, 94, 145] + }, // Sidebar entry { "class": "sidebar_label", @@ -597,6 +604,24 @@ "parents": [{"class": "tree_row", "attributes": ["selected"]}], "layer0.texture": "Theme - Soda/Soda Light/group-closed-selected.png" }, + // Sidebar folder closed + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "layer0.texture": "Theme - Soda/Soda Light/folder-closed.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Light/folder-closed-hover.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/folder-closed-selected.png" + }, // Sidebar group open { "class": "disclosure_button_control", @@ -615,6 +640,27 @@ "parents": [{"class": "tree_row", "attributes": ["selected"]}], "layer0.texture": "Theme - Soda/Soda Light/group-open-selected.png" }, + // Sidebar folder open + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "attributes": ["expanded"], + "layer0.texture": "Theme - Soda/Soda Light/folder-open.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "attributes": ["expanded"], + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Light/folder-open-hover.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "attributes": ["expanded"], + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/folder-open-selected.png" + }, // // STANDARD TEXT BUTTONS diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/Widget - Soda Light 3.sublime-settings b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/Widget - Soda Light 3.sublime-settings new file mode 100644 index 0000000..5cf1379 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/Widget - Soda Light 3.sublime-settings @@ -0,0 +1,4 @@ +{ + "color_scheme": "Packages/Theme - Soda/Soda Light/Widget - Soda Light.stTheme", + "draw_shadows": false +} diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/Widget - Soda Light.stTheme b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/Widget - Soda Light.stTheme index 3c84a2f..7b59a19 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/Widget - Soda Light.stTheme +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/Widget - Soda Light.stTheme @@ -21,8 +21,65 @@ #000000 invisibles #E0E0E0 + lineHighlight + #2D89D825 selection #b5d5ff + selectionBorder + #b5d5ff00 + inactiveSelection + #EDEDED + + + + name + Comment + scope + comment + settings + + fontStyle + italic + foreground + #0066FF + + + + name + Keyword + scope + keyword, storage + settings + + foreground + #4271AE + + + + scope + constant + settings + + foreground + #2C473E + + + + scope + string + settings + + foreground + #1D577D + + + + scope + constant.character.escape + settings + + foreground + #e47b18 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-left-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-left-on.png index 9b09ad7..c39d6c2 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-left-on.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-left-on.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-left-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-left-on@2x.png index 942fb8c..2f1e8e9 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-left-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-left-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-left.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-left.png index 62eab2f..ca80559 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-left.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-left.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-left@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-left@2x.png index ec5f09a..0f9071a 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-left@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-left@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-middle.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-middle.png index 016d334..6118c7a 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-middle.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-middle.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-middle@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-middle@2x.png index fa640c4..80a20c3 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-middle@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-middle@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-right-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-right-on@2x.png index 4a20169..8d6e7b0 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-right-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-right-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-right.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-right.png index 6662bb5..4e0a8c2 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-right.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-right.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-right@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-right@2x.png index 5bdbc40..c9e67ac 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-right@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-right@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-large-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-large-on.png index 1bc3770..527647e 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-large-on.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-large-on.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-large-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-large-on@2x.png index 921950f..a4e4d38 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-large-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-large-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-large.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-large.png index acef524..46bf8c1 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-large.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-large.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-large@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-large@2x.png index a3ff24b..20cade4 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-large@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-large@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-small-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-small-on.png index 395332e..27d0ff5 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-small-on.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-small-on.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-small-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-small-on@2x.png index 0896a6c..eb0cef5 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-small-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-small-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-small.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-small.png index acef524..46bf8c1 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-small.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-small.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-small@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-small@2x.png index a3ff24b..20cade4 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-small@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-small@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-active.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-active.png index dfa1238..7b897ba 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-active.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-active.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-active@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-active@2x.png index 7f01778..27f4935 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-active@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-active@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-hover.png index 9c80775..1a3a028 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-hover.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-hover.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-hover@2x.png index 82e931f..4d1a154 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-hover@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-hover@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-inactive.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-inactive.png index c24ff88..875d028 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-inactive.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-inactive.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-inactive@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-inactive@2x.png index 5e57ee8..d2885b6 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-inactive@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-inactive@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-close-selected.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-close-selected.png index 12032b2..cace18c 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-close-selected.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-close-selected.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-close-selected@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-close-selected@2x.png index 672ed1f..63dcb2a 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-close-selected@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-close-selected@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-close.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-close.png index 2ad7f5f..208cd82 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-close.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-close.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-close@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-close@2x.png index 357679c..7a88575 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-close@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-close@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-dirty-selected.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-dirty-selected.png index d882c1d..b6cc225 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-dirty-selected.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-dirty-selected.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-dirty-selected@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-dirty-selected@2x.png index 39dc46b..6fce947 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-dirty-selected@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-dirty-selected@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-closed-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-closed-hover.png index 6c6cecf..67ccfb4 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-closed-hover.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-closed-hover.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-closed-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-closed-hover@2x.png index e49848e..2b56eeb 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-closed-hover@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-closed-hover@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-closed.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-closed.png index 4a69442..da76c65 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-closed.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-closed.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-closed@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-closed@2x.png index 4ec7114..0c240cf 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-closed@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-closed@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-open-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-open-hover.png index 75d9596..7ab33ad 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-open-hover.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-open-hover.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-open-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-open-hover@2x.png index 38b2973..d6a6891 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-open-hover@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-open-hover@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-open.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-open.png index d96748d..653d14f 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-open.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-open.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-open@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-open@2x.png index 778b395..26d06bb 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-open@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-open@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed-hover.png new file mode 100644 index 0000000..69a8d9d Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed-hover.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed-hover@2x.png new file mode 100644 index 0000000..9bfdf66 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed-hover@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed-selected.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed-selected.png new file mode 100644 index 0000000..cdd80ba Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed-selected.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed-selected@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed-selected@2x.png new file mode 100644 index 0000000..a8e0890 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed-selected@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed.png new file mode 100644 index 0000000..30fca80 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed@2x.png new file mode 100644 index 0000000..106876f Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-open-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-open-hover.png new file mode 100644 index 0000000..ea61a4f Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-open-hover.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-open-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-open-hover@2x.png new file mode 100644 index 0000000..1ee80ed Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-open-hover@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-open-selected.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-open-selected.png new file mode 100644 index 0000000..1440eb3 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-open-selected.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-open-selected@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-open-selected@2x.png new file mode 100644 index 0000000..7ca33d6 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-open-selected@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-open.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-open.png new file mode 100644 index 0000000..8eb2e7a Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-open.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-open@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-open@2x.png new file mode 100644 index 0000000..e4c22a1 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-open@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed-hover.png index 83fc42b..c5b3323 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed-hover.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed-hover.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed-hover@2x.png index c0d5cc5..9ad2704 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed-hover@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed-hover@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed-selected.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed-selected.png index 7a5a877..f3b8cde 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed-selected.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed-selected.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed-selected@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed-selected@2x.png index 20e918d..952963a 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed-selected@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed-selected@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed.png index 819803d..9ae650b 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed@2x.png index 8a0918e..17e88c0 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open-hover.png index 1730a23..881580e 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open-hover.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open-hover.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open-hover@2x.png index 8734f58..be7ab7b 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open-hover@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open-hover@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open-selected.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open-selected.png index f0a0bf4..ac3c28f 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open-selected.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open-selected.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open-selected@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open-selected@2x.png index f9650f6..fb9a2c6 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open-selected@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open-selected@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open.png index d0f5cd4..576df0b 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open@2x.png index 211ff8b..dbb3622 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-buffer-off.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-buffer-off.png index 20cc171..0851ba0 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-buffer-off.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-buffer-off.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-buffer-off@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-buffer-off@2x.png index 1644a77..41bd998 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-buffer-off@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-buffer-off@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-buffer-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-buffer-on.png index ff80fc9..9f464b8 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-buffer-on.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-buffer-on.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-buffer-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-buffer-on@2x.png index c71ee4e..e8f6025 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-buffer-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-buffer-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-case-off.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-case-off.png index 281ae05..028f16c 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-case-off.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-case-off.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-case-off@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-case-off@2x.png index 9a2f176..b602eb2 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-case-off@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-case-off@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-case-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-case-on.png index e768693..6eb4ad3 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-case-on.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-case-on.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-case-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-case-on@2x.png index 335ac21..729fea0 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-case-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-case-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-context-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-context-on.png index 6330e23..e4ad24b 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-context-on.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-context-on.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-context-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-context-on@2x.png index c6163e9..35e5ccf 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-context-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-context-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-highlight-off.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-highlight-off.png index 2cedd0a..2549b51 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-highlight-off.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-highlight-off.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-highlight-off@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-highlight-off@2x.png index 243784e..a24f029 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-highlight-off@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-highlight-off@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-highlight-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-highlight-on.png index 3472404..f10bafc 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-highlight-on.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-highlight-on.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-highlight-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-highlight-on@2x.png index f5610d1..7e938af 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-highlight-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-highlight-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-preserve-off.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-preserve-off.png index f0b7616..402785f 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-preserve-off.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-preserve-off.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-preserve-off@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-preserve-off@2x.png index 9816b42..39bb640 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-preserve-off@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-preserve-off@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-preserve-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-preserve-on.png index 3c58245..a8e9290 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-preserve-on.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-preserve-on.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-preserve-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-preserve-on@2x.png index b515dcb..819a8c9 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-preserve-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-preserve-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-regex-off.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-regex-off.png index 61ff932..54657e1 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-regex-off.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-regex-off.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-regex-off@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-regex-off@2x.png index cf88632..884a9bc 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-regex-off@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-regex-off@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-regex-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-regex-on.png index 658b0c8..92d7cc8 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-regex-on.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-regex-on.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-regex-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-regex-on@2x.png index 3b6b7f2..aab3f57 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-regex-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-regex-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-reverse-off.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-reverse-off.png index d6ec595..e6b8fd3 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-reverse-off.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-reverse-off.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-reverse-off@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-reverse-off@2x.png index 503e6bb..0687d84 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-reverse-off@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-reverse-off@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-reverse-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-reverse-on.png index a4ebbb4..cf71654 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-reverse-on.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-reverse-on.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-reverse-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-reverse-on@2x.png index a3cc249..8d5efe7 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-reverse-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-reverse-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-selection-off.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-selection-off.png index 91378b1..fa2d2bd 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-selection-off.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-selection-off.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-selection-off@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-selection-off@2x.png index a0a59e7..4c06d66 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-selection-off@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-selection-off@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-selection-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-selection-on.png index d303c5c..7d34fc8 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-selection-on.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-selection-on.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-selection-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-selection-on@2x.png index 473d626..775593d 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-selection-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-selection-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-word-off.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-word-off.png index b0af0ca..0e7d77e 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-word-off.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-word-off.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-word-off@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-word-off@2x.png index d0328da..bcc96f9 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-word-off@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-word-off@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-word-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-word-on.png index f3050c8..6bdd0bd 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-word-on.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-word-on.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-word-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-word-on@2x.png index deea770..f3bf654 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-word-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-word-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-wrap-off.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-wrap-off.png index f9de6d3..f60a4d3 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-wrap-off.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-wrap-off.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-wrap-off@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-wrap-off@2x.png index c05e445..be2929c 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-wrap-off@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-wrap-off@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-wrap-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-wrap-on.png index 09161a2..553da68 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-wrap-on.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-wrap-on.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-wrap-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-wrap-on@2x.png index 81aa1d4..ac044cc 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-wrap-on@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-wrap-on@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/overlay-scrollbar-horizontal.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/overlay-scrollbar-horizontal.png index ff2ca6e..be64d27 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/overlay-scrollbar-horizontal.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/overlay-scrollbar-horizontal.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/overlay-scrollbar-horizontal@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/overlay-scrollbar-horizontal@2x.png index 2b5bb07..0bbd1cb 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/overlay-scrollbar-horizontal@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/overlay-scrollbar-horizontal@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/overlay-scrollbar-vertical.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/overlay-scrollbar-vertical.png index 5e981c3..70100d0 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/overlay-scrollbar-vertical.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/overlay-scrollbar-vertical.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/overlay-scrollbar-vertical@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/overlay-scrollbar-vertical@2x.png index 3d3ff15..e860a90 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/overlay-scrollbar-vertical@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/overlay-scrollbar-vertical@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/panel-close-pressed.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/panel-close-pressed.png new file mode 100644 index 0000000..a612a71 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/panel-close-pressed.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/panel-close-pressed@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/panel-close-pressed@2x.png new file mode 100644 index 0000000..8d8b03b Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/panel-close-pressed@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/panel-close.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/panel-close.png new file mode 100644 index 0000000..bf78f8a Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/panel-close.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/panel-close@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/panel-close@2x.png new file mode 100644 index 0000000..a3b944a Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/panel-close@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/quick-panel-background-fix.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/quick-panel-background-fix.png index f874034..05440d4 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/quick-panel-background-fix.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/quick-panel-background-fix.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/quick-panel-background-fix@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/quick-panel-background-fix@2x.png index 1bdd5fb..479ef3a 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/quick-panel-background-fix@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/quick-panel-background-fix@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/quick-panel-background.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/quick-panel-background.png index 68b8eb5..9897713 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/quick-panel-background.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/quick-panel-background.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/quick-panel-background@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/quick-panel-background@2x.png index 57ba0ec..ee7e828 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/quick-panel-background@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/quick-panel-background@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/sidebar-row-selected.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/sidebar-row-selected.png index 02d7e68..5166236 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/sidebar-row-selected.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/sidebar-row-selected.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/sidebar-row-selected@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/sidebar-row-selected@2x.png index 3abf3e7..9afcf8c 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/sidebar-row-selected@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/sidebar-row-selected@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-puck-horizontal.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-puck-horizontal.png index 826080e..f91545b 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-puck-horizontal.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-puck-horizontal.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-puck-horizontal@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-puck-horizontal@2x.png index 41204cb..5dfd8e7 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-puck-horizontal@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-puck-horizontal@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-corner.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-corner.png index 6369c9f..89cb55c 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-corner.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-corner.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-corner@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-corner@2x.png index 75ea721..be274ed 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-corner@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-corner@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-horizontal.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-horizontal.png index 8486fcb..6a79412 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-horizontal.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-horizontal.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-horizontal@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-horizontal@2x.png index b074beb..34b1871 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-horizontal@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-horizontal@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-vertical.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-vertical.png index 1321ccc..5db1229 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-vertical.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-vertical.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-vertical@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-vertical@2x.png index cbf5138..417b4d4 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-vertical@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-vertical@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tab-close-inactive.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tab-close-inactive.png index 17d53d2..f1fadd1 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tab-close-inactive.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tab-close-inactive.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tab-close-inactive@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tab-close-inactive@2x.png index 89a063f..f5412cf 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tab-close-inactive@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tab-close-inactive@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tab-close.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tab-close.png index 0e44300..c86acc3 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tab-close.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tab-close.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tab-close@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tab-close@2x.png index 6af55f6..466e73b 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tab-close@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tab-close@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-background.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-background.png index 54d9a50..c895cb9 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-background.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-background.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-background@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-background@2x.png index ea8ae03..21fa7c4 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-background@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-background@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-left-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-left-hover.png new file mode 100644 index 0000000..a990fc7 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-left-hover.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-left-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-left-hover@2x.png new file mode 100644 index 0000000..ab2433a Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-left-hover@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-left.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-left.png new file mode 100644 index 0000000..6c6a021 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-left.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-left@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-left@2x.png new file mode 100644 index 0000000..22d4d75 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-left@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-list-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-list-hover.png new file mode 100644 index 0000000..f55c06a Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-list-hover.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-list-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-list-hover@2x.png new file mode 100644 index 0000000..0878115 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-list-hover@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-list.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-list.png new file mode 100644 index 0000000..b97cac0 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-list.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-list@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-list@2x.png new file mode 100644 index 0000000..7610a76 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-list@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-right-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-right-hover.png new file mode 100644 index 0000000..8081499 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-right-hover.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-right-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-right-hover@2x.png new file mode 100644 index 0000000..03a2893 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-right-hover@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-right.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-right.png new file mode 100644 index 0000000..b44e89c Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-right.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-right@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-right@2x.png new file mode 100644 index 0000000..ac80fc0 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-right@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field-list-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field-list-hover.png new file mode 100644 index 0000000..ab8b8c6 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field-list-hover.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field-list-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field-list-hover@2x.png new file mode 100644 index 0000000..b398714 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field-list-hover@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field-list.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field-list.png new file mode 100644 index 0000000..53ec754 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field-list.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field-list@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field-list@2x.png new file mode 100644 index 0000000..55528d7 Binary files /dev/null and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field-list@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field.png index d4e67d3..b992a45 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field@2x.png index 3b55fda..045b666 100644 Binary files a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field@2x.png and b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field@2x.png differ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/package-metadata.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/package-metadata.json index 171865a..55e09df 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/package-metadata.json +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/package-metadata.json @@ -1 +1 @@ -{"url": "http://buymeasoda.github.com/soda-theme/", "version": "2012.12.12.08.37.38", "description": "Dark and light custom UI themes for Sublime Text 2"} \ No newline at end of file +{"url": "http://buymeasoda.github.com/soda-theme/", "version": "2013.08.02.02.42.26", "description": "Dark and light custom UI themes for Sublime Text"} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/ZZZ.EthanBrown.SublimeKeyMap.Util/Default (OSX).sublime-keymap b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/ZZZ.EthanBrown.SublimeKeyMap.Util/Default (OSX).sublime-keymap new file mode 100644 index 0000000..2e3cd54 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/ZZZ.EthanBrown.SublimeKeyMap.Util/Default (OSX).sublime-keymap @@ -0,0 +1,31 @@ +[ + //AdvancedNewFile - https://github.com/xobb1t/Sublime-AdvancedNewFile + { "keys": ["ctrl+alt+n"], "command": "advanced_new_file"}, + //{ "keys": ["shift+ctrl+alt+n"], "command": "advanced_new_file", "args": {"is_python": true}}, + { + "keys": ["tab"], + "command": "insert_best_completion", + "args": {"default": "", "exact": false}, + "context": [ + { "key": "advanced_new_file_completion"}, + { "key": "setting.tab_completion", "operator": "equal", "operand": true }, + { "key": "last_command", "operator": "not_equal", "operand": "insert_best_completion" }, + { "key": "auto_complete_visible" , "operator": "equal", "operand": false} + ] + }, + + //Navigation History - https://github.com/timjrobinson/SublimeNavigationHistory + { "keys": ["alt+left"], "command": "navigation_history_back"}, + { "keys": ["alt+right"], "command": "navigation_history_forward"}, + + //SideBarEnhancements - https://github.com/titoBouzout/SideBarEnhancements/ + { "keys": ["super+t"], "command": "side_bar_new_file2" }, + { "keys": ["f12"], "command": "side_bar_open_in_browser" , "args":{"paths":[], "type":"testing" }}, + { "keys": ["alt+f12"], "command": "side_bar_open_in_browser", "args":{"paths":[], "type":"production" }}, + { "keys": ["f2"], "command": "side_bar_rename" }, + { "keys": ["ctrl+alt+f"], "command": "side_bar_find_files_path_containing" }, + { "keys": ["ctrl+shift+r"], "command": "side_bar_move" }, + + //SublimeFiles - https://github.com/al63/SublimeFiles + { "keys": ["shift+ctrl+alt+n"], "command": "sublime_files", "args": {"command":"navigate"}} +] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/ZZZ.EthanBrown.SublimeKeyMap.Util/package-metadata.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/ZZZ.EthanBrown.SublimeKeyMap.Util/package-metadata.json index 9054c07..1f9554c 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/ZZZ.EthanBrown.SublimeKeyMap.Util/package-metadata.json +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/ZZZ.EthanBrown.SublimeKeyMap.Util/package-metadata.json @@ -1 +1 @@ -{"url": "https://github.com/Iristyle/SublimeKeyMap.Util", "version": "2013.03.11.16.06.11", "description": "A simple repository used to host / share my customized Sublime Text 2 key bindings for Sublime utility plugins"} \ No newline at end of file +{"url": "https://github.com/Iristyle/SublimeKeyMap.Util", "version": "2013.09.17.01.16.26", "description": "A simple repository used to host / share my customized Sublime Text 2 key bindings for Sublime utility plugins"} \ No newline at end of file