5ea25e6736430ab5b8439d11b2e9a837a803771b

Author
Septs <github@septs.pw>
Committer
GitHub <noreply@github.com>
Date

Message

Add `shell-proxy` plugin (#8692)

Diff

  1diff --git a/plugins/shell-proxy/README.md b/plugins/shell-proxy/README.md
  2new file mode 100644
  3index 0000000000000000000000000000000000000000..6f2cd13e0685cb7b8d41009557444aa561fdb9ac
  4--- /dev/null
  5+++ b/plugins/shell-proxy/README.md
  6@@ -0,0 +1,52 @@
  7+# Shell Proxy oh-my-zsh plugin
  8+
  9+This a pure user-space program, shell-proxy setter, written Python3 and Bash.
 10+
 11+100% only no side-effects, only effect **environment variables** and **aliases**
 12+
 13+## Key feature
 14+
 15+- Support Ubuntu, Archlinux, etc (Linux)
 16+- Support macOS
 17+- Support git via based-`$GIT_SSH`
 18+- Support ssh, sftp, scp, slogin and ssh-copy-id via based-`alias`
 19+- Built-in Auto-complete
 20+
 21+## Usage
 22+
 23+Method 1:
 24+
 25+`$DEFAULT_PROXY` is the proxy URL you will set
 26+
 27+Method 2:
 28+
 29+Write a program to `$HOME/.config/proxy` in the file.
 30+
 31+Example program:
 32+
 33+```bash
 34+#!/bin/bash
 35+# The file path: $HOME/.config/proxy
 36+if [[ "$OSTYPE" == "darwin"* ]]; then
 37+  echo "http://127.0.0.1:6152" # Surge Mac
 38+else
 39+  echo "http://127.0.0.1:8123" # polipo
 40+fi
 41+```
 42+
 43+Method 3:
 44+
 45+The working path of **Method 2** can be changed via `$CONFIG_PROXY`
 46+
 47+## Reference
 48+
 49+- `$GIT_SSH`: <https://www.git-scm.com/docs/git#Documentation/git.txt-codeGITSSHcode>
 50+- OpenSSH manual: <https://man.openbsd.org/ssh>
 51+
 52+## Maintainer
 53+
 54+- <https://github.com/septs>
 55+
 56+## The oh-my-zsh plugin (shell-proxy)
 57+
 58+Public Domain
 59diff --git a/plugins/shell-proxy/proxy.py b/plugins/shell-proxy/proxy.py
 60new file mode 100755
 61index 0000000000000000000000000000000000000000..2b62f6cb5363a756765e07d92626438163d4d2d5
 62--- /dev/null
 63+++ b/plugins/shell-proxy/proxy.py
 64@@ -0,0 +1,72 @@
 65+#!/usr/bin/env python3
 66+import os
 67+import sys
 68+from subprocess import check_output, list2cmdline
 69+
 70+cwd = os.path.dirname(__file__)
 71+ssh_agent = os.path.join(cwd, "ssh-agent.py")
 72+user_proxy = os.environ.get("CONFIG_PROXY", os.path.expandvars("$HOME/.config/proxy"))
 73+
 74+
 75+def get_http_proxy():
 76+    if "DEFAULT_PROXY" in os.environ:
 77+        return os.environ["DEFAULT_PROXY"]
 78+    if os.path.isfile(user_proxy):
 79+        return check_output(user_proxy).decode("utf-8").strip()
 80+    raise Exception("Not found, Proxy configuration")
 81+
 82+
 83+def make_proxies(url: str):
 84+    proxies = {"%s_PROXY" % _: url for _ in ("HTTP", "HTTPS", "FTP", "RSYNC", "ALL")}
 85+    proxies.update({name.lower(): value for (name, value) in proxies.items()})
 86+    proxies["GIT_SSH"] = ssh_agent
 87+    return proxies
 88+
 89+
 90+def merge(mapping: dict):
 91+    return ("%s=%s" % _ for _ in mapping.items())
 92+
 93+
 94+class CommandSet:
 95+    proxies = make_proxies(get_http_proxy())
 96+    aliases = {
 97+        _: "env NAME=%s %s" % (_, ssh_agent)
 98+        for _ in ("ssh", "sftp", "scp", "slogin", "ssh-copy-id")
 99+    }
100+
101+    def enable(self):
102+        cmdline("export", *merge(self.proxies))
103+        cmdline("alias", *merge(self.aliases))
104+
105+    def disable(self):
106+        cmdline("unset", *self.proxies.keys())
107+        cmdline("unalias", *self.aliases.keys())
108+
109+    def status(self):
110+        proxies = (
111+            "%11s = %s" % (name, os.environ[name])
112+            for name in self.proxies.keys()
113+            if name in os.environ
114+        )
115+        for _ in proxies:
116+            cmdline("echo", _)
117+
118+    def usage(self):
119+        cmdline("echo", "usage: proxy {enable,disable,status}")
120+        self.status()
121+
122+
123+def cmdline(*items):
124+    print(list2cmdline(items))
125+
126+
127+def main():
128+    command = CommandSet()
129+    if len(sys.argv) == 1:
130+        command.usage()
131+        sys.exit(-1)
132+    getattr(command, sys.argv[1], command.usage)()
133+
134+
135+if __name__ == "__main__":
136+    main()
137diff --git a/plugins/shell-proxy/shell-proxy.plugin.zsh b/plugins/shell-proxy/shell-proxy.plugin.zsh
138new file mode 100644
139index 0000000000000000000000000000000000000000..fd0e2fe79716294dd615292e7cd15c0df1ef1918
140--- /dev/null
141+++ b/plugins/shell-proxy/shell-proxy.plugin.zsh
142@@ -0,0 +1,16 @@
143+#!/usr/bin/bash
144+# shellcheck disable=SC1090
145+
146+__PROXY__="${0:A:h}/proxy.py"
147+
148+proxy() {
149+	source <("$__PROXY__" "$1")
150+}
151+
152+_proxy() {
153+	local -r commands=('enable' 'disable' 'status')
154+	compset -P '*,'
155+	compadd -S '' "${commands[@]}"
156+}
157+
158+compdef '_proxy' 'proxy'
159diff --git a/plugins/shell-proxy/ssh-agent.py b/plugins/shell-proxy/ssh-agent.py
160new file mode 100755
161index 0000000000000000000000000000000000000000..61cf84c0b8bfe67cd9a4465b9d70d23fcb7fa6c8
162--- /dev/null
163+++ b/plugins/shell-proxy/ssh-agent.py
164@@ -0,0 +1,16 @@
165+#!/usr/bin/env python3
166+import os
167+import subprocess
168+import sys
169+
170+ssh_proxy = os.path.join(os.path.dirname(__file__), "ssh-proxy.py")
171+
172+argv = [
173+    os.environ.get("NAME", "ssh"),
174+    "-o",
175+    "ProxyCommand={} %h %p".format(ssh_proxy),
176+    "-o",
177+    "Compression=yes",
178+]
179+
180+subprocess.call(argv + sys.argv[1:], env=os.environ)
181diff --git a/plugins/shell-proxy/ssh-proxy.py b/plugins/shell-proxy/ssh-proxy.py
182new file mode 100755
183index 0000000000000000000000000000000000000000..5efd5fd2184885d67e2f20caf681516bafe0e2f2
184--- /dev/null
185+++ b/plugins/shell-proxy/ssh-proxy.py
186@@ -0,0 +1,18 @@
187+#!/usr/bin/env python3
188+import os
189+import subprocess
190+import sys
191+import urllib.parse
192+
193+proxy = next(os.environ[_] for _ in ("HTTP_PROXY", "HTTPS_PROXY") if _ in os.environ)
194+argv = [
195+    "nc",
196+    "-X",
197+    "connect",
198+    "-x",
199+    urllib.parse.urlparse(proxy).netloc,  # proxy-host:proxy-port
200+    sys.argv[1],  # host
201+    sys.argv[2],  # port
202+]
203+
204+subprocess.call(argv)