Advertisement
b3gund4L

git

Aug 25th, 2022
1,922
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 22.06 KB | None | 0 0
  1. #!/usr/bin/env python3
  2. # encoding: utf-8
  3. from contextlib import closing
  4. import argparse
  5. import multiprocessing
  6. import os
  7. import os.path
  8. import re
  9. import socket
  10. import subprocess
  11. import sys
  12. import traceback
  13. import urllib.parse
  14. import urllib3
  15.  
  16. import bs4
  17. import dulwich.index
  18. import dulwich.objects
  19. import dulwich.pack
  20. import requests
  21. import socks
  22.  
  23.  
  24. def printf(fmt, *args, file=sys.stdout):
  25.     if args:
  26.         fmt = fmt % args
  27.  
  28.     file.write(fmt)
  29.     file.flush()
  30.  
  31.  
  32. def is_html(response):
  33.     """ Return True if the response is a HTML webpage """
  34.     return (
  35.             "Content-Type" in response.headers
  36.             and "text/html" in response.headers["Content-Type"]
  37.     )
  38.  
  39.  
  40. def is_safe_path(path):
  41.     """ Prevent directory traversal attacks """
  42.     if path.startswith("/"):
  43.         return False
  44.  
  45.     safe_path = os.path.expanduser("~")
  46.     return (
  47.             os.path.commonpath(
  48.                 (os.path.realpath(os.path.join(safe_path, path)), safe_path)
  49.             )
  50.             == safe_path
  51.     )
  52.  
  53.  
  54. def get_indexed_files(response):
  55.     """ Return all the files in the directory index webpage """
  56.     html = bs4.BeautifulSoup(response.text, "html.parser")
  57.     files = []
  58.  
  59.     for link in html.find_all("a"):
  60.         url = urllib.parse.urlparse(link.get("href"))
  61.  
  62.         if (
  63.                 url.path
  64.                 and is_safe_path(url.path)
  65.                 and not url.scheme
  66.                 and not url.netloc
  67.         ):
  68.             files.append(url.path)
  69.  
  70.     return files
  71.  
  72.  
  73. def verify_response(response):
  74.     if response.status_code != 200:
  75.         return (
  76.             False,
  77.             "[-] %s/%s responded with status code {code}\n".format(
  78.                 code=response.status_code
  79.             ),
  80.         )
  81.     elif (
  82.             "Content-Length" in response.headers
  83.             and response.headers["Content-Length"] == 0
  84.     ):
  85.         return False, "[-] %s/%s responded with a zero-length body\n"
  86.     elif (
  87.             "Content-Type" in response.headers
  88.             and "text/html" in response.headers["Content-Type"]
  89.     ):
  90.         return False, "[-] %s/%s responded with HTML\n"
  91.     else:
  92.         return True, True
  93.  
  94.  
  95. def create_intermediate_dirs(path):
  96.     """ Create intermediate directories, if necessary """
  97.  
  98.     dirname, basename = os.path.split(path)
  99.  
  100.     if dirname and not os.path.exists(dirname):
  101.         try:
  102.             os.makedirs(dirname)
  103.         except FileExistsError:
  104.             pass  # race condition
  105.  
  106.  
  107. def get_referenced_sha1(obj_file):
  108.     """ Return all the referenced SHA1 in the given object file """
  109.     objs = []
  110.  
  111.     if isinstance(obj_file, dulwich.objects.Commit):
  112.         objs.append(obj_file.tree.decode())
  113.  
  114.         for parent in obj_file.parents:
  115.             objs.append(parent.decode())
  116.     elif isinstance(obj_file, dulwich.objects.Tree):
  117.         for item in obj_file.iteritems():
  118.             objs.append(item.sha.decode())
  119.     elif isinstance(obj_file, dulwich.objects.Blob):
  120.         pass
  121.     elif isinstance(obj_file, dulwich.objects.Tag):
  122.         pass
  123.     else:
  124.         printf(
  125.             "error: unexpected object type: %r\n" % obj_file, file=sys.stderr
  126.         )
  127.         sys.exit(1)
  128.  
  129.     return objs
  130.  
  131.  
  132. class Worker(multiprocessing.Process):
  133.     """ Worker for process_tasks """
  134.  
  135.     def __init__(self, pending_tasks, tasks_done, args):
  136.         super().__init__()
  137.         self.daemon = True
  138.         self.pending_tasks = pending_tasks
  139.         self.tasks_done = tasks_done
  140.         self.args = args
  141.  
  142.     def run(self):
  143.         # initialize process
  144.         self.init(*self.args)
  145.  
  146.         # fetch and do tasks
  147.         while True:
  148.             task = self.pending_tasks.get(block=True)
  149.  
  150.             if task is None:  # end signal
  151.                 return
  152.  
  153.             try:
  154.                 result = self.do_task(task, *self.args)
  155.             except Exception:
  156.                 printf("Task %s raised exception:\n", task, file=sys.stderr)
  157.                 traceback.print_exc()
  158.                 result = []
  159.  
  160.             assert isinstance(
  161.                 result, list
  162.             ), "do_task() should return a list of tasks"
  163.  
  164.             self.tasks_done.put(result)
  165.  
  166.     def init(self, *args):
  167.         raise NotImplementedError
  168.  
  169.     def do_task(self, task, *args):
  170.         raise NotImplementedError
  171.  
  172.  
  173. def process_tasks(initial_tasks, worker, jobs, args=(), tasks_done=None):
  174.     """ Process tasks in parallel """
  175.  
  176.     if not initial_tasks:
  177.         return
  178.  
  179.     tasks_seen = set(tasks_done) if tasks_done else set()
  180.     pending_tasks = multiprocessing.Queue()
  181.     tasks_done = multiprocessing.Queue()
  182.     num_pending_tasks = 0
  183.  
  184.     # add all initial tasks in the queue
  185.     for task in initial_tasks:
  186.         assert task is not None
  187.  
  188.         if task not in tasks_seen:
  189.             pending_tasks.put(task)
  190.             num_pending_tasks += 1
  191.             tasks_seen.add(task)
  192.  
  193.     # initialize processes
  194.     processes = [worker(pending_tasks, tasks_done, args) for _ in range(jobs)]
  195.  
  196.     # launch them all
  197.     for p in processes:
  198.         p.start()
  199.  
  200.     # collect task results
  201.     while num_pending_tasks > 0:
  202.         task_result = tasks_done.get(block=True)
  203.         num_pending_tasks -= 1
  204.  
  205.         for task in task_result:
  206.             assert task is not None
  207.  
  208.             if task not in tasks_seen:
  209.                 pending_tasks.put(task)
  210.                 num_pending_tasks += 1
  211.                 tasks_seen.add(task)
  212.  
  213.     # send termination signal (task=None)
  214.     for _ in range(jobs):
  215.         pending_tasks.put(None)
  216.  
  217.     # join all
  218.     for p in processes:
  219.         p.join()
  220.  
  221.  
  222. class DownloadWorker(Worker):
  223.     """ Download a list of files """
  224.  
  225.     def init(self, url, directory, retry, timeout, http_headers):
  226.         self.session = requests.Session()
  227.         self.session.verify = False
  228.         self.session.headers = http_headers
  229.         self.session.mount(
  230.             url, requests.adapters.HTTPAdapter(max_retries=retry)
  231.         )
  232.  
  233.     def do_task(self, filepath, url, directory, retry, timeout, http_headers):
  234.         if os.path.isfile(os.path.join(directory, filepath)):
  235.             printf("[-] Already downloaded %s/%s\n", url, filepath)
  236.             return []
  237.  
  238.         with closing(
  239.                 self.session.get(
  240.                     "%s/%s" % (url, filepath),
  241.                     allow_redirects=False,
  242.                     stream=True,
  243.                     timeout=timeout,
  244.                 )
  245.         ) as response:
  246.             printf(
  247.                 "[-] Fetching %s/%s [%d]\n",
  248.                 url,
  249.                 filepath,
  250.                 response.status_code,
  251.             )
  252.  
  253.             valid, error_message = verify_response(response)
  254.             if not valid:
  255.                 printf(error_message, url, filepath, file=sys.stderr)
  256.                 return []
  257.  
  258.             abspath = os.path.abspath(os.path.join(directory, filepath))
  259.             create_intermediate_dirs(abspath)
  260.  
  261.             # write file
  262.             with open(abspath, "wb") as f:
  263.                 for chunk in response.iter_content(4096):
  264.                     f.write(chunk)
  265.  
  266.             return []
  267.  
  268.  
  269. class RecursiveDownloadWorker(DownloadWorker):
  270.     """ Download a directory recursively """
  271.  
  272.     def do_task(self, filepath, url, directory, retry, timeout, http_headers):
  273.         if os.path.isfile(os.path.join(directory, filepath)):
  274.             printf("[-] Already downloaded %s/%s\n", url, filepath)
  275.             return []
  276.  
  277.         with closing(
  278.                 self.session.get(
  279.                     "%s/%s" % (url, filepath),
  280.                     allow_redirects=False,
  281.                     stream=True,
  282.                     timeout=timeout,
  283.                 )
  284.         ) as response:
  285.             printf(
  286.                 "[-] Fetching %s/%s [%d]\n",
  287.                 url,
  288.                 filepath,
  289.                 response.status_code,
  290.             )
  291.  
  292.             if (
  293.                     response.status_code in (301, 302)
  294.                     and "Location" in response.headers
  295.                     and response.headers["Location"].endswith(filepath + "/")
  296.             ):
  297.                 return [filepath + "/"]
  298.  
  299.             if filepath.endswith("/"):  # directory index
  300.                 assert is_html(response)
  301.  
  302.                 return [
  303.                     filepath + filename
  304.                     for filename in get_indexed_files(response)
  305.                 ]
  306.             else:  # file
  307.                 valid, error_message = verify_response(response)
  308.                 if not valid:
  309.                     printf(error_message, url, filepath, file=sys.stderr)
  310.                     return []
  311.  
  312.                 abspath = os.path.abspath(os.path.join(directory, filepath))
  313.                 create_intermediate_dirs(abspath)
  314.  
  315.                 # write file
  316.                 with open(abspath, "wb") as f:
  317.                     for chunk in response.iter_content(4096):
  318.                         f.write(chunk)
  319.  
  320.                 return []
  321.  
  322.  
  323. class FindRefsWorker(DownloadWorker):
  324.     """ Find refs/ """
  325.  
  326.     def do_task(self, filepath, url, directory, retry, timeout, http_headers):
  327.         response = self.session.get(
  328.             "%s/%s" % (url, filepath), allow_redirects=False, timeout=timeout
  329.         )
  330.         printf(
  331.             "[-] Fetching %s/%s [%d]\n", url, filepath, response.status_code
  332.         )
  333.  
  334.         valid, error_message = verify_response(response)
  335.         if not valid:
  336.             printf(error_message, url, filepath, file=sys.stderr)
  337.             return []
  338.  
  339.         abspath = os.path.abspath(os.path.join(directory, filepath))
  340.         create_intermediate_dirs(abspath)
  341.  
  342.         # write file
  343.         with open(abspath, "w") as f:
  344.             f.write(response.text)
  345.  
  346.         # find refs
  347.         tasks = []
  348.  
  349.         for ref in re.findall(
  350.                 r"(refs(/[a-zA-Z0-9\-\.\_\*]+)+)", response.text
  351.         ):
  352.             ref = ref[0]
  353.             if not ref.endswith("*") and is_safe_path(ref):
  354.                 tasks.append(".git/%s" % ref)
  355.                 tasks.append(".git/logs/%s" % ref)
  356.  
  357.         return tasks
  358.  
  359.  
  360. class FindObjectsWorker(DownloadWorker):
  361.     """ Find objects """
  362.  
  363.     def do_task(self, obj, url, directory, retry, timeout, http_headers):
  364.         filepath = ".git/objects/%s/%s" % (obj[:2], obj[2:])
  365.  
  366.         if os.path.isfile(os.path.join(directory, filepath)):
  367.             printf("[-] Already downloaded %s/%s\n", url, filepath)
  368.         else:
  369.             response = self.session.get(
  370.                 "%s/%s" % (url, filepath),
  371.                 allow_redirects=False,
  372.                 timeout=timeout,
  373.                 )
  374.             printf(
  375.                 "[-] Fetching %s/%s [%d]\n",
  376.                 url,
  377.                 filepath,
  378.                 response.status_code,
  379.             )
  380.  
  381.             valid, error_message = verify_response(response)
  382.             if not valid:
  383.                 printf(error_message, url, filepath, file=sys.stderr)
  384.                 return []
  385.  
  386.             abspath = os.path.abspath(os.path.join(directory, filepath))
  387.             create_intermediate_dirs(abspath)
  388.  
  389.             # write file
  390.             with open(abspath, "wb") as f:
  391.                 f.write(response.content)
  392.  
  393.         abspath = os.path.abspath(os.path.join(directory, filepath))
  394.         # parse object file to find other objects
  395.         obj_file = dulwich.objects.ShaFile.from_path(abspath)
  396.         return get_referenced_sha1(obj_file)
  397.  
  398.  
  399. def fetch_git(url, directory, jobs, retry, timeout, http_headers):
  400.     """ Dump a git repository into the output directory """
  401.  
  402.     assert os.path.isdir(directory), "%s is not a directory" % directory
  403.     assert jobs >= 1, "invalid number of jobs"
  404.     assert retry >= 1, "invalid number of retries"
  405.     assert timeout >= 1, "invalid timeout"
  406.  
  407.     session = requests.Session()
  408.     session.verify = False
  409.     session.headers = http_headers
  410.     session.mount(url, requests.adapters.HTTPAdapter(max_retries=retry))
  411.  
  412.     if os.listdir(directory):
  413.         printf("Warning: Destination '%s' is not empty\n", directory)
  414.  
  415.     # find base url
  416.     url = url.rstrip("/")
  417.     if url.endswith("HEAD"):
  418.         url = url[:-4]
  419.     url = url.rstrip("/")
  420.     if url.endswith(".git"):
  421.         url = url[:-4]
  422.     url = url.rstrip("/")
  423.  
  424.     # check for /.git/HEAD
  425.     printf("[-] Testing %s/.git/HEAD ", url)
  426.     response = session.get("%s/.git/HEAD" % url, allow_redirects=False)
  427.     printf("[%d]\n", response.status_code)
  428.  
  429.     valid, error_message = verify_response(response)
  430.     if not valid:
  431.         printf(error_message, url, "/.git/HEAD", file=sys.stderr)
  432.         return 1
  433.     elif not re.match(r"^(ref:.*|[0-9a-f]{40}$)", response.text.strip()):
  434.         printf(
  435.             "error: %s/.git/HEAD is not a git HEAD file\n",
  436.             url,
  437.             file=sys.stderr,
  438.         )
  439.         return 1
  440.  
  441.     # check for directory listing
  442.     printf("[-] Testing %s/.git/ ", url)
  443.     response = session.get("%s/.git/" % url, allow_redirects=False)
  444.     printf("[%d]\n", response.status_code)
  445.  
  446.     if (
  447.             response.status_code == 200
  448.             and is_html(response)
  449.             and "HEAD" in get_indexed_files(response)
  450.     ):
  451.         printf("[-] Fetching .git recursively\n")
  452.         process_tasks(
  453.             [".git/", ".gitignore"],
  454.             RecursiveDownloadWorker,
  455.             jobs,
  456.             args=(url, directory, retry, timeout, http_headers),
  457.         )
  458.  
  459.         printf("[-] Running git checkout .\n")
  460.         os.chdir(directory)
  461.         subprocess.check_call(["git", "checkout", "."])
  462.         return 0
  463.  
  464.     # no directory listing
  465.     printf("[-] Fetching common files\n")
  466.     tasks = [
  467.         ".gitignore",
  468.         ".git/COMMIT_EDITMSG",
  469.         ".git/description",
  470.         ".git/hooks/applypatch-msg.sample",
  471.         ".git/hooks/commit-msg.sample",
  472.         ".git/hooks/post-commit.sample",
  473.         ".git/hooks/post-receive.sample",
  474.         ".git/hooks/post-update.sample",
  475.         ".git/hooks/pre-applypatch.sample",
  476.         ".git/hooks/pre-commit.sample",
  477.         ".git/hooks/pre-push.sample",
  478.         ".git/hooks/pre-rebase.sample",
  479.         ".git/hooks/pre-receive.sample",
  480.         ".git/hooks/prepare-commit-msg.sample",
  481.         ".git/hooks/update.sample",
  482.         ".git/index",
  483.         ".git/info/exclude",
  484.         ".git/objects/info/packs",
  485.     ]
  486.     process_tasks(
  487.         tasks,
  488.         DownloadWorker,
  489.         jobs,
  490.         args=(url, directory, retry, timeout, http_headers),
  491.     )
  492.  
  493.     # find refs
  494.     printf("[-] Finding refs/\n")
  495.     tasks = [
  496.         ".git/FETCH_HEAD",
  497.         ".git/HEAD",
  498.         ".git/ORIG_HEAD",
  499.         ".git/config",
  500.         ".git/info/refs",
  501.         ".git/logs/HEAD",
  502.         ".git/logs/refs/heads/master",
  503.         ".git/logs/refs/remotes/origin/HEAD",
  504.         ".git/logs/refs/remotes/origin/master",
  505.         ".git/logs/refs/stash",
  506.         ".git/packed-refs",
  507.         ".git/refs/heads/master",
  508.         ".git/refs/remotes/origin/HEAD",
  509.         ".git/refs/remotes/origin/master",
  510.         ".git/refs/stash",
  511.         ".git/refs/wip/wtree/refs/heads/master",  # Magit
  512.         ".git/refs/wip/index/refs/heads/master",  # Magit
  513.     ]
  514.  
  515.     process_tasks(
  516.         tasks,
  517.         FindRefsWorker,
  518.         jobs,
  519.         args=(url, directory, retry, timeout, http_headers),
  520.     )
  521.  
  522.     # find packs
  523.     printf("[-] Finding packs\n")
  524.     tasks = []
  525.  
  526.     # use .git/objects/info/packs to find packs
  527.     info_packs_path = os.path.join(
  528.         directory, ".git", "objects", "info", "packs"
  529.     )
  530.     if os.path.exists(info_packs_path):
  531.         with open(info_packs_path, "r") as f:
  532.             info_packs = f.read()
  533.  
  534.         for sha1 in re.findall(r"pack-([a-f0-9]{40})\.pack", info_packs):
  535.             tasks.append(".git/objects/pack/pack-%s.idx" % sha1)
  536.             tasks.append(".git/objects/pack/pack-%s.pack" % sha1)
  537.  
  538.     process_tasks(
  539.         tasks,
  540.         DownloadWorker,
  541.         jobs,
  542.         args=(url, directory, retry, timeout, http_headers),
  543.     )
  544.  
  545.     # find objects
  546.     printf("[-] Finding objects\n")
  547.     objs = set()
  548.     packed_objs = set()
  549.  
  550.     # .git/packed-refs, .git/info/refs, .git/refs/*, .git/logs/*
  551.     files = [
  552.         os.path.join(directory, ".git", "packed-refs"),
  553.         os.path.join(directory, ".git", "info", "refs"),
  554.         os.path.join(directory, ".git", "FETCH_HEAD"),
  555.         os.path.join(directory, ".git", "ORIG_HEAD"),
  556.     ]
  557.     for dirpath, _, filenames in os.walk(
  558.             os.path.join(directory, ".git", "refs")
  559.     ):
  560.         for filename in filenames:
  561.             files.append(os.path.join(dirpath, filename))
  562.     for dirpath, _, filenames in os.walk(
  563.             os.path.join(directory, ".git", "logs")
  564.     ):
  565.         for filename in filenames:
  566.             files.append(os.path.join(dirpath, filename))
  567.  
  568.     for filepath in files:
  569.         if not os.path.exists(filepath):
  570.             continue
  571.  
  572.         with open(filepath, "r") as f:
  573.             content = f.read()
  574.  
  575.         for obj in re.findall(r"(^|\s)([a-f0-9]{40})($|\s)", content):
  576.             obj = obj[1]
  577.             objs.add(obj)
  578.  
  579.     # use .git/index to find objects
  580.     index_path = os.path.join(directory, ".git", "index")
  581.     if os.path.exists(index_path):
  582.         index = dulwich.index.Index(index_path)
  583.  
  584.         for entry in index.iterobjects():
  585.             objs.add(entry[1].decode())
  586.  
  587.     # use packs to find more objects to fetch, and objects that are packed
  588.     pack_file_dir = os.path.join(directory, ".git", "objects", "pack")
  589.     if os.path.isdir(pack_file_dir):
  590.         for filename in os.listdir(pack_file_dir):
  591.             if filename.startswith("pack-") and filename.endswith(".pack"):
  592.                 pack_data_path = os.path.join(pack_file_dir, filename)
  593.                 pack_idx_path = os.path.join(
  594.                     pack_file_dir, filename[:-5] + ".idx"
  595.                 )
  596.                 pack_data = dulwich.pack.PackData(pack_data_path)
  597.                 pack_idx = dulwich.pack.load_pack_index(pack_idx_path)
  598.                 try:
  599.                     pack = dulwich.pack.Pack.from_objects(pack_data, pack_idx)
  600.                     for obj_file in pack.iterobjects():
  601.                         packed_objs.add(obj_file.sha().hexdigest())
  602.                         objs |= set(get_referenced_sha1(obj_file))
  603.                 except dulwich.errors.ChecksumMismatch as error:
  604.                     print(error)
  605.                
  606.  
  607.                
  608.  
  609.     # fetch all objects
  610.     printf("[-] Fetching objects\n")
  611.     process_tasks(
  612.         objs,
  613.         FindObjectsWorker,
  614.         jobs,
  615.         args=(url, directory, retry, timeout, http_headers),
  616.         tasks_done=packed_objs,
  617.     )
  618.  
  619.     # git checkout
  620.     printf("[-] Running git checkout .\n")
  621.     os.chdir(directory)
  622.  
  623.     # ignore errors
  624.     subprocess.call(["git", "checkout", "."], stderr=open(os.devnull, "wb"))
  625.  
  626.     return 0
  627.  
  628.  
  629. def main():
  630.     parser = argparse.ArgumentParser(
  631.         usage="git-dumper [options] URL DIR",
  632.         description="Dump a git repository from a website.",
  633.     )
  634.     parser.add_argument("url", metavar="URL", help="url")
  635.     parser.add_argument("directory", metavar="DIR", help="output directory")
  636.     parser.add_argument("--proxy", help="use the specified proxy")
  637.     parser.add_argument(
  638.         "-j",
  639.         "--jobs",
  640.         type=int,
  641.         default=10,
  642.         help="number of simultaneous requests",
  643.     )
  644.     parser.add_argument(
  645.         "-r",
  646.         "--retry",
  647.         type=int,
  648.         default=3,
  649.         help="number of request attempts before giving up",
  650.     )
  651.     parser.add_argument(
  652.         "-t",
  653.         "--timeout",
  654.         type=int,
  655.         default=3,
  656.         help="maximum time in seconds before giving up",
  657.     )
  658.     parser.add_argument(
  659.         "-u",
  660.         "--user-agent",
  661.         type=str,
  662.         default="Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0",
  663.         help="user-agent to use for requests",
  664.     )
  665.     parser.add_argument(
  666.         "-H",
  667.         "--header",
  668.         type=str,
  669.         action="append",
  670.         help="additional http headers, e.g `NAME=VALUE`",
  671.     )
  672.     args = parser.parse_args()
  673.  
  674.     # jobs
  675.     if args.jobs < 1:
  676.         parser.error("invalid number of jobs, got `%d`" % args.jobs)
  677.  
  678.     # retry
  679.     if args.retry < 1:
  680.         parser.error("invalid number of retries, got `%d`" % args.retry)
  681.  
  682.     # timeout
  683.     if args.timeout < 1:
  684.         parser.error("invalid timeout, got `%d`" % args.timeout)
  685.  
  686.     # header
  687.     http_headers = {"User-Agent": args.user_agent}
  688.     if args.header:
  689.         for header in args.header:
  690.             tokens = header.split("=", maxsplit=1)
  691.             if len(tokens) != 2:
  692.                 parser.error(
  693.                     "http header must have the form NAME=VALUE, got `%s`"
  694.                     % header
  695.                 )
  696.             name, value = tokens
  697.             http_headers[name.strip()] = value.strip()
  698.  
  699.     # proxy
  700.     if args.proxy:
  701.         proxy_valid = False
  702.  
  703.         for pattern, proxy_type in [
  704.             (r"^socks5:(.*):(\d+)$", socks.PROXY_TYPE_SOCKS5),
  705.             (r"^socks4:(.*):(\d+)$", socks.PROXY_TYPE_SOCKS4),
  706.             (r"^http://(.*):(\d+)$", socks.PROXY_TYPE_HTTP),
  707.             (r"^(.*):(\d+)$", socks.PROXY_TYPE_SOCKS5),
  708.         ]:
  709.             m = re.match(pattern, args.proxy)
  710.             if m:
  711.                 socks.setdefaultproxy(proxy_type, m.group(1), int(m.group(2)))
  712.                 socket.socket = socks.socksocket
  713.                 proxy_valid = True
  714.                 break
  715.  
  716.         if not proxy_valid:
  717.             parser.error("invalid proxy, got `%s`" % args.proxy)
  718.  
  719.     # output directory
  720.     if not os.path.exists(args.directory):
  721.         os.makedirs(args.directory)
  722.  
  723.     if not os.path.isdir(args.directory):
  724.         parser.error("`%s` is not a directory" % args.directory)
  725.  
  726.     urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
  727.  
  728.     # fetch everything
  729.     sys.exit(
  730.         fetch_git(
  731.             args.url,
  732.             args.directory,
  733.             args.jobs,
  734.             args.retry,
  735.             args.timeout,
  736.             http_headers,
  737.         )
  738.     )
  739.  
  740.  
  741. if __name__ == "__main__":
  742.     main()
  743.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement