diff --git a/lib/python/nullroute/__init__.py b/lib/python/nullroute/__init__.py index 321931896..51fbac365 100644 --- a/lib/python/nullroute/__init__.py +++ b/lib/python/nullroute/__init__.py @@ -1,11 +1,11 @@ import sys def warn(*args): - print("\033[1;33mwarning:\033[m", *args, file=sys.stderr) + print("\033[1;33mwarning:\033[m", *args, file=sys.stderr) def err(*args): - print("\033[1;31merror:\033[m", *args, file=sys.stderr) + print("\033[1;31merror:\033[m", *args, file=sys.stderr) def die(*args): - err(*args) - sys.exit(1) + err(*args) + sys.exit(1) diff --git a/lib/python/nullroute/authorized_keys.py b/lib/python/nullroute/authorized_keys.py index 8f8ab8d9a..9633c3f26 100644 --- a/lib/python/nullroute/authorized_keys.py +++ b/lib/python/nullroute/authorized_keys.py @@ -1,9 +1,5 @@ -#!/usr/bin/env python +# parser for OpenSSH authorized_keys files # vim: ft=python - -# State-machine-based parser for OpenSSH authorized_keys files. -# (c) 2011 Mantas M. -# Released under WTFPL v2 # # for line in open("authorized_keys"): # if line and not line.startswith("#"): @@ -13,156 +9,156 @@ import hashlib class PublicKeyOptions(list): - def __str__(self): - o = [] - for k, v in self: - if v is True: - o.append(k) - else: - o.append("%s=%s" % (k, v)) - return ",".join(o) - - @classmethod - def parse(klass, text): - keys = [] - values = [] - current = "" - state = "key" - - for char in text: - if state == "key": - if char == ",": - keys.append(current) - values.append(True) - current = "" - elif char == "=": - keys.append(current) - current = "" - state = "value" - else: - current += char - elif state == "value": - if char == ",": - values.append(current) - current = "" - state = "key" - elif char == "\"": - current += char - state = "value dquote" - else: - current += char - elif state == "value dquote": - if char == "\"": - current += char - state = "value" - elif char == "\\": - current += char - state = "value dquote escape" - else: - current += char - elif state == "value dquote escape": - current += char - state = "value dquote" - - if current: - if state == "key": - keys.append(current) - values.append(True) - else: - values.append(current) - - return klass(zip(keys, values)) + def __str__(self): + o = [] + for k, v in self: + if v is True: + o.append(k) + else: + o.append("%s=%s" % (k, v)) + return ",".join(o) + + @classmethod + def parse(klass, text): + keys = [] + values = [] + current = "" + state = "key" + + for char in text: + if state == "key": + if char == ",": + keys.append(current) + values.append(True) + current = "" + elif char == "=": + keys.append(current) + current = "" + state = "value" + else: + current += char + elif state == "value": + if char == ",": + values.append(current) + current = "" + state = "key" + elif char == "\"": + current += char + state = "value dquote" + else: + current += char + elif state == "value dquote": + if char == "\"": + current += char + state = "value" + elif char == "\\": + current += char + state = "value dquote escape" + else: + current += char + elif state == "value dquote escape": + current += char + state = "value dquote" + + if current: + if state == "key": + keys.append(current) + values.append(True) + else: + values.append(current) + + return klass(zip(keys, values)) class PublicKey(object): - def __init__(self, line=None): - if line: - tokens = self.parse(line) - else: - tokens = ["", None, None, None] - - self.prefix, self.algo, self.blob, self.comment = tokens - - self.options = PublicKeyOptions.parse(self.prefix) - - def __repr__(self): - return "" % \ - (self.prefix, self.algo, self.comment) - - def __str__(self): - options = self.options - blob = base64.b64encode(self.blob).decode("utf-8") - comment = self.comment - k = [self.algo, blob] - if len(options): - k.insert(0, str(options)) - if len(comment): - k.append(comment) - return " ".join(k) - - def fingerprint(self, alg=None, hex=False): - if alg is None: - alg = hashlib.md5 - m = alg() - m.update(self.blob) - return m.hexdigest() if hex else m.digest() - - @classmethod - def parse(self, line): - tokens = [] - current = "" - state = "normal" - - for char in line: - old = state - if state == "normal": - if char in " \t": - tokens.append(current) - current = "" - elif char == "\"": - current += char - state = "dquote" - else: - current += char - elif state == "dquote": - if char == "\"": - current += char - state = "normal" - elif char == "\\": - current += char - state = "dquote escape" - else: - current += char - elif state == "dquote escape": - current += char - state = "dquote" - - if current: - tokens.append(current) - - if tokens[0] in {"ssh-rsa", "ssh-dss", "ecdsa-sha2-nistp256", - "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp521"}: - prefix = "" - else: - prefix = tokens.pop(0) - algo = tokens[0] - blob = tokens[1] - blob = base64.b64decode(blob.encode("utf-8")) - comment = " ".join(tokens[2:]) - - return prefix, algo, blob, comment + def __init__(self, line=None): + if line: + tokens = self.parse(line) + else: + tokens = ["", None, None, None] + + self.prefix, self.algo, self.blob, self.comment = tokens + + self.options = PublicKeyOptions.parse(self.prefix) + + def __repr__(self): + return "" % \ + (self.prefix, self.algo, self.comment) + + def __str__(self): + options = self.options + blob = base64.b64encode(self.blob).decode("utf-8") + comment = self.comment + k = [self.algo, blob] + if len(options): + k.insert(0, str(options)) + if len(comment): + k.append(comment) + return " ".join(k) + + def fingerprint(self, alg=None, hex=False): + if alg is None: + alg = hashlib.md5 + m = alg() + m.update(self.blob) + return m.hexdigest() if hex else m.digest() + + @classmethod + def parse(self, line): + tokens = [] + current = "" + state = "normal" + + for char in line: + old = state + if state == "normal": + if char in " \t": + tokens.append(current) + current = "" + elif char == "\"": + current += char + state = "dquote" + else: + current += char + elif state == "dquote": + if char == "\"": + current += char + state = "normal" + elif char == "\\": + current += char + state = "dquote escape" + else: + current += char + elif state == "dquote escape": + current += char + state = "dquote" + + if current: + tokens.append(current) + + if tokens[0] in {"ssh-rsa", "ssh-dss", "ecdsa-sha2-nistp256", + "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp521"}: + prefix = "" + else: + prefix = tokens.pop(0) + algo = tokens[0] + blob = tokens[1] + blob = base64.b64decode(blob.encode("utf-8")) + comment = " ".join(tokens[2:]) + + return prefix, algo, blob, comment if __name__ == "__main__": - import os - - path = os.path.expanduser("~/.ssh/authorized_keys") - - for line in open(path, "r"): - line = line.strip() - if line and not line.startswith("#"): - key = PublicKey(line) - print("key = %r" % key) - print("prefix = %r" % key.prefix) - print("algo = %r" % key.algo) - print("comment = %r" % key.comment) - print("options = %r" % key.options) - print() + import os + + path = os.path.expanduser("~/.ssh/authorized_keys") + + for line in open(path, "r"): + line = line.strip() + if line and not line.startswith("#"): + key = PublicKey(line) + print("key = %r" % key) + print("prefix = %r" % key.prefix) + print("algo = %r" % key.algo) + print("comment = %r" % key.comment) + print("options = %r" % key.options) + print() diff --git a/lib/python/nullroute/clipboard.py b/lib/python/nullroute/clipboard.py index 91c719b48..5c070f82a 100644 --- a/lib/python/nullroute/clipboard.py +++ b/lib/python/nullroute/clipboard.py @@ -1,30 +1,30 @@ import sys def get(): - if sys.platform == "win32": - import win32clipboard as clip - clip.OpenClipboard() - # TODO: what type does this return? - data = clip.GetClipboardData(clip.CF_UNICODETEXT) - #print("clipboard.get =", repr(data)) - clip.CloseClipboard() - return data - else: - raise RuntimeError("Unsupported platform") + if sys.platform == "win32": + import win32clipboard as clip + clip.OpenClipboard() + # TODO: what type does this return? + data = clip.GetClipboardData(clip.CF_UNICODETEXT) + #print("clipboard.get =", repr(data)) + clip.CloseClipboard() + return data + else: + raise RuntimeError("Unsupported platform") def put(data): - if sys.platform == "win32": - import win32clipboard as clip - clip.OpenClipboard() - clip.EmptyClipboard() - clip.SetClipboardText(data, clip.CF_UNICODETEXT) - clip.CloseClipboard() - elif sys.platform.startswith("linux"): - import subprocess - proc = subprocess.Popen(("xsel", "-i", "-b", "-l", "/dev/null"), - stdin=subprocess.PIPE) - proc.stdin.write(data.encode("utf-8")) - proc.stdin.close() - proc.wait() - else: - raise RuntimeError("Unsupported platform") + if sys.platform == "win32": + import win32clipboard as clip + clip.OpenClipboard() + clip.EmptyClipboard() + clip.SetClipboardText(data, clip.CF_UNICODETEXT) + clip.CloseClipboard() + elif sys.platform.startswith("linux"): + import subprocess + proc = subprocess.Popen(("xsel", "-i", "-b", "-l", "/dev/null"), + stdin=subprocess.PIPE) + proc.stdin.write(data.encode("utf-8")) + proc.stdin.close() + proc.wait() + else: + raise RuntimeError("Unsupported platform") diff --git a/lib/python/nullroute/irc.py b/lib/python/nullroute/irc.py index 1216095d6..00995a38f 100644 --- a/lib/python/nullroute/irc.py +++ b/lib/python/nullroute/irc.py @@ -1,269 +1,266 @@ -#!/usr/bin/env python from __future__ import (print_function, unicode_literals) import base64 import socket import re class InvalidPrefixError(Exception): - pass + pass class Prefix(object): - def __init__(self, nick=None, user=None, host=None, is_server=False): - self.nick = nick - self.user = user - self.host = host - self.is_server = is_server - - @classmethod - def parse(cls, prefix): - if len(prefix) == 0: - return None - - dpos = prefix.find(".") + 1 - upos = prefix.find("!") + 1 - hpos = prefix.find("@", upos) + 1 - - if upos == 1 or hpos == 1: - return None - if 0 < dpos < min(upos, hpos): - return None - - self = cls() - if upos > 0: - self.nick = prefix[:upos-1] - if hpos > 0: - self.user = prefix[upos:hpos-1] - self.host = prefix[hpos:] - else: - self.user = prefix[upos:] - elif hpos > 0: - self.nick = prefix[:hpos-1] - self.host = prefix[hpos:] - elif dpos > 0: - self.host = prefix - self.is_server = True - else: - self.nick = prefix - - return self - - def unparse(self): - if not (self.nick is None or self.user is None or self.host is None): - return self.nick + "!" + self.user + "@" + self.host - elif self.nick: - return self.nick - elif self.host: - return self.host - else: - return None - - def __str__(self): - if not (self.nick is None or self.user is None or self.host is None): - return "%s!%s@%s" % (self.nick, self.user, self.host) - elif self.nick: - return self.nick - elif self.host: - return self.host - else: - return "(empty)" - - def __repr__(self): - return "" % (self.nick, self.user, self.host) - - def to_a(self): - return [self.nick, self.user, self.host, self.is_server] + def __init__(self, nick=None, user=None, host=None, is_server=False): + self.nick = nick + self.user = user + self.host = host + self.is_server = is_server + + @classmethod + def parse(cls, prefix): + if len(prefix) == 0: + return None + + dpos = prefix.find(".") + 1 + upos = prefix.find("!") + 1 + hpos = prefix.find("@", upos) + 1 + + if upos == 1 or hpos == 1: + return None + if 0 < dpos < min(upos, hpos): + return None + + self = cls() + if upos > 0: + self.nick = prefix[:upos-1] + if hpos > 0: + self.user = prefix[upos:hpos-1] + self.host = prefix[hpos:] + else: + self.user = prefix[upos:] + elif hpos > 0: + self.nick = prefix[:hpos-1] + self.host = prefix[hpos:] + elif dpos > 0: + self.host = prefix + self.is_server = True + else: + self.nick = prefix + + return self + + def unparse(self): + if not (self.nick is None or self.user is None or self.host is None): + return self.nick + "!" + self.user + "@" + self.host + elif self.nick: + return self.nick + elif self.host: + return self.host + else: + return None + + def __str__(self): + if not (self.nick is None or self.user is None or self.host is None): + return "%s!%s@%s" % (self.nick, self.user, self.host) + elif self.nick: + return self.nick + elif self.host: + return self.host + else: + return "(empty)" + + def __repr__(self): + return "" % (self.nick, self.user, self.host) + + def to_a(self): + return [self.nick, self.user, self.host, self.is_server] class Line(object): - """ - An IRC protocol line. - """ - def __init__(self, tags=None, prefix=None, cmd=None, args=None): - self.tags = tags or {} - self.prefix = prefix - self.cmd = cmd - self.args = args or [] - - @classmethod - def split(cls, line): - """ - Split an IRC protocol line into tokens as defined in RFC 1459 - and the IRCv3 message-tags extension. - """ - - line = line.decode("utf-8", "replace") - line = line.rstrip("\r\n").split(" ") - i, n = 0, len(line) - parv = [] - - while i < n and line[i] == "": - i += 1 - - if i < n and line[i].startswith("@"): - parv.append(line[i]) - i += 1 - while i < n and line[i] == "": - i += 1 - - if i < n and line[i].startswith(":"): - parv.append(line[i]) - i += 1 - while i < n and line[i] == "": - i += 1 - - while i < n: - if line[i].startswith(":"): - break - elif line[i] != "": - parv.append(line[i]) - i += 1 - - if i < n: - trailing = " ".join(line[i:]) - parv.append(trailing[1:]) - - return parv - - @classmethod - def parse(cls, line, parse_prefix=True): - """ - Parse an IRC protocol line into a Line object consisting of - tags, prefix, command, and arguments. - """ - - line = line.decode("utf-8", "replace") - parv = line.rstrip("\r\n").split(" ") - i, n = 0, len(parv) - self = cls() - - while i < n and parv[i] == "": - i += 1 - - if i < n and parv[i].startswith("@"): - tags = parv[i][1:] - i += 1 - while i < n and parv[i] == "": - i += 1 - - self.tags = dict() - for item in tags.split(";"): - if "=" in item: - k, v = item.split("=", 1) - else: - k, v = item, True - self.tags[k] = v - - if i < n and parv[i].startswith(":"): - prefix = parv[i][1:] - i += 1 - while i < n and parv[i] == "": - i += 1 - - if parse_prefix: - self.prefix = Prefix.parse(prefix) - else: - self.prefix = prefix - - if i < n: - self.cmd = parv[i].upper() - - while i < n: - if parv[i].startswith(":"): - trailing = " ".join(parv[i:]) - self.args.append(trailing[1:]) - break - elif parv[i] != "": - self.args.append(parv[i]) - i += 1 - - return self - - @classmethod - def join(cls, argv): - i, n = 0, len(argv) - - if i < n and argv[i].startswith("@"): - if " " in argv[i]: - raise ValueError("Argument %d contains spaces: %r" % (i, argv[i])) - i += 1 - - if i < n and " " in argv[i]: - raise ValueError("Argument %d contains spaces: %r" % (i, argv[i])) - - if i < n and argv[i].startswith(":"): - if " " in argv[i]: - raise ValueError("Argument %d contains spaces: %r" % (i, argv[i])) - i += 1 - - while i < n-1: - if not argv[i]: - raise ValueError("Argument %d is empty: %r" % (i, argv[i])) - elif argv[i].startswith(":"): - raise ValueError("Argument %d starts with ':': %r" % (i, argv[i])) - elif " " in argv[i]: - raise ValueError("Argument %d contains spaces: %r" % (i, argv[i])) - i += 1 - - parv = argv[:i] - - if i < n: - if not argv[i] or argv[i].startswith(":") or " " in argv[i]: - parv.append(":%s" % argv[i]) - else: - parv.append(argv[i]) - - return " ".join(parv) - - def unparse(self): - parv = [] - - if self.tags: - tags = [k if v is True else k + b"=" + v - for k, v in self.tags.items()] - parv.append("@" + b",".join(tags)) - - if self.prefix: - parv.append(":" + self.prefix.unparse()) - - parv.append(self.cmd) - - parv.extend(self.args) - - return self.join(parv) - - def __repr__(self): - return "" % ( - self.tags, self.prefix, - self.cmd, self.args) + """ + An IRC protocol line. + """ + def __init__(self, tags=None, prefix=None, cmd=None, args=None): + self.tags = tags or {} + self.prefix = prefix + self.cmd = cmd + self.args = args or [] + + @classmethod + def split(cls, line): + """ + Split an IRC protocol line into tokens as defined in RFC 1459 + and the IRCv3 message-tags extension. + """ + + line = line.decode("utf-8", "replace") + line = line.rstrip("\r\n").split(" ") + i, n = 0, len(line) + parv = [] + + while i < n and line[i] == "": + i += 1 + + if i < n and line[i].startswith("@"): + parv.append(line[i]) + i += 1 + while i < n and line[i] == "": + i += 1 + + if i < n and line[i].startswith(":"): + parv.append(line[i]) + i += 1 + while i < n and line[i] == "": + i += 1 + + while i < n: + if line[i].startswith(":"): + break + elif line[i] != "": + parv.append(line[i]) + i += 1 + + if i < n: + trailing = " ".join(line[i:]) + parv.append(trailing[1:]) + + return parv + + @classmethod + def parse(cls, line, parse_prefix=True): + """ + Parse an IRC protocol line into a Line object consisting of + tags, prefix, command, and arguments. + """ + + line = line.decode("utf-8", "replace") + parv = line.rstrip("\r\n").split(" ") + i, n = 0, len(parv) + self = cls() + + while i < n and parv[i] == "": + i += 1 + + if i < n and parv[i].startswith("@"): + tags = parv[i][1:] + i += 1 + while i < n and parv[i] == "": + i += 1 + + self.tags = dict() + for item in tags.split(";"): + if "=" in item: + k, v = item.split("=", 1) + else: + k, v = item, True + self.tags[k] = v + + if i < n and parv[i].startswith(":"): + prefix = parv[i][1:] + i += 1 + while i < n and parv[i] == "": + i += 1 + + if parse_prefix: + self.prefix = Prefix.parse(prefix) + else: + self.prefix = prefix + + if i < n: + self.cmd = parv[i].upper() + + while i < n: + if parv[i].startswith(":"): + trailing = " ".join(parv[i:]) + self.args.append(trailing[1:]) + break + elif parv[i] != "": + self.args.append(parv[i]) + i += 1 + + return self + + @classmethod + def join(cls, argv): + i, n = 0, len(argv) + + if i < n and argv[i].startswith("@"): + if " " in argv[i]: + raise ValueError("Argument %d contains spaces: %r" % (i, argv[i])) + i += 1 + + if i < n and " " in argv[i]: + raise ValueError("Argument %d contains spaces: %r" % (i, argv[i])) + + if i < n and argv[i].startswith(":"): + if " " in argv[i]: + raise ValueError("Argument %d contains spaces: %r" % (i, argv[i])) + i += 1 + + while i < n-1: + if not argv[i]: + raise ValueError("Argument %d is empty: %r" % (i, argv[i])) + elif argv[i].startswith(":"): + raise ValueError("Argument %d starts with ':': %r" % (i, argv[i])) + elif " " in argv[i]: + raise ValueError("Argument %d contains spaces: %r" % (i, argv[i])) + i += 1 + + parv = argv[:i] + + if i < n: + if not argv[i] or argv[i].startswith(":") or " " in argv[i]: + parv.append(":%s" % argv[i]) + else: + parv.append(argv[i]) + + return " ".join(parv) + + def unparse(self): + parv = [] + + if self.tags: + tags = [k if v is True else k + b"=" + v + for k, v in self.tags.items()] + parv.append("@" + b",".join(tags)) + + if self.prefix: + parv.append(":" + self.prefix.unparse()) + + parv.append(self.cmd) + + parv.extend(self.args) + + return self.join(parv) + + def __repr__(self): + return "" % ( + self.tags, self.prefix, + self.cmd, self.args) class Connection(object): - def __init__(self): - self.host = None - self.port = None - self.ai = None - self._fd = None - self._file = None - - def connect(self, host, port, ssl=False): - self.ai = socket.getaddrinfo(host, str(port), 0, socket.SOCK_STREAM) - print(repr(self.ai)) - for af, proto, _, cname, addr in self.ai: - self._fd = socket.socket(af, proto) - self._fd.connect(addr) - break - import io - self._fi = self._fd.makefile("rwb") - - def writeraw(self, buf): - self._fi.write(buf+b"\r\n") - self._fi.flush() - - def readraw(self): - return self._fi.readline() - - def write(self, *args): - self.writeraw(Line.join(args)) - - def read(self): - return Line.parse(self.readraw()) - -# vim: ts=4:sw=4 + def __init__(self): + self.host = None + self.port = None + self.ai = None + self._fd = None + self._file = None + + def connect(self, host, port, ssl=False): + self.ai = socket.getaddrinfo(host, str(port), 0, socket.SOCK_STREAM) + print(repr(self.ai)) + for af, proto, _, cname, addr in self.ai: + self._fd = socket.socket(af, proto) + self._fd.connect(addr) + break + import io + self._fi = self._fd.makefile("rwb") + + def writeraw(self, buf): + self._fi.write(buf+b"\r\n") + self._fi.flush() + + def readraw(self): + return self._fi.readline() + + def write(self, *args): + self.writeraw(Line.join(args)) + + def read(self): + return Line.parse(self.readraw()) diff --git a/lib/python/nullroute/mp3tags.py b/lib/python/nullroute/mp3tags.py index da30725f8..4e9fa219d 100644 --- a/lib/python/nullroute/mp3tags.py +++ b/lib/python/nullroute/mp3tags.py @@ -1,157 +1,157 @@ import mutagen def rva_from_string(gain, peak): - rg_gain = float(gain[0].split(' ')[0]) - rg_peak = float(peak[0]) - return mutagen.id3.RVA2(desc=u'track', channel=1, gain=rg_gain, peak=rg_peak) + rg_gain = float(gain[0].split(' ')[0]) + rg_peak = float(peak[0]) + return mutagen.id3.RVA2(desc=u'track', channel=1, gain=rg_gain, peak=rg_peak) def rva_to_string(rva): - rg_gain = rva._raw_gain or "%.2f dB" % rva.gain - rg_peak = rva._raw_peak or "%.2f dB" % rva.peak - return (rg_gain, rg_peak) + rg_gain = rva._raw_gain or "%.2f dB" % rva.gain + rg_peak = rva._raw_peak or "%.2f dB" % rva.peak + return (rg_gain, rg_peak) def rva_to_soundcheck(rva): - # http://projects.robinbowes.com/flac2mp3/trac/ticket/30#comment:7 - # [-1]: prefixed with a space - # [0, 1]: volume adjustment in 1/1000 W per dBm - # [2, 3]: volume adjustment in 1/2500 W per dBm - # [4, 5]: unsure (identical for same song when volumes differ) - # [6, 7]: peak (max sample) as absolute value: 0x7FFF for 16-bit samples - # [8, 9]: same as [4, 5] - gain2sc = lambda gain, base: u"%08X" % min(round((10 ** (-gain/10)) * base), 65534) - - sc = [ - u"", - # 1/1000 W per dBm - gain2sc(rva.gain, 1000), - gain2sc(rva.gain, 1000), - # 1/2500 W per dBm - gain2sc(rva.gain, 2500), - gain2sc(rva.gain, 2500), - u"00024CA8", - u"00024CA8", - u"00007FFF", - u"00007FFF", - u"00024CA8", - u"00024CA8", - ] - - return u" ".join(sc) + # http://projects.robinbowes.com/flac2mp3/trac/ticket/30#comment:7 + # [-1]: prefixed with a space + # [0, 1]: volume adjustment in 1/1000 W per dBm + # [2, 3]: volume adjustment in 1/2500 W per dBm + # [4, 5]: unsure (identical for same song when volumes differ) + # [6, 7]: peak (max sample) as absolute value: 0x7FFF for 16-bit samples + # [8, 9]: same as [4, 5] + gain2sc = lambda gain, base: u"%08X" % min(round((10 ** (-gain/10)) * base), 65534) + + sc = [ + u"", + # 1/1000 W per dBm + gain2sc(rva.gain, 1000), + gain2sc(rva.gain, 1000), + # 1/2500 W per dBm + gain2sc(rva.gain, 2500), + gain2sc(rva.gain, 2500), + u"00024CA8", + u"00024CA8", + u"00007FFF", + u"00007FFF", + u"00024CA8", + u"00024CA8", + ] + + return u" ".join(sc) class GainValue(object): - MODES = {'track', 'album'} - - def __init__(self, mode=u'track'): - if mode not in self.MODES: - raise ValueError("mode must be one of %r" % self.MODES) - - self._mode = unicode(mode) - - self.gain = None - self.peak = 1.0 - self._raw_gain = None - self._raw_peak = None - - def __repr__(self): - return "" % (self._mode, self.gain, self.peak) - - @property - def mode(self): - return self._mode - - @mode.setter - def mode(self, value): - if value not in self.MODES: - raise ValueError("mode must be one of %r" % self.MODES) - - self._mode = unicode(value) - - @classmethod - def from_rva2(self, mode, frame): - gv = self(mode) - gv.gain = frame.gain - gv.peak = frame.peak - return gv - - @classmethod - def from_string(self, mode, gain, peak): - rg_gain = float(gain[0].split(' ')[0]) - rg_peak = float(peak[0].split(' ')[0]) - - gv = self(mode) - gv._raw_gain = gain - gv._raw_peak = peak - gv.gain = rg_gain - gv.peak = rg_peak - return gv - - def to_string(self): - rg_gain = self._raw_gain or "%.2f dB" % self.gain - rg_peak = self._raw_peak or "%.2f dB" % self.peak - return (rg_gain, rg_peak) - - def to_rva2(self): - return mutagen.id3.RVA2(desc=self._mode, channel=1, gain=self.gain, peak=self.peak) - - def to_soundcheck(self): - return rva_to_soundcheck(self) - - @classmethod - def import_tag(self, tag, mode): - if mode not in self.MODES: - raise ValueError("mode must be one of %r" % self.MODES) - - if (u'RVA2:%s' % mode) in tag: - # ID3v2.4 RVA2 - #print "Found ID3v2.4 RVA2 frame" - return self.from_rva2(mode, tag[u'RVA2:%s' % mode]) - elif (u'TXXX:replaygain_%s_gain' % mode) in tag: - # ID3v2 foobar2000 - #print "Found ID3v2 foobar2000 tag" - return self.from_string(mode, - tag[u'TXXX:replaygain_%s_gain' % mode], - tag[u'TXXX:replaygain_%s_peak' % mode]) - elif ('----:com.apple.iTunes:replaygain_%s_gain' % mode) in tag: - # MP4 foobar2000 - #print "Found MP4 foobar2000 tag" - return self.from_string(mode, - tag['----:com.apple.iTunes:replaygain_%s_gain' % mode], - tag['----:com.apple.iTunes:replaygain_%s_peak' % mode]) - elif ('replaygain_%s_gain' % mode) in tag: - # FLAC - #print "Found FLAC tag" - return self.from_string(mode, - tag['replaygain_%s_gain' % mode], - tag['replaygain_%s_peak' % mode]) - else: - return None - - def export_id3(self, tag): - tag[u'RVA2:%s' % self._mode] = self.to_rva2() - - rg_gain, rg_peak = self.to_string() - tx_gain = mutagen.id3.TXXX(desc=u'replaygain_%s_gain' % self._mode, - encoding=1, text=[rg_gain]) - tx_peak = mutagen.id3.TXXX(desc=u'replaygain_%s_peak' % self._mode, - encoding=1, text=[rg_peak]) - tag[u'TXXX:'+tx_gain.desc] = tx_gain - tag[u'TXXX:'+tx_peak.desc] = tx_peak - - if self._mode == 'track': - sc_raw = self.to_soundcheck() - sc_norm = mutagen.id3.COMM(desc=u'iTunNORM', lang='eng', - encoding=0, text=[sc_raw]) - tag[u"COMM:%s:'%s'" % (sc_norm.desc, sc_norm.lang)] = sc_norm - - def export_mp4(self, tag): - #print "Adding MP4 foobar2000 tag" - rg_gain, rg_peak = self.to_string() - tag['----:com.apple.iTunes:replaygain_%s_gain' % self._mode] = rg_gain - tag['----:com.apple.iTunes:replaygain_%s_peak' % self._mode] = rg_peak - - def export_flac(self, tag): - #print "Adding FLAC tag" - rg_gain, rg_peak = self.to_string() - tag['replaygain_%s_gain' % self._mode] = rg_gain - tag['replaygain_%s_peak' % self._mode] = rg_peak + MODES = {'track', 'album'} + + def __init__(self, mode=u'track'): + if mode not in self.MODES: + raise ValueError("mode must be one of %r" % self.MODES) + + self._mode = unicode(mode) + + self.gain = None + self.peak = 1.0 + self._raw_gain = None + self._raw_peak = None + + def __repr__(self): + return "" % (self._mode, self.gain, self.peak) + + @property + def mode(self): + return self._mode + + @mode.setter + def mode(self, value): + if value not in self.MODES: + raise ValueError("mode must be one of %r" % self.MODES) + + self._mode = unicode(value) + + @classmethod + def from_rva2(self, mode, frame): + gv = self(mode) + gv.gain = frame.gain + gv.peak = frame.peak + return gv + + @classmethod + def from_string(self, mode, gain, peak): + rg_gain = float(gain[0].split(' ')[0]) + rg_peak = float(peak[0].split(' ')[0]) + + gv = self(mode) + gv._raw_gain = gain + gv._raw_peak = peak + gv.gain = rg_gain + gv.peak = rg_peak + return gv + + def to_string(self): + rg_gain = self._raw_gain or "%.2f dB" % self.gain + rg_peak = self._raw_peak or "%.2f dB" % self.peak + return (rg_gain, rg_peak) + + def to_rva2(self): + return mutagen.id3.RVA2(desc=self._mode, channel=1, gain=self.gain, peak=self.peak) + + def to_soundcheck(self): + return rva_to_soundcheck(self) + + @classmethod + def import_tag(self, tag, mode): + if mode not in self.MODES: + raise ValueError("mode must be one of %r" % self.MODES) + + if (u'RVA2:%s' % mode) in tag: + # ID3v2.4 RVA2 + #print "Found ID3v2.4 RVA2 frame" + return self.from_rva2(mode, tag[u'RVA2:%s' % mode]) + elif (u'TXXX:replaygain_%s_gain' % mode) in tag: + # ID3v2 foobar2000 + #print "Found ID3v2 foobar2000 tag" + return self.from_string(mode, + tag[u'TXXX:replaygain_%s_gain' % mode], + tag[u'TXXX:replaygain_%s_peak' % mode]) + elif ('----:com.apple.iTunes:replaygain_%s_gain' % mode) in tag: + # MP4 foobar2000 + #print "Found MP4 foobar2000 tag" + return self.from_string(mode, + tag['----:com.apple.iTunes:replaygain_%s_gain' % mode], + tag['----:com.apple.iTunes:replaygain_%s_peak' % mode]) + elif ('replaygain_%s_gain' % mode) in tag: + # FLAC + #print "Found FLAC tag" + return self.from_string(mode, + tag['replaygain_%s_gain' % mode], + tag['replaygain_%s_peak' % mode]) + else: + return None + + def export_id3(self, tag): + tag[u'RVA2:%s' % self._mode] = self.to_rva2() + + rg_gain, rg_peak = self.to_string() + tx_gain = mutagen.id3.TXXX(desc=u'replaygain_%s_gain' % self._mode, + encoding=1, text=[rg_gain]) + tx_peak = mutagen.id3.TXXX(desc=u'replaygain_%s_peak' % self._mode, + encoding=1, text=[rg_peak]) + tag[u'TXXX:'+tx_gain.desc] = tx_gain + tag[u'TXXX:'+tx_peak.desc] = tx_peak + + if self._mode == 'track': + sc_raw = self.to_soundcheck() + sc_norm = mutagen.id3.COMM(desc=u'iTunNORM', lang='eng', + encoding=0, text=[sc_raw]) + tag[u"COMM:%s:'%s'" % (sc_norm.desc, sc_norm.lang)] = sc_norm + + def export_mp4(self, tag): + #print "Adding MP4 foobar2000 tag" + rg_gain, rg_peak = self.to_string() + tag['----:com.apple.iTunes:replaygain_%s_gain' % self._mode] = rg_gain + tag['----:com.apple.iTunes:replaygain_%s_peak' % self._mode] = rg_peak + + def export_flac(self, tag): + #print "Adding FLAC tag" + rg_gain, rg_peak = self.to_string() + tag['replaygain_%s_gain' % self._mode] = rg_gain + tag['replaygain_%s_peak' % self._mode] = rg_peak diff --git a/lib/python/nullroute/sexp.py b/lib/python/nullroute/sexp.py index 3ac79e173..d3e29a48e 100644 --- a/lib/python/nullroute/sexp.py +++ b/lib/python/nullroute/sexp.py @@ -1,5 +1,3 @@ -#!python -# # S-exp parser and dumper # (c) 2011 Mantas M. # @@ -16,396 +14,396 @@ import base64 from io import BytesIO -ALPHA = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" -DIGITS = b"0123456789" -WHITESPACE = b" \t\v\f\r\n" -PSEUDO_ALPHA = b"-./_:*+=" -PUNCTUATION = b'()[]{}|#"&\\' -#VERBATIM = b"!%^~;',<>?" # Rivest's spec uses these -VERBATIM = b"!%^~'<>" # nettle's sexp-conv is more permissive? - -TOKEN_CHARS = DIGITS + ALPHA + PSEUDO_ALPHA - -HEX_DIGITS = b"0123456789ABCDEFabcdef" -B64_DIGITS = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" - -PRINTABLE_CHARS = bytes(range(0x20, 0x80)) -ESCAPE_CHARS = b"\b\t\v\n\f\r\\" - -ESCAPE_CHARS_TB = { - b"\b": b"b", - b"\t": b"t", - b"\v": b"v", - b"\n": b"n", - b"\f": b"f", - b"\r": b"r", - b"\\": b"\\", +ALPHA = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +DIGITS = b"0123456789" +WHITESPACE = b" \t\v\f\r\n" +PSEUDO_ALPHA = b"-./_:*+=" +PUNCTUATION = b'()[]{}|#"&\\' +#VERBATIM = b"!%^~;',<>?" # Rivest's spec uses these +VERBATIM = b"!%^~'<>" # nettle's sexp-conv is more permissive? + +TOKEN_CHARS = DIGITS + ALPHA + PSEUDO_ALPHA + +HEX_DIGITS = b"0123456789ABCDEFabcdef" +B64_DIGITS = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" + +PRINTABLE_CHARS = bytes(range(0x20, 0x80)) +ESCAPE_CHARS = b"\b\t\v\n\f\r\\" + +ESCAPE_CHARS_TB = { + b"\b": b"b", + b"\t": b"t", + b"\v": b"v", + b"\n": b"n", + b"\f": b"f", + b"\r": b"r", + b"\\": b"\\", } class SexpParser(object): - def __init__(self, buf): - self.bytesize = 8 - self.bits = 0 - self.nBits = 0 - self.buf = buf if hasattr(buf, "read") else BytesIO(buf) - self.char = "" - self.advance() - - def __iter__(self): - return self - - def __next__(self): - return self.next() - - def next(self): - obj = self.scan_object() - if obj: - return obj - else: - raise StopIteration - - @property - def pos(self): - return self.buf.tell() - - def advance(self): - """Get the next byte in the input stream. - - Will read as many input bytes as needed from Base64 or hex areas. - """ - while True: - self.last = self.char - self.char = self.buf.read(1) - if not self.char: - self.bytesize = 8 - self.char = None - return self.char - - if self.char is None: - return self.char - elif (self.bytesize == 6 and self.char in b"|}") \ - or (self.bytesize == 4 and self.char == b"#"): - if self.nBits and (1 << self.nBits)-1 & self.bits: - raise IOError("%d-bit region ended with %d unused bits at %d" % - (self.bytesize, self.nBits, self.pos)) - self.bytesize = 8 - return self.char - elif self.bytesize != 8 and self.char in WHITESPACE: - # ignore whitespace in hex/base64 regions - pass - elif self.bytesize == 6 and self.char == b"=": - # Base64 padding - self.nBits -= 2 - elif self.bytesize == 8: - return self.char - elif self.bytesize < 8: - self.bits <<= self.bytesize - self.nBits += self.bytesize - if self.bytesize == 6 and self.char in B64_DIGITS: - self.bits |= B64_DIGITS.index(self.char) - elif self.bytesize == 4 and self.char in HEX_DIGITS: - self.bits |= int(self.char, 16) - else: - raise IOError("char %r found in %d-bit region" % - (self.char, self.bytesize)) - - if self.nBits >= 8: - self.nBits -= 8 - byte = (self.bits >> self.nBits) & 0xFF - self.bits &= (1 << self.nBits)-1 - self.char = bytes([byte]) - return self.char - - def skip_whitespace(self): - while self.char: - if self.char in WHITESPACE: - self.advance() - elif self.char == b";" and self.last in b"\r\n": - while self.char and self.char not in b"\r\n": - self.advance() - else: - return - - def skip_char(self, char): - """Skip the next character if it matches expectations.""" - if len(char) != 1: - raise ValueError("only single characters allowed") - elif not self.char: - raise IOError("EOF found where %r expected" % char) - elif self.char == char: - self.advance() - else: - raise IOError("char %r found where %r expected" % ( - self.char, char)) - - def scan_token(self): - self.skip_whitespace() - out = b"" - while self.char and self.char in TOKEN_CHARS: - out += self.char - self.advance() - #print("scan_simple_string ->", repr(out)) - return out - - def scan_decimal(self): - i, value = 0, 0 - while self.char and self.char in DIGITS: - value = value*10 + int(self.char) - i += 1 - if i > 8: - raise IOError("decimal %d... too long" % value) - self.advance() - return value - - def scan_verbatim_string(self, length=None): - """Return the value of verbatim string with given length.""" - self.skip_whitespace() - self.skip_char(b":") - if not length: - raise ValueError("verbatim string had no length") - out, i = b"", 0 - while i < length: - out += self.char - self.advance() - i += 1 - return out - - def scan_quoted_string(self, length=None): - self.skip_char(b"\"") - out = b"" - while length is None or len(out) <= length: - if not self.char: - raise ValueError("quoted string is missing closing quote") - elif self.char == b"\"": - if length is None or len(out) == length: - self.skip_char(b"\"") - break - else: - raise ValueError("quoted string ended too early (expected %d)" % length) - elif self.char == b"\\": - c = self.advance() - if c == b"\r": - continue - elif c == b"\n": - continue - elif c in b"0123": - s = c + self.advance() + self.advance() - val = int(s, 8) - out += chr(val) - elif c == b"b": - out += b"\b" - elif c == b"f": - out += b"\f" - elif c == b"n": - out += b"\n" - elif c == b"r": - out += b"\r" - elif c == b"t": - out += b"\t" - elif c == b"v": - out += b"\v" - elif c == b"x": - s = self.advance() + self.advance() - val = int(s, 16) - out += chr(val) - else: - raise ValueError("unknown escape character \\%s at %d" % (c, self.pos)) - else: - out += self.char - self.advance() - return out - - def scan_hex_string(self, length=None): - self.bytesize = 4 - self.skip_char(b"#") - out = b"" - while self.char and (self.char != b"#" or self.bytesize == 4): - out += self.char - self.advance() - self.skip_char(b"#") - if length and length != len(out): - raise ValueError("hexstring length %d != declared length %d" % - (len(out), length)) - return out - - def scan_base64_string(self, length=None): - self.bytesize = 6 - self.skip_char(b"|") - out = b"" - while self.char and (self.char != b"|" or self.bytesize == 6): - out += self.char - self.advance() - self.skip_char(b"|") - if length and length != len(out): - raise ValueError("base64 length %d != declared length %d" % - (len(out), length)) - return out - - def scan_simple_string(self): - self.skip_whitespace() - if not self.char: - return None - elif self.char in TOKEN_CHARS and self.char not in DIGITS: - return self.scan_token() - elif self.char in DIGITS or self.char in b"\"#|:": - if self.char in DIGITS: - length = self.scan_decimal() - else: - length = None - if self.char == b"\"": - return self.scan_quoted_string(length) - elif self.char == b"#": - return self.scan_hex_string(length) - elif self.char == b"|": - return self.scan_base64_string(length) - elif self.char == b":": - return self.scan_verbatim_string(length) - else: - raise ValueError("illegal char %r at %d" % (self.char, self.pos)) - else: - raise ValueError("illegal char %r at %d" % (self.char, self.pos)) - - def scan_string(self): - # TODO: How should hints be handled in a Pythonic way? - hint = None - if self.char == b"[": - self.skip_char(b"[") - hint = self.scan_simple_string() - self.skip_whitespace() - self.skip_char(b"]") - self.skip_whitespace() - out = self.scan_simple_string() - return (hint, out) if hint else out - - def scan_list(self): - out = [] - self.skip_char(b"(") - while True: - self.skip_whitespace() - if not self.char: - raise ValueError("list is missing closing paren") - elif self.char == b")": - self.skip_char(b")") - return out - else: - out.append(self.scan_object()) - - def scan_object(self): - """Return the next object of any type.""" - self.skip_whitespace() - if not self.char: - out = None - elif self.char == b"{": - self.bytesize = 6 - self.skip_char(b"{") - out = self.scan_object() - self.skip_char(b"}") - elif self.char == b"(": - out = self.scan_list() - else: - out = self.scan_string() - return out + def __init__(self, buf): + self.bytesize = 8 + self.bits = 0 + self.nBits = 0 + self.buf = buf if hasattr(buf, "read") else BytesIO(buf) + self.char = "" + self.advance() + + def __iter__(self): + return self + + def __next__(self): + return self.next() + + def next(self): + obj = self.scan_object() + if obj: + return obj + else: + raise StopIteration + + @property + def pos(self): + return self.buf.tell() + + def advance(self): + """Get the next byte in the input stream. + + Will read as many input bytes as needed from Base64 or hex areas. + """ + while True: + self.last = self.char + self.char = self.buf.read(1) + if not self.char: + self.bytesize = 8 + self.char = None + return self.char + + if self.char is None: + return self.char + elif (self.bytesize == 6 and self.char in b"|}") \ + or (self.bytesize == 4 and self.char == b"#"): + if self.nBits and (1 << self.nBits)-1 & self.bits: + raise IOError("%d-bit region ended with %d unused bits at %d" % + (self.bytesize, self.nBits, self.pos)) + self.bytesize = 8 + return self.char + elif self.bytesize != 8 and self.char in WHITESPACE: + # ignore whitespace in hex/base64 regions + pass + elif self.bytesize == 6 and self.char == b"=": + # Base64 padding + self.nBits -= 2 + elif self.bytesize == 8: + return self.char + elif self.bytesize < 8: + self.bits <<= self.bytesize + self.nBits += self.bytesize + if self.bytesize == 6 and self.char in B64_DIGITS: + self.bits |= B64_DIGITS.index(self.char) + elif self.bytesize == 4 and self.char in HEX_DIGITS: + self.bits |= int(self.char, 16) + else: + raise IOError("char %r found in %d-bit region" % + (self.char, self.bytesize)) + + if self.nBits >= 8: + self.nBits -= 8 + byte = (self.bits >> self.nBits) & 0xFF + self.bits &= (1 << self.nBits)-1 + self.char = bytes([byte]) + return self.char + + def skip_whitespace(self): + while self.char: + if self.char in WHITESPACE: + self.advance() + elif self.char == b";" and self.last in b"\r\n": + while self.char and self.char not in b"\r\n": + self.advance() + else: + return + + def skip_char(self, char): + """Skip the next character if it matches expectations.""" + if len(char) != 1: + raise ValueError("only single characters allowed") + elif not self.char: + raise IOError("EOF found where %r expected" % char) + elif self.char == char: + self.advance() + else: + raise IOError("char %r found where %r expected" % ( + self.char, char)) + + def scan_token(self): + self.skip_whitespace() + out = b"" + while self.char and self.char in TOKEN_CHARS: + out += self.char + self.advance() + #print("scan_simple_string ->", repr(out)) + return out + + def scan_decimal(self): + i, value = 0, 0 + while self.char and self.char in DIGITS: + value = value*10 + int(self.char) + i += 1 + if i > 8: + raise IOError("decimal %d... too long" % value) + self.advance() + return value + + def scan_verbatim_string(self, length=None): + """Return the value of verbatim string with given length.""" + self.skip_whitespace() + self.skip_char(b":") + if not length: + raise ValueError("verbatim string had no length") + out, i = b"", 0 + while i < length: + out += self.char + self.advance() + i += 1 + return out + + def scan_quoted_string(self, length=None): + self.skip_char(b"\"") + out = b"" + while length is None or len(out) <= length: + if not self.char: + raise ValueError("quoted string is missing closing quote") + elif self.char == b"\"": + if length is None or len(out) == length: + self.skip_char(b"\"") + break + else: + raise ValueError("quoted string ended too early (expected %d)" % length) + elif self.char == b"\\": + c = self.advance() + if c == b"\r": + continue + elif c == b"\n": + continue + elif c in b"0123": + s = c + self.advance() + self.advance() + val = int(s, 8) + out += chr(val) + elif c == b"b": + out += b"\b" + elif c == b"f": + out += b"\f" + elif c == b"n": + out += b"\n" + elif c == b"r": + out += b"\r" + elif c == b"t": + out += b"\t" + elif c == b"v": + out += b"\v" + elif c == b"x": + s = self.advance() + self.advance() + val = int(s, 16) + out += chr(val) + else: + raise ValueError("unknown escape character \\%s at %d" % (c, self.pos)) + else: + out += self.char + self.advance() + return out + + def scan_hex_string(self, length=None): + self.bytesize = 4 + self.skip_char(b"#") + out = b"" + while self.char and (self.char != b"#" or self.bytesize == 4): + out += self.char + self.advance() + self.skip_char(b"#") + if length and length != len(out): + raise ValueError("hexstring length %d != declared length %d" % + (len(out), length)) + return out + + def scan_base64_string(self, length=None): + self.bytesize = 6 + self.skip_char(b"|") + out = b"" + while self.char and (self.char != b"|" or self.bytesize == 6): + out += self.char + self.advance() + self.skip_char(b"|") + if length and length != len(out): + raise ValueError("base64 length %d != declared length %d" % + (len(out), length)) + return out + + def scan_simple_string(self): + self.skip_whitespace() + if not self.char: + return None + elif self.char in TOKEN_CHARS and self.char not in DIGITS: + return self.scan_token() + elif self.char in DIGITS or self.char in b"\"#|:": + if self.char in DIGITS: + length = self.scan_decimal() + else: + length = None + if self.char == b"\"": + return self.scan_quoted_string(length) + elif self.char == b"#": + return self.scan_hex_string(length) + elif self.char == b"|": + return self.scan_base64_string(length) + elif self.char == b":": + return self.scan_verbatim_string(length) + else: + raise ValueError("illegal char %r at %d" % (self.char, self.pos)) + else: + raise ValueError("illegal char %r at %d" % (self.char, self.pos)) + + def scan_string(self): + # TODO: How should hints be handled in a Pythonic way? + hint = None + if self.char == b"[": + self.skip_char(b"[") + hint = self.scan_simple_string() + self.skip_whitespace() + self.skip_char(b"]") + self.skip_whitespace() + out = self.scan_simple_string() + return (hint, out) if hint else out + + def scan_list(self): + out = [] + self.skip_char(b"(") + while True: + self.skip_whitespace() + if not self.char: + raise ValueError("list is missing closing paren") + elif self.char == b")": + self.skip_char(b")") + return out + else: + out.append(self.scan_object()) + + def scan_object(self): + """Return the next object of any type.""" + self.skip_whitespace() + if not self.char: + out = None + elif self.char == b"{": + self.bytesize = 6 + self.skip_char(b"{") + out = self.scan_object() + self.skip_char(b"}") + elif self.char == b"(": + out = self.scan_list() + else: + out = self.scan_string() + return out def load(buf): - out = list(SexpParser(buf)) - if not out: - return None - elif len(out) == 1: - return out[0] - else: - return out + out = list(SexpParser(buf)) + if not out: + return None + elif len(out) == 1: + return out[0] + else: + return out def dump(obj, canonical=False, transport=False): - if transport: - canonical = True - - if isinstance(obj, (str, bytes)): - exp = dump_string(obj, canonical) - elif isinstance(obj, dict): - exp = dump_list(obj.items(), canonical) - elif isinstance(obj, (list, tuple)): - exp = dump_list(obj, canonical) - elif isinstance(obj, int): - exp = dump_string(str(obj), canonical) - else: - raise TypeError("unsupported object type %r of %r" % (type(obj), obj)) - - if transport: - return b"{" + base64.b64encode(exp) + b"}" - else: - return exp + if transport: + canonical = True + + if isinstance(obj, (str, bytes)): + exp = dump_string(obj, canonical) + elif isinstance(obj, dict): + exp = dump_list(obj.items(), canonical) + elif isinstance(obj, (list, tuple)): + exp = dump_list(obj, canonical) + elif isinstance(obj, int): + exp = dump_string(str(obj), canonical) + else: + raise TypeError("unsupported object type %r of %r" % (type(obj), obj)) + + if transport: + return b"{" + base64.b64encode(exp) + b"}" + else: + return exp def dump_string(obj, canonical=False, hex=False, hint=None): - if hasattr(obj, "encode"): - obj = obj.encode("utf-8") - - if canonical: - out = ("%d:" % len(obj)).encode("utf-8") + obj - elif is_token(obj): - out = bytes(obj) - elif is_quoteable(obj): - out = bytearray(b'"') - # This sucks. - # In python2, iterates over 1-char strings. - # In python3, iterates over integers. NOT 1-char bytes() - # No, screw it. I officially drop Python 2 compatibility here. - for char in obj: - if char in ESCAPE_CHARS_TB: - out += b"\\" - out += ESCAPE_CHARS_TB[char] - elif char in b"'\"": - out += b"\\" - out.append(char) - else: - out.append(char) - out += b'"' - out = bytes(out) - elif hex: - out = b"#" + obj.encode("hex") + b"#" - else: - out = b"|" + base64.b64encode(obj) + b"|" - - # Add [mimetypehint] - if hint: - return b"[" + dump_string(hint, canonical, hex, None) + b"]" + out - else: - return out + if hasattr(obj, "encode"): + obj = obj.encode("utf-8") + + if canonical: + out = ("%d:" % len(obj)).encode("utf-8") + obj + elif is_token(obj): + out = bytes(obj) + elif is_quoteable(obj): + out = bytearray(b'"') + # This sucks. + # In python2, iterates over 1-char strings. + # In python3, iterates over integers. NOT 1-char bytes() + # No, screw it. I officially drop Python 2 compatibility here. + for char in obj: + if char in ESCAPE_CHARS_TB: + out += b"\\" + out += ESCAPE_CHARS_TB[char] + elif char in b"'\"": + out += b"\\" + out.append(char) + else: + out.append(char) + out += b'"' + out = bytes(out) + elif hex: + out = b"#" + obj.encode("hex") + b"#" + else: + out = b"|" + base64.b64encode(obj) + b"|" + + # Add [mimetypehint] + if hint: + return b"[" + dump_string(hint, canonical, hex, None) + b"]" + out + else: + return out def dump_hint(obj, canonical=False): - return b"[" + dump_string(obj, canonical) + b"]" + return b"[" + dump_string(obj, canonical) + b"]" def dump_list(obj, canonical=False): - out = b"(" - if canonical: - out += b"".join(dump(x, canonical=True) for x in obj) - else: - out += b" ".join(dump(x) for x in obj) - out += b")" - return out + out = b"(" + if canonical: + out += b"".join(dump(x, canonical=True) for x in obj) + else: + out += b" ".join(dump(x) for x in obj) + out += b")" + return out def to_int(buf): - num = 0 - for byte in buf: - num <<= 8 - num |= ord(byte) - return num + num = 0 + for byte in buf: + num <<= 8 + num |= ord(byte) + return num def is_token(string): - if string[0] in DIGITS: - return False - for char in string: - if char not in TOKEN_CHARS: - return False - return True + if string[0] in DIGITS: + return False + for char in string: + if char not in TOKEN_CHARS: + return False + return True def is_quoteable(string): - for char in string: - if char in VERBATIM: - return False - elif char in PRINTABLE_CHARS: - pass - elif char in ESCAPE_CHARS: - pass - else: - return False - return True + for char in string: + if char in VERBATIM: + return False + elif char in PRINTABLE_CHARS: + pass + elif char in ESCAPE_CHARS: + pass + else: + return False + return True diff --git a/lib/python/nullroute/tests.py b/lib/python/nullroute/tests.py index 567e5e391..c534b411c 100755 --- a/lib/python/nullroute/tests.py +++ b/lib/python/nullroute/tests.py @@ -5,66 +5,66 @@ import irc def parse_test(file): - for line in open(file): - line = line.strip() - if not line or line.startswith("//"): - continue - yield json.loads("[%s]" % line) + for line in open(file): + line = line.strip() + if not line or line.startswith("//"): + continue + yield json.loads("[%s]" % line) def run_test(file, func): - passed, failed = 0, 0 - for input, wanted_output in parse_test(file): - actual_output = func(input) - if wanted_output == actual_output: - msg = " OK " - passed += 1 - else: - msg = "FAIL" - failed += 1 - print("%s: %r -> %s" % (msg, input, json.dumps(actual_output))) - if msg == "FAIL": - print("\033[33m%s: %r -> %s\033[m" % ("WANT", - input, json.dumps(wanted_output))) - print("Tests: %s passed, %d failed" % (passed, failed)) - return failed + passed, failed = 0, 0 + for input, wanted_output in parse_test(file): + actual_output = func(input) + if wanted_output == actual_output: + msg = " OK " + passed += 1 + else: + msg = "FAIL" + failed += 1 + print("%s: %r -> %s" % (msg, input, json.dumps(actual_output))) + if msg == "FAIL": + print("\033[33m%s: %r -> %s\033[m" % ("WANT", + input, json.dumps(wanted_output))) + print("Tests: %s passed, %d failed" % (passed, failed)) + return failed def test_split(input): - input = input.encode("utf-8") - try: - return irc.Line.split(input) - except ValueError: - return None + input = input.encode("utf-8") + try: + return irc.Line.split(input) + except ValueError: + return None def test_join(input): - try: - return irc.Line.join(input) - except ValueError: - return None + try: + return irc.Line.join(input) + except ValueError: + return None def test_prefix_split(input): - try: - p = irc.Prefix.parse(input) - if p: - return p.to_a() - else: - return None - except ValueError: - return None + try: + p = irc.Prefix.parse(input) + if p: + return p.to_a() + else: + return None + except ValueError: + return None def test_parse(input): - input = input.encode("utf-8") - p = irc.Line.parse(input) - tags = [k if v is True or v == "" else "%s=%s" % (k, v) - for k, v in p.tags.items()] - if tags: - tags.sort() - else: - tags = None - if p.prefix: - prefix = p.prefix.to_a() - else: - prefix = None - return [tags, prefix, p.args] + input = input.encode("utf-8") + p = irc.Line.parse(input) + tags = [k if v is True or v == "" else "%s=%s" % (k, v) + for k, v in p.tags.items()] + if tags: + tags.sort() + else: + tags = None + if p.prefix: + prefix = p.prefix.to_a() + else: + prefix = None + return [tags, prefix, p.args] dir = "../../tests" diff --git a/lib/python/nullroute/windows/firewall.py b/lib/python/nullroute/windows/firewall.py index bd1abf972..a8ebb0468 100644 --- a/lib/python/nullroute/windows/firewall.py +++ b/lib/python/nullroute/windows/firewall.py @@ -2,213 +2,213 @@ from .registry import RegistryKey class Firewall(object): - ROOT_KEY = "SYSTEM\\CurrentControlSet\\Services\\SharedAccess\\Parameters\\FirewallPolicy\\StandardProfile" + ROOT_KEY = "SYSTEM\\CurrentControlSet\\Services\\SharedAccess\\Parameters\\FirewallPolicy\\StandardProfile" - SCOPE_ALL = "*" - SCOPE_SUBNET = "LocalSubNet" + SCOPE_ALL = "*" + SCOPE_SUBNET = "LocalSubNet" - status_ENABLED = "Enabled" - status_DISABLED = "Disabled" + status_ENABLED = "Enabled" + status_DISABLED = "Disabled" - def __init__(self, machine=None): - self.machine = machine - self.hKey = RegistryKey(Con.HKEY_LOCAL_MACHINE, path=self.ROOT_KEY, - machine=self.machine) + def __init__(self, machine=None): + self.machine = machine + self.hKey = RegistryKey(Con.HKEY_LOCAL_MACHINE, path=self.ROOT_KEY, + machine=self.machine) - self._appList = None - self._portList = None + self._appList = None + self._portList = None - @property - def apps(self): - if not self._appList: - self._appList = _FirewallApplicationList(self) - return self._appList + @property + def apps(self): + if not self._appList: + self._appList = _FirewallApplicationList(self) + return self._appList - @property - def ports(self): - if not self._portList: - self._portList = _FirewallPortList(self) - return self._portList + @property + def ports(self): + if not self._portList: + self._portList = _FirewallPortList(self) + return self._portList class _FirewallApplicationList(object): - APPS_SUBKEY = "AuthorizedApplications\\List" - - POS_EXEPATH = 0 - POS_SCOPE = 1 - POS_status = 2 - POS_NAME = 3 - - def __init__(self, fw): - self.fwProfile = fw - self.hKey = self.fwProfile.hKey.open_subkey(self.APPS_SUBKEY) - - @classmethod - def _pack(self, exepath, scope=None, enabled=None, name=None): - if scope is not None: - status = "Enabled" if enabled else "Disabled" - return ":".join([exepath, scope, status, name]) - else: - return exepath - - @classmethod - def _unpack_value(self, val): - return val - - @classmethod - def _unpack_data(self, val): - # deal with drive:\path bogosity - _drive, _rest = val[:2], val[2:] - exepath, scope, status, name = _rest.split(":", 3) - exepath = _drive + exepath - enabled = (status.lower() == "enabled") - return exepath, scope, enabled, name - - # Lookup - - def query(self, exepath): - value = self._pack(exepath) - data, _reg_type = self.hKey[value] - return self._unpack_data(data) - - def __getitem__(self, key): - return self.query(key) - - def set(self, exepath, scope, enabled, name): - scope = scope or self.SCOPE_ALL - value = self._pack(exepath) - data = self._pack(exepath, scope, enabled, name) - self.hKey[value] = (data, Con.REG_SZ) - - def __setitem__(self, key, val): - if val[0] != key: - raise ValueError("exepath must be identical to the key") - self.set(*val) - - def delete(self, exepath): - value = self._pack(exepath) - del self.hKey[value] - - def __delitem__(self, key): - self.delete(key) - - def __iter__(self): - for k, (v, t) in self.hKey: - yield self._unpack_value(k) - - def values(self): - for k, (v, t) in self.hKey: - yield self._unpack_data(v) - - def items(self): - for k, (v, t) in self.hKey: - yield self._unpack_value(k), self._unpack_data(v) + APPS_SUBKEY = "AuthorizedApplications\\List" + + POS_EXEPATH = 0 + POS_SCOPE = 1 + POS_status = 2 + POS_NAME = 3 + + def __init__(self, fw): + self.fwProfile = fw + self.hKey = self.fwProfile.hKey.open_subkey(self.APPS_SUBKEY) + + @classmethod + def _pack(self, exepath, scope=None, enabled=None, name=None): + if scope is not None: + status = "Enabled" if enabled else "Disabled" + return ":".join([exepath, scope, status, name]) + else: + return exepath + + @classmethod + def _unpack_value(self, val): + return val + + @classmethod + def _unpack_data(self, val): + # deal with drive:\path bogosity + _drive, _rest = val[:2], val[2:] + exepath, scope, status, name = _rest.split(":", 3) + exepath = _drive + exepath + enabled = (status.lower() == "enabled") + return exepath, scope, enabled, name + + # Lookup + + def query(self, exepath): + value = self._pack(exepath) + data, _reg_type = self.hKey[value] + return self._unpack_data(data) + + def __getitem__(self, key): + return self.query(key) + + def set(self, exepath, scope, enabled, name): + scope = scope or self.SCOPE_ALL + value = self._pack(exepath) + data = self._pack(exepath, scope, enabled, name) + self.hKey[value] = (data, Con.REG_SZ) + + def __setitem__(self, key, val): + if val[0] != key: + raise ValueError("exepath must be identical to the key") + self.set(*val) + + def delete(self, exepath): + value = self._pack(exepath) + del self.hKey[value] + + def __delitem__(self, key): + self.delete(key) + + def __iter__(self): + for k, (v, t) in self.hKey: + yield self._unpack_value(k) + + def values(self): + for k, (v, t) in self.hKey: + yield self._unpack_data(v) + + def items(self): + for k, (v, t) in self.hKey: + yield self._unpack_value(k), self._unpack_data(v) class _FirewallPortList(object): - PORTS_SUBKEY = "GloballyOpenPorts\\List" - - #POS_PORT = 0 - #POS_PROTO = 1 - #POS_SCOPE = 2 - #POS_status = 3 - #POS_NAME = 4 - - POS_PORTSPEC = 0 - POS_SCOPE = 1 - POS_status = 2 - POS_NAME = 3 - - def __init__(self, fw): - self.fwProfile = fw - self.hKey = self.fwProfile.hKey.open_subkey(self.PORTS_SUBKEY) - - @classmethod - def _pack(self, portspec, scope=None, enabled=None, name=None): - try: - port, proto = portspec - except (TypeError, ValueError) as e: - raise ValueError("portspec must be (port, protocol)") - port = str(port) - proto = proto.upper() - if scope is not None: - status = "Enabled" if enabled else "Disabled" - return ":".join([port, proto, scope, status, name]) - else: - return ":".join([port, proto]) - - @classmethod - def _unpack_value(self, val): - port, proto = val.split(":", 1) - port = int(port) - proto = proto.upper() - return port, proto - - @classmethod - def _unpack_data(self, val): - port, proto, scope, status, name = val.split(":", 4) - port = int(port) - proto = proto.upper() - enabled = (status.lower() == "enabled") - return (port, proto), scope, enabled, name - - @classmethod - def _verify_key(self, key): - try: - assert len(key) == 2 - except (TypeError, AssertionError): - raise KeyError("key must be a (port, proto)") - - # Dict-like lookup - - def query(self, portspec): - value = self._pack(portspec) - data, _reg_type = self.hKey[value] - return self._unpack_data(data) - - def __getitem__(self, key): - return self.query(key) - - def set(self, portspec, scope, enabled, name): - scope = scope or self.SCOPE_ALL - value = self._pack(portspec) - data = self._pack(portspec, scope, enabled, name) - self.hKey[value] = (data, Con.REG_SZ) - - def __setitem__(self, key, val): - if val[0] != key: - raise ValueError("portspec must be identical to key") - self.set(*val) - - def delete(self, portspec): - value = self._pack(portspec) - del self.hKey[value] - - def __delitem__(self, key): - self.delete(key) - - def __iter__(self): - for k, (v, t) in self.hKey: - yield self._unpack_value(k) - - def values(self): - for k, (v, t) in self.hKey: - yield self._unpack_data(v) - - def items(self): - for k, (v, t) in self.hKey: - yield self._unpack_value(k), self._unpack_data(v) - - # Rule management - - def get_rule_status(self, portspec): - return self[portspec][self.POS_STATUS] - - def set_rule_status(self, portspec, enabled): - _, scope, _, name = self[portspec] - self[portspec] = portspec, scope, enabled, name - - def get_rule_name(self, portspec): - return self[portspec][self.POS_NAME] - - def set_rule_name(self, portspec, name): - _, scope, enabled, _ = self[portspec] - self[portspec] = portspec, scope, enabled, name + PORTS_SUBKEY = "GloballyOpenPorts\\List" + + #POS_PORT = 0 + #POS_PROTO = 1 + #POS_SCOPE = 2 + #POS_status = 3 + #POS_NAME = 4 + + POS_PORTSPEC = 0 + POS_SCOPE = 1 + POS_status = 2 + POS_NAME = 3 + + def __init__(self, fw): + self.fwProfile = fw + self.hKey = self.fwProfile.hKey.open_subkey(self.PORTS_SUBKEY) + + @classmethod + def _pack(self, portspec, scope=None, enabled=None, name=None): + try: + port, proto = portspec + except (TypeError, ValueError) as e: + raise ValueError("portspec must be (port, protocol)") + port = str(port) + proto = proto.upper() + if scope is not None: + status = "Enabled" if enabled else "Disabled" + return ":".join([port, proto, scope, status, name]) + else: + return ":".join([port, proto]) + + @classmethod + def _unpack_value(self, val): + port, proto = val.split(":", 1) + port = int(port) + proto = proto.upper() + return port, proto + + @classmethod + def _unpack_data(self, val): + port, proto, scope, status, name = val.split(":", 4) + port = int(port) + proto = proto.upper() + enabled = (status.lower() == "enabled") + return (port, proto), scope, enabled, name + + @classmethod + def _verify_key(self, key): + try: + assert len(key) == 2 + except (TypeError, AssertionError): + raise KeyError("key must be a (port, proto)") + + # Dict-like lookup + + def query(self, portspec): + value = self._pack(portspec) + data, _reg_type = self.hKey[value] + return self._unpack_data(data) + + def __getitem__(self, key): + return self.query(key) + + def set(self, portspec, scope, enabled, name): + scope = scope or self.SCOPE_ALL + value = self._pack(portspec) + data = self._pack(portspec, scope, enabled, name) + self.hKey[value] = (data, Con.REG_SZ) + + def __setitem__(self, key, val): + if val[0] != key: + raise ValueError("portspec must be identical to key") + self.set(*val) + + def delete(self, portspec): + value = self._pack(portspec) + del self.hKey[value] + + def __delitem__(self, key): + self.delete(key) + + def __iter__(self): + for k, (v, t) in self.hKey: + yield self._unpack_value(k) + + def values(self): + for k, (v, t) in self.hKey: + yield self._unpack_data(v) + + def items(self): + for k, (v, t) in self.hKey: + yield self._unpack_value(k), self._unpack_data(v) + + # Rule management + + def get_rule_status(self, portspec): + return self[portspec][self.POS_STATUS] + + def set_rule_status(self, portspec, enabled): + _, scope, _, name = self[portspec] + self[portspec] = portspec, scope, enabled, name + + def get_rule_name(self, portspec): + return self[portspec][self.POS_NAME] + + def set_rule_name(self, portspec, name): + _, scope, enabled, _ = self[portspec] + self[portspec] = portspec, scope, enabled, name diff --git a/lib/python/nullroute/windows/io.py b/lib/python/nullroute/windows/io.py index 59d0ed473..f50596238 100644 --- a/lib/python/nullroute/windows/io.py +++ b/lib/python/nullroute/windows/io.py @@ -1,12 +1,12 @@ import sys import msvcrt -O_TEXT = 0x4000 # file mode is text (translated) -O_BINARY = 0x8000 # file mode is binary (untranslated) -O_WTEXT = 0x10000 # file mode is UTF16 (translated) -O_U16TEXT = 0x20000 # file mode is UTF16 no BOM (translated) -O_U8TEXT = 0x40000 # file mode is UTF8 no BOM (translated) +O_TEXT = 0x4000 # file mode is text (translated) +O_BINARY = 0x8000 # file mode is binary (untranslated) +O_WTEXT = 0x10000 # file mode is UTF16 (translated) +O_U16TEXT = 0x20000 # file mode is UTF16 no BOM (translated) +O_U8TEXT = 0x40000 # file mode is UTF8 no BOM (translated) def setconsmode(flag): - for fh in (sys.stdin, sys.stdout, sys.stderr): - msvcrt.setmode(fh.fileno(), flag) + for fh in (sys.stdin, sys.stdout, sys.stderr): + msvcrt.setmode(fh.fileno(), flag) diff --git a/lib/python/nullroute/windows/registry.py b/lib/python/nullroute/windows/registry.py index ab6d18975..0c32e1f39 100644 --- a/lib/python/nullroute/windows/registry.py +++ b/lib/python/nullroute/windows/registry.py @@ -3,77 +3,77 @@ import win32con as Con class RegistryKey(object): - def __init__(self, hKey, path=None, machine=None): - self.access = Con.KEY_ALL_ACCESS - if isinstance(hKey, pywintypes.HANDLEType): - if path is None: - self.hKey = hKey - self.path = None - else: - self.hKey = Api.RegOpenKeyEx(hKey, path, 0, self.access) - self.path = path - self.machine = None - else: - if machine is None: - self.hKey = Api.RegOpenKeyEx(hKey, path, 0, self.access) - self.path = path - else: - if not machine.startswith("\\\\"): - machine = "\\\\%s" % machine - hKey = Api.RegConnectRegistry(machine, hKey) - if path is None: - self.hKey = hKey - else: - self.hKey = Api.RegOpenKeyEx(hKey, path, 0, self.access) - self.path = path - self.machine = machine + def __init__(self, hKey, path=None, machine=None): + self.access = Con.KEY_ALL_ACCESS + if isinstance(hKey, pywintypes.HANDLEType): + if path is None: + self.hKey = hKey + self.path = None + else: + self.hKey = Api.RegOpenKeyEx(hKey, path, 0, self.access) + self.path = path + self.machine = None + else: + if machine is None: + self.hKey = Api.RegOpenKeyEx(hKey, path, 0, self.access) + self.path = path + else: + if not machine.startswith("\\\\"): + machine = "\\\\%s" % machine + hKey = Api.RegConnectRegistry(machine, hKey) + if path is None: + self.hKey = hKey + else: + self.hKey = Api.RegOpenKeyEx(hKey, path, 0, self.access) + self.path = path + self.machine = machine - def close(self): - if self.hKey: - Api.RegCloseKey(self.hKey) - self.hKey = None + def close(self): + if self.hKey: + Api.RegCloseKey(self.hKey) + self.hKey = None - def __del__(self): - self.close() + def __del__(self): + self.close() - def query(self, valueName): - try: - return Api.RegQueryValueEx(self.hKey, valueName) - except pywintypes.error as e: - if e.winerror == 2: - raise KeyError(valueName) - else: - raise + def query(self, valueName): + try: + return Api.RegQueryValueEx(self.hKey, valueName) + except pywintypes.error as e: + if e.winerror == 2: + raise KeyError(valueName) + else: + raise - def __getitem__(self, valueName): - return self.query(valueName) + def __getitem__(self, valueName): + return self.query(valueName) - def set(self, valueName, valueType, valueData): - return Api.RegSetValueEx(self.hKey, valueName, None, valueType, valueData) + def set(self, valueName, valueType, valueData): + return Api.RegSetValueEx(self.hKey, valueName, None, valueType, valueData) - def __setitem__(self, valueName, valueTypedData): - valueData, valueType = valueTypedData - return self.set(valueName, valueType, valueData) + def __setitem__(self, valueName, valueTypedData): + valueData, valueType = valueTypedData + return self.set(valueName, valueType, valueData) - def delete(self, valueName): - return Api.RegDeleteValue(self.hKey, valueName) + def delete(self, valueName): + return Api.RegDeleteValue(self.hKey, valueName) - def __delitem__(self, valueName): - return self.delete(valueName) + def __delitem__(self, valueName): + return self.delete(valueName) - def enumerate(self): - i = 0 - while True: - try: - value, data, type = Api.RegEnumValue(self.hKey, i) - except Api.error: - break - else: - yield value, (data, type) - i += 1 + def enumerate(self): + i = 0 + while True: + try: + value, data, type = Api.RegEnumValue(self.hKey, i) + except Api.error: + break + else: + yield value, (data, type) + i += 1 - def __iter__(self): - return self.enumerate() + def __iter__(self): + return self.enumerate() - def open_subkey(self, path): - return self.__class__(self.hKey, path) + def open_subkey(self, path): + return self.__class__(self.hKey, path) diff --git a/lib/python/nullroute/windows/util.py b/lib/python/nullroute/windows/util.py index 9b3714f18..2403a1656 100644 --- a/lib/python/nullroute/windows/util.py +++ b/lib/python/nullroute/windows/util.py @@ -2,16 +2,16 @@ import win32con as Con def load_string_resource(pointer): - """Resolve a @dllname,ordinal string resource pointer""" + """Resolve a @dllname,ordinal string resource pointer""" - if pointer[0] != "@": - return pointer + if pointer[0] != "@": + return pointer - resfile, resid = pointer[1:].split(",") - resid = int(resid) + resfile, resid = pointer[1:].split(",") + resid = int(resid) - hRes = Api.LoadLibraryEx(resfile, 0, Con.LOAD_LIBRARY_AS_DATAFILE) - val = Api.LoadString(hRes, -resid, 1024) - Api.FreeLibrary(hRes) + hRes = Api.LoadLibraryEx(resfile, 0, Con.LOAD_LIBRARY_AS_DATAFILE) + val = Api.LoadString(hRes, -resid, 1024) + Api.FreeLibrary(hRes) - return val.split('\x00', 1)[0] + return val.split('\x00', 1)[0] diff --git a/misc/bthkeys.py b/misc/bthkeys.py index 65bd5fa13..e48ceee93 100755 --- a/misc/bthkeys.py +++ b/misc/bthkeys.py @@ -4,95 +4,95 @@ import sys def fromhex(string): - return string.replace(":", "").replace("-", "").decode("hex") + return string.replace(":", "").replace("-", "").decode("hex") def tohex(string, sep=""): - if sep: - return sep.join(byte.encode("hex") for byte in string) - else: - return string.encode("hex") + if sep: + return sep.join(byte.encode("hex") for byte in string) + else: + return string.encode("hex") def bluez_import_system(local_addr=None, root="/"): - path = os.path.join(root, "var/lib/bluetooth") - keys = {} - for subdir in os.listdir(path): - local_addr = fromhex(subdir) - keyfile = os.path.join(path, subdir, "linkkeys") - with open(keyfile, "r") as fd: - keys.update(bluez_import_fd(fd, local_addr)) - return keys + path = os.path.join(root, "var/lib/bluetooth") + keys = {} + for subdir in os.listdir(path): + local_addr = fromhex(subdir) + keyfile = os.path.join(path, subdir, "linkkeys") + with open(keyfile, "r") as fd: + keys.update(bluez_import_fd(fd, local_addr)) + return keys def bluez_import_fd(fd, local_addr): - keys = {} - for line in (fd or sys.stdin): - addr, key, _ = line.split(" ", 2) - addr = fromhex(addr) - key = fromhex(key) - keys[addr] = key - return {local_addr: keys} + keys = {} + for line in (fd or sys.stdin): + addr, key, _ = line.split(" ", 2) + addr = fromhex(addr) + key = fromhex(key) + keys[addr] = key + return {local_addr: keys} def bluez_export_system(keys, root="/"): - path = os.path.join(root, "/var/lib/bluetooth") - os.umask(077) - for local_addr, dev_keys in keys.items(): - device_path = os.path.join(path, tohex(local_addr, ":").upper()) - if not os.path.exists(device_path): - os.mkdir(device_path) - keyfile = os.path.join(device_path, "linkkeys") - with open(keyfile, "w") as fd: - bluez_export_fd(fd, {local_addr: dev_keys}) + path = os.path.join(root, "/var/lib/bluetooth") + os.umask(077) + for local_addr, dev_keys in keys.items(): + device_path = os.path.join(path, tohex(local_addr, ":").upper()) + if not os.path.exists(device_path): + os.mkdir(device_path) + keyfile = os.path.join(device_path, "linkkeys") + with open(keyfile, "w") as fd: + bluez_export_fd(fd, {local_addr: dev_keys}) def bluez_export_fd(fd, keys): - for local_addr, dev_keys in keys.items(): - for addr, key in dev_keys.items(): - addr = tohex(addr, ":").upper() - key = tohex(key).upper() - fd.write("%s %s %d %d\n" % (addr, key, 0, 4)) + for local_addr, dev_keys in keys.items(): + for addr, key in dev_keys.items(): + addr = tohex(addr, ":").upper() + key = tohex(key).upper() + fd.write("%s %s %d %d\n" % (addr, key, 0, 4)) def winreg_export_fd(fd, keys): - fd.write("Windows Registry Editor Version 5.00\r\n") - for local_addr, dev_keys in keys.items(): - fd.write("\r\n") - fd.write("[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\" \ - "Services\\BTHPORT\\Parameters\\Keys\\%s]\r\n" % tohex(local_addr)) - for addr, key in dev_keys.items(): - fd.write("\"%s\"=hex:%s\r\n" % (tohex(addr), tohex(key, ","))) + fd.write("Windows Registry Editor Version 5.00\r\n") + for local_addr, dev_keys in keys.items(): + fd.write("\r\n") + fd.write("[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\" \ + "Services\\BTHPORT\\Parameters\\Keys\\%s]\r\n" % tohex(local_addr)) + for addr, key in dev_keys.items(): + fd.write("\"%s\"=hex:%s\r\n" % (tohex(addr), tohex(key, ","))) Me = os.path.basename(sys.argv[0]) try: - in_format, out_format, local_addr = sys.argv[1:] + in_format, out_format, local_addr = sys.argv[1:] except ValueError: - print("usage: %s " % Me, - file=sys.stderr) - sys.exit(2) + print("usage: %s " % Me, + file=sys.stderr) + sys.exit(2) formats = {} formats["bluez"] = { - "import": (bluez_import_system, bluez_import_fd), - "export": (bluez_export_system, bluez_export_fd), + "import": (bluez_import_system, bluez_import_fd), + "export": (bluez_export_system, bluez_export_fd), } formats["winreg"] = { - "import": (None, None), - "export": (None, winreg_export_fd), + "import": (None, None), + "export": (None, winreg_export_fd), } in_root = "/snow" -in_fmt, in_system, in_file = 'bluez', True, None -out_fmt, out_system, out_file = 'winreg', False, "-" +in_fmt, in_system, in_file = 'bluez', True, None +out_fmt, out_system, out_file = 'winreg', False, "-" import_system, import_fd = formats[in_fmt]["import"] export_system, export_fd = formats[out_fmt]["export"] if in_system: - keys = import_system(root=in_root) + keys = import_system(root=in_root) elif in_file == "-": - keys = import_fd(sys.stdin) + keys = import_fd(sys.stdin) else: - keys = import_fd(open(in_file, "r")) + keys = import_fd(open(in_file, "r")) if out_system: - export_system(keys) + export_system(keys) elif out_file == "-": - export_fd(sys.stdout, keys) + export_fd(sys.stdout, keys) else: - export_fd(open(out_file, "w"), keys) + export_fd(open(out_file, "w"), keys) diff --git a/misc/dupes b/misc/dupes index 04079f5d3..5da1ce609 100755 --- a/misc/dupes +++ b/misc/dupes @@ -16,111 +16,110 @@ _ttywidth = None # header and hash caches, to avoid reading # or hashing the same file twice header_size = 512 -file_headers = {} # path → header -file_hashes = {} # path → hash +file_headers = {} # path → header +file_hashes = {} # path → hash def ttywidth(): - global _ttywidth - if _ttywidth is None: - with os.popen("stty size", "r") as fh: - line = fh.read().strip() - rows, cols = line.split() - _ttywidth = int(cols) - return _ttywidth + global _ttywidth + if _ttywidth is None: + with os.popen("stty size", "r") as fh: + line = fh.read().strip() + rows, cols = line.split() + _ttywidth = int(cols) + return _ttywidth def status(*args): - if sys.stderr.isatty(): - msg = " ".join(args) - msg = msg[:ttywidth()] - sys.stderr.write("\r\033[K\033[33m%s\033[m" % msg) - sys.stderr.flush() + if sys.stderr.isatty(): + msg = " ".join(args) + msg = msg[:ttywidth()] + sys.stderr.write("\r\033[K\033[33m%s\033[m" % msg) + sys.stderr.flush() def weed_ignores(dirs): - ignores = {".git", ".hg"} - for item in dirs[:]: - if item in ignores: - dirs.remove(item) + ignores = {".git", ".hg"} + for item in dirs[:]: + if item in ignores: + dirs.remove(item) def enum_files(root_dir): - for subdir, dirs, files in os.walk(root_dir): - weed_ignores(dirs) - for name in files: - path = os.path.join(subdir, name) - yield path + for subdir, dirs, files in os.walk(root_dir): + weed_ignores(dirs) + for name in files: + path = os.path.join(subdir, name) + yield path def get_header(path): - if path not in file_headers: - if opts.verbose: - print("reading", path) - with open(path, "rb") as fh: - file_headers[path] = fh.read(header_size) - return file_headers[path] + if path not in file_headers: + if opts.verbose: + print("reading", path) + with open(path, "rb") as fh: + file_headers[path] = fh.read(header_size) + return file_headers[path] def hash_file(path): - if path not in file_hashes: - if opts.verbose: - print("hashing", path) - h = hashlib.sha1() - with open(path, "rb") as fh: - buf = True - while buf: - buf = fh.read(4194304) - h.update(buf) - file_hashes[path] = h.digest() - return file_hashes[path] + if path not in file_hashes: + if opts.verbose: + print("hashing", path) + h = hashlib.sha1() + with open(path, "rb") as fh: + buf = True + while buf: + buf = fh.read(4194304) + h.update(buf) + file_hashes[path] = h.digest() + return file_hashes[path] def find_duplicates(root_dirs): - # dicts keeping duplicate items - known_sizes = defaultdict(list) # size → path[] - known_headers = defaultdict(list) # (size, header) → path[] - known_hashes = defaultdict(list) # (size, hash) → path[] - - # find files identical in size - for root_dir in root_dirs: - for path in enum_files(root_dir): - status("stat", path) - st = os.lstat(path) - if not stat.S_ISREG(st.st_mode): - continue - - known_sizes[st.st_size].append(path) - - status() - - # find files identical in size and first `header_size` bytes - for size, paths in known_sizes.items(): - if len(paths) < 2: - continue - - for path in paths: - status("head", path) - header = get_header(path) - known_headers[size, header].append(path) - - status() - - # find files identical in size and hash - for (size, header), paths in known_headers.items(): - if len(paths) < 2: - continue - if size <= header_size: - # optimization: don't compare by hash if - # the entire contents are already known - status() - yield paths - continue - - for path in paths: - status("hash", path) - filehash = hash_file(path) - known_hashes[size, filehash].append(path) - - status() - - for (size, filehash), paths in known_hashes.items(): - if len(paths) < 2: - continue - yield paths + # dicts keeping duplicate items + known_sizes = defaultdict(list) # size → path[] + known_headers = defaultdict(list) # (size, header) → path[] + known_hashes = defaultdict(list) # (size, hash) → path[] + + # find files identical in size + for root_dir in root_dirs: + for path in enum_files(root_dir): + status("stat", path) + st = os.lstat(path) + if not stat.S_ISREG(st.st_mode): + continue + known_sizes[st.st_size].append(path) + + status() + + # find files identical in size and first `header_size` bytes + for size, paths in known_sizes.items(): + if len(paths) < 2: + continue + + for path in paths: + status("head", path) + header = get_header(path) + known_headers[size, header].append(path) + + status() + + # find files identical in size and hash + for (size, header), paths in known_headers.items(): + if len(paths) < 2: + continue + if size <= header_size: + # optimization: don't compare by hash if + # the entire contents are already known + status() + yield paths + continue + + for path in paths: + status("hash", path) + filehash = hash_file(path) + known_hashes[size, filehash].append(path) + + status() + + for (size, filehash), paths in known_hashes.items(): + if len(paths) < 2: + continue + yield paths cli_usage = "%prog [options] {path}" @@ -135,30 +134,30 @@ This program ignores symlinks, special files, and the like. It also does not kno cli_version = "1.∞" if __name__ == "__main__": - op = OptionParser( - usage=cli_usage, - description=cli_desc, - epilog=cli_epilog, - version=cli_version) - op.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False, - help="show files as they are processed") - - opts, args = op.parse_args() - if args: - root_dir = args[:] - else: - root_dir = ["."] - - try: - for paths in find_duplicates(root_dir): - print("Duplicates:") - for path in paths: - print(" ", path) - # do something with duplicates here. - except KeyboardInterrupt: - status() - print("Interrupted.") - - if opts.verbose: - print("%d files compared by header" % len(file_headers)) - print("%d files compared by hash" % len(file_hashes)) + op = OptionParser( + usage=cli_usage, + description=cli_desc, + epilog=cli_epilog, + version=cli_version) + op.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False, + help="show files as they are processed") + + opts, args = op.parse_args() + if args: + root_dir = args[:] + else: + root_dir = ["."] + + try: + for paths in find_duplicates(root_dir): + print("Duplicates:") + for path in paths: + print(" ", path) + # do something with duplicates here. + except KeyboardInterrupt: + status() + print("Interrupted.") + + if opts.verbose: + print("%d files compared by header" % len(file_headers)) + print("%d files compared by hash" % len(file_hashes)) diff --git a/misc/ldifunwrap b/misc/ldifunwrap index 71145b500..7b7df7d97 100755 --- a/misc/ldifunwrap +++ b/misc/ldifunwrap @@ -1,15 +1,14 @@ #!/usr/bin/env python # unwrap LDIF files - import sys buf = "" for line in sys.stdin: - line = line.rstrip("\r\n") - if line.startswith(" "): - buf += line[1:] - else: - print(buf) - buf = line + line = line.rstrip("\r\n") + if line.startswith(" "): + buf += line[1:] + else: + print(buf) + buf = line if buf: - print(buf) + print(buf) diff --git a/misc/putty-hostalias.py b/misc/putty-hostalias.py index 5e884c16e..8feaea385 100644 --- a/misc/putty-hostalias.py +++ b/misc/putty-hostalias.py @@ -12,110 +12,110 @@ """ Usage: - hostalias [puttyargs] [user@]host[:port] - - Both or , when specified, override those from hostaliases. + hostalias [puttyargs] [user@]host[:port] + + Both or , when specified, override those from hostaliases. hostalias syntax: - entry: = [@][][:] [puttyargs] + entry: = [@][][:] [puttyargs] + + Aliases are comma-separated. Spaces are ignored. Hosts are lowercase'd. + + If starts with a period ".", then it is _appended_ to input. + + All asterisks "*" in are replaced with input. + + is not required if user and/or port are specified. (If you only + want to set puttyargs but keep user@host:port, set host to *) - Aliases are comma-separated. Spaces are ignored. Hosts are lowercase'd. - - If starts with a period ".", then it is _appended_ to input. - - All asterisks "*" in are replaced with input. - - is not required if user and/or port are specified. (If you only - want to set puttyargs but keep user@host:port, set host to *) - - Aliases can be chained but not looped. + Aliases can be chained but not looped. Example hostalias entries: - # host 'box' becomes 'homebox.dyndns.org' - box = homebox.dyndns.org + # host 'box' becomes 'homebox.dyndns.org' + box = homebox.dyndns.org - # hosts 'foo' and 'bar' get '.example.com' appended - # user is set to 'joeuser' unless overriden by commandline - foo, bar = joeuser@.example.com + # hosts 'foo' and 'bar' get '.example.com' appended + # user is set to 'joeuser' unless overriden by commandline + foo, bar = joeuser@.example.com - # host 'mail' becomes 'adm-mail.example.com' - # host 'log' becomes 'adm-log.example.com' - # port is set to 21032 unless overriden by commandline - mail, log = adm-*.example.com:21032 + # host 'mail' becomes 'adm-mail.example.com' + # host 'log' becomes 'adm-log.example.com' + # port is set to 21032 unless overriden by commandline + mail, log = adm-*.example.com:21032 """ def split_address(address): - user, host, port = None, address, None - if "@" in host: - user, host = host.split("@", 2) - if ":" in host: - host, port = host.split(":", 2) - try: - port = int(port) - except TypeError: - port = None - - return user, host, port + user, host, port = None, address, None + if "@" in host: + user, host = host.split("@", 2) + if ":" in host: + host, port = host.split(":", 2) + try: + port = int(port) + except TypeError: + port = None + + return user, host, port class addr(): - def gethost(self, input): - if self.host == "" or self.host is None: - return "" - elif self.host[0] == ".": - return input + self.host - elif "*" in target.host: - return self.host.replace("*", input) - else: - return self.host - - def __init__(s): - s.user, s.host, s.port, s.opts = None, None, None, [] + def gethost(self, input): + if self.host == "" or self.host is None: + return "" + elif self.host[0] == ".": + return input + self.host + elif "*" in target.host: + return self.host.replace("*", input) + else: + return self.host + + def __init__(s): + s.user, s.host, s.port, s.opts = None, None, None, [] def read_aliases(file): - alias_map = {} - for line in open(file, "r"): - line = line.strip() - if line == "" or line[0] == "#": continue - - names, data = line.split("=", 1) - - names = names.lower().replace(" ", "").split(",") - data = data.split() - - target = addr() - - target.user, target.host, target.port = split_address(data[0]) - target.opts = data[1:] - - for n in names: - if n not in alias_map: alias_map[n] = addr() - if target.user: alias_map[n].user = target.user - if target.host: alias_map[n].host = target.host - if target.port: alias_map[n].port = target.port - alias_map[n].opts.extend(target.opts) - return alias_map + alias_map = {} + for line in open(file, "r"): + line = line.strip() + if line == "" or line[0] == "#": continue + + names, data = line.split("=", 1) + + names = names.lower().replace(" ", "").split(",") + data = data.split() + + target = addr() + + target.user, target.host, target.port = split_address(data[0]) + target.opts = data[1:] + + for n in names: + if n not in alias_map: alias_map[n] = addr() + if target.user: alias_map[n].user = target.user + if target.host: alias_map[n].host = target.host + if target.port: alias_map[n].port = target.port + alias_map[n].opts.extend(target.opts) + return alias_map if len(sys.argv) >= 2: - host = sys.argv.pop() - extargs = sys.argv[1:] + host = sys.argv.pop() + extargs = sys.argv[1:] else: - print("Usage: %s [puttyargs] [user@]host[:port]" - % os.path.basename(sys.argv[0]), - file=sys.stderr) - sys.exit(2) + print("Usage: %s [puttyargs] [user@]host[:port]" + % os.path.basename(sys.argv[0]), + file=sys.stderr) + sys.exit(2) user, host, port = split_address(host) aliases = read_aliases(alias_file) def dump(): - defuser = user or "" - defport = port or 22 - for k in aliases: - target = aliases[k] - print("%s = %s@%s:%s %s" % (k, target.user or defuser, target.host or k, target.port or defport, subprocess.list2cmdline(target.opts))) + defuser = user or "" + defport = port or 22 + for k in aliases: + target = aliases[k] + print("%s = %s@%s:%s %s" % (k, target.user or defuser, target.host or k, target.port or defport, subprocess.list2cmdline(target.opts))) #dump() #sys.exit() @@ -123,37 +123,37 @@ def dump(): # resolve alias antiloop = [] while host not in antiloop: - host = host.lower() - antiloop.append(host) - if host in aliases: - target = aliases[host] - - if target.host == "": - pass - elif target.host.startswith("."): - host += target.host - elif "*" in target.host: - host = target.host.replace("*", host) - else: - host = target.host - - if user == None: - user = target.user - if port == None: - port = target.port - - extargs += target.opts + host = host.lower() + antiloop.append(host) + if host in aliases: + target = aliases[host] + + if target.host == "": + pass + elif target.host.startswith("."): + host += target.host + elif "*" in target.host: + host = target.host.replace("*", host) + else: + host = target.host + + if user == None: + user = target.user + if port == None: + port = target.port + + extargs += target.opts if host in antiloop[:-1]: - antiloop.append(t_host) - print("Loop detected:", " -> ".join(antiloop)) + antiloop.append(t_host) + print("Loop detected:", " -> ".join(antiloop)) pArgs = ["putty", host] pArgs += extargs if user: - pArgs += ["-l", user] + pArgs += ["-l", user] if port: - pArgs += ["-P", str(port)] + pArgs += ["-P", str(port)] print("exec:", subprocess.list2cmdline(pArgs)) subprocess.Popen(pArgs) diff --git a/misc/sony-ericsson-screenshot b/misc/sony-ericsson-screenshot index bf240aedc..e561d9683 100755 --- a/misc/sony-ericsson-screenshot +++ b/misc/sony-ericsson-screenshot @@ -20,100 +20,100 @@ convert -size 240x320 -depth 8 foo.rgba foo.png """ def trace(x): - sys.stderr.write(x) - sys.stderr.flush() + sys.stderr.write(x) + sys.stderr.flush() def serial_command(ser, command): - ser.write(command+"\r\n") - echo = False - while True: - line = ser.readline() - if not line: - raise IOError("device lost") - line = line.strip() - if not line: - pass - #elif not echo and line == command: - # echo = True - elif line in ("OK", "ERROR"): - yield line - break - else: - yield line + ser.write(command+"\r\n") + echo = False + while True: + line = ser.readline() + if not line: + raise IOError("device lost") + line = line.strip() + if not line: + pass + #elif not echo and line == command: + # echo = True + elif line in ("OK", "ERROR"): + yield line + break + else: + yield line def capture(ser): - dimensions = None - for line in serial_command(ser, "AT*ZISI=?"): - if line[:7] == "*ZISI: ": - line = line[7:].split(",") - line = [int(x.strip()) for x in line] - dimensions = line - height, width, bpp = dimensions[:3] - - trace("Capturing %dx%d\n" % (width, height)) - - scanlines, buf = [], b"" - for line in serial_command(ser, "AT*ZISI"): - if line == "OK": - trace("\rReading %d%%\n" % 100) - elif line == "ERROR": - trace("\nReceived ERROR from device.") - elif line.startswith("AT"): - trace("\rReading %d%%" % 0) - elif line.startswith("*"): - if line.startswith("*ZISI: "): - if buf: - scanlines.append(buf) - buf = line - trace("\rReading %d%%" % \ - (100.0 / height * len(scanlines))) - else: - buf += line - scanlines.append(buf) - - scanlines = [decode_scanline(line) for line in scanlines] - - return height, width, scanlines + dimensions = None + for line in serial_command(ser, "AT*ZISI=?"): + if line[:7] == "*ZISI: ": + line = line[7:].split(",") + line = [int(x.strip()) for x in line] + dimensions = line + height, width, bpp = dimensions[:3] + + trace("Capturing %dx%d\n" % (width, height)) + + scanlines, buf = [], b"" + for line in serial_command(ser, "AT*ZISI"): + if line == "OK": + trace("\rReading %d%%\n" % 100) + elif line == "ERROR": + trace("\nReceived ERROR from device.") + elif line.startswith("AT"): + trace("\rReading %d%%" % 0) + elif line.startswith("*"): + if line.startswith("*ZISI: "): + if buf: + scanlines.append(buf) + buf = line + trace("\rReading %d%%" % \ + (100.0 / height * len(scanlines))) + else: + buf += line + scanlines.append(buf) + + scanlines = [decode_scanline(line) for line in scanlines] + + return height, width, scanlines def decode_scanline(line): - if line[:7] != "*ZISI: ": - raise ValueError("invalid input: %r" % line) - line = line[7:].decode("hex") - # split into pixels - line = [line[i:i+4] for i in xrange(0, len(line), 4)] - # convert ARGB to RGBA - line = [pixel[1:] + pixel[0] for pixel in line] - return line + if line[:7] != "*ZISI: ": + raise ValueError("invalid input: %r" % line) + line = line[7:].decode("hex") + # split into pixels + line = [line[i:i+4] for i in xrange(0, len(line), 4)] + # convert ARGB to RGBA + line = [pixel[1:] + pixel[0] for pixel in line] + return line def flatten(scanlines): - return "".join("".join(line) for line in scanlines) + return "".join("".join(line) for line in scanlines) def store_image(data, output_file): - size = data[1], data[0] - buf = flatten(data[2]) - img = Image.fromstring("RGBA", size, buf, "raw", "RGBA", 0, 1) - img.save(output_file) + size = data[1], data[0] + buf = flatten(data[2]) + img = Image.fromstring("RGBA", size, buf, "raw", "RGBA", 0, 1) + img.save(output_file) def store_raw(data, output_file): - buf = flatten(data[2]) - with open(output_file, "wb") as img: - img.write(buf) + buf = flatten(data[2]) + with open(output_file, "wb") as img: + img.write(buf) try: - port, output_file = sys.argv[1:] + port, output_file = sys.argv[1:] except IndexError: - trace("Usage: grabscreen.py \n") - trace("(imagefile can be rgba: to output raw rgba data)\n") - sys.exit(2) + trace("Usage: grabscreen.py \n") + trace("(imagefile can be rgba: to output raw rgba data)\n") + sys.exit(2) if output_file[:4] == "raw:": - writer = store_raw - output_file = output_file[4:] + writer = store_raw + output_file = output_file[4:] elif output_file[:5] == "rgba:": - writer = store_raw - output_file = output_file[5:] + writer = store_raw + output_file = output_file[5:] else: - writer = store_image + writer = store_image trace("Connecting to %s\n" % port) ser = serial.Serial(port, 921600, timeout=5) diff --git a/misc/zlib b/misc/zlib index f5515753d..a61734ecd 100755 --- a/misc/zlib +++ b/misc/zlib @@ -5,36 +5,36 @@ import zlib import sys def usage(): - print("Usage: zlib [-d | -0..9] ") - print("") - print(" -d Decompress (inflate) the given file") - print(" -0..9 Set deflate level (default is 6)") - print("") - print("If is not given, stdin will be used instead.") + print("Usage: zlib [-d | -0..9] ") + print("") + print(" -d Decompress (inflate) the given file") + print(" -0..9 Set deflate level (default is 6)") + print("") + print("If is not given, stdin will be used instead.") def compress(inf, outf, level): - z = zlib.compressobj(level) - while True: - buf = inf.read(16384) - if buf: - buf = z.compress(buf) - outf.write(buf) - else: - buf = z.flush() - outf.write(buf) - break + z = zlib.compressobj(level) + while True: + buf = inf.read(16384) + if buf: + buf = z.compress(buf) + outf.write(buf) + else: + buf = z.flush() + outf.write(buf) + break def decompress(inf, outf, **kwargs): - z = zlib.decompressobj() - while True: - buf = inf.read(16384) - if buf: - buf = z.decompress(buf) - outf.write(buf) - else: - buf = z.flush() - outf.write(buf) - break + z = zlib.decompressobj() + while True: + buf = inf.read(16384) + if buf: + buf = z.decompress(buf) + outf.write(buf) + else: + buf = z.flush() + outf.write(buf) + break arg0 = sys.argv[0].split("/")[-1] @@ -44,26 +44,26 @@ mode = compress level = 6 if arg0 in ("dezlib", "unzlib"): - mode = decompress + mode = decompress try: - opts, args = getopt.getopt(sys.argv[1:], "dh0123456789") + opts, args = getopt.getopt(sys.argv[1:], "dh0123456789") except getopt.GetoptError: - usage() - exit(2) + usage() + exit(2) for opt, optarg in opts: - if opt == "-h": - usage() - exit() - elif opt == "-d": - mode = decompress - elif opt[1] in "0123456789": - level = int(opt[1]) + if opt == "-h": + usage() + exit() + elif opt == "-d": + mode = decompress + elif opt[1] in "0123456789": + level = int(opt[1]) if args: - inpath = args[0] + inpath = args[0] with open(inpath, "rb") as inf: - with open(outpath, "wb") as outf: - mode(inf, outf, level=level) + with open(outpath, "wb") as outf: + mode(inf, outf, level=level) diff --git a/music/id3-cover b/music/id3-cover index cf8e41c73..b5d3bc347 100755 --- a/music/id3-cover +++ b/music/id3-cover @@ -4,142 +4,142 @@ import getopt from mutagen import mp3, id3 def usage(): - print >> sys.stderr, """\ + print >> sys.stderr, """\ import: cover -i [-f imagefile] audio_file [audio_file ...] export: cover -e [-f imagefile] audio_file remove: cover -x audio_file [audio_file ...] """ - sys.exit(2) + sys.exit(2) def fileext_to_type(ext): - return { - None: None, - "jpeg": "image/jpeg", - "jpg": "image/jpeg", - "png": "image/png", - }.get(ext[1:], None) + return { + None: None, + "jpeg": "image/jpeg", + "jpg": "image/jpeg", + "png": "image/png", + }.get(ext[1:], None) def type_to_fileext(type): - return { - None: None, - "image/jpeg": ".jpeg", - "image/png": ".png", - }.get(type, "jpeg") + return { + None: None, + "image/jpeg": ".jpeg", + "image/png": ".png", + }.get(type, "jpeg") def export_cover(file, cover_file): - try: - filetag = mp3.MP3(file) - except BaseException as e: - print >> sys.stderr, "Error:", e - return False - - if "APIC:" not in filetag: - print >> sys.stderr, "Error: No cover image (APIC frame not found)" - return False - - with open(cover_file or "/dev/stdout", "wb") as cover_fh: - if verbose: - print >> sys.stderr, "Exporting image: %s" % cover_file - cover_fh.write(filetag["APIC:"].data) - return True + try: + filetag = mp3.MP3(file) + except BaseException as e: + print >> sys.stderr, "Error:", e + return False + + if "APIC:" not in filetag: + print >> sys.stderr, "Error: No cover image (APIC frame not found)" + return False + + with open(cover_file or "/dev/stdout", "wb") as cover_fh: + if verbose: + print >> sys.stderr, "Exporting image: %s" % cover_file + cover_fh.write(filetag["APIC:"].data) + return True def import_cover(file, image_data, image_type="image/jpeg"): - TYPE_FRONT_COVER = 3 - ENC_UTF8 = 3 - - try: - filetag = mp3.MP3(file) - except BaseException as e: - print >> sys.stderr, "Error:", e - return False - - filetag.tags.add(id3.APIC( - data=image_data, - mime=image_type, - type=TYPE_FRONT_COVER, - desc=u"", - encoding=ENC_UTF8)) - - if verbose: - print >> sys.stderr, "Updating tags: %s" % file - try: - filetag.save() - except BaseException as e: - print >> sys.stderr, "Error:", e - return False - else: - return True + TYPE_FRONT_COVER = 3 + ENC_UTF8 = 3 + + try: + filetag = mp3.MP3(file) + except BaseException as e: + print >> sys.stderr, "Error:", e + return False + + filetag.tags.add(id3.APIC( + data=image_data, + mime=image_type, + type=TYPE_FRONT_COVER, + desc=u"", + encoding=ENC_UTF8)) + + if verbose: + print >> sys.stderr, "Updating tags: %s" % file + try: + filetag.save() + except BaseException as e: + print >> sys.stderr, "Error:", e + return False + else: + return True def remove_cover(file): - try: - filetag = mp3.MP3(file) - except BaseException as e: - print >> sys.stderr, "Error:", e - return False - - if "APIC:" in filetag: - del filetag["APIC:"] - - if verbose: - print >> sys.stderr, "Updating tags: %s" % file - try: - filetag.save() - except BaseException as e: - print >> sys.stderr, "Error:", e - return False - else: - return True + try: + filetag = mp3.MP3(file) + except BaseException as e: + print >> sys.stderr, "Error:", e + return False + + if "APIC:" in filetag: + del filetag["APIC:"] + + if verbose: + print >> sys.stderr, "Updating tags: %s" % file + try: + filetag.save() + except BaseException as e: + print >> sys.stderr, "Error:", e + return False + else: + return True try: - options, files = getopt.gnu_getopt(sys.argv[1:], "ef:iovx") + options, files = getopt.gnu_getopt(sys.argv[1:], "ef:iovx") except getopt.GetoptError as e: - print >> sys.stderr, "Error:", e - usage() + print >> sys.stderr, "Error:", e + usage() mode = None cover_file = None verbose = False for opt, value in options: - if opt == "-e": mode = "export" - elif opt == "-f": cover_file = value - elif opt == "-i": mode = "import" - elif opt == "-o": mode = "export" - elif opt == "-v": verbose = True - elif opt == "-x": mode = "kill" + if opt == "-e": mode = "export" + elif opt == "-f": cover_file = value + elif opt == "-i": mode = "import" + elif opt == "-o": mode = "export" + elif opt == "-v": verbose = True + elif opt == "-x": mode = "kill" if len(files) < 1: - print >> sys.stderr, "Error: no mp3 files specified" - usage() + print >> sys.stderr, "Error: no mp3 files specified" + usage() if mode == "import": - if cover_file: - cover_fh = open(cover_file, 'rb') - ext = os.path.splitext(cover_file) - image_type = fileext_to_type(ext) - else: - cover_fh = sys.stdin - image_type = None #"image/jpeg" - image_data = cover_fh.read() - for audiofile in files: - import_cover(audiofile, image_data, image_type) + if cover_file: + cover_fh = open(cover_file, 'rb') + ext = os.path.splitext(cover_file) + image_type = fileext_to_type(ext) + else: + cover_fh = sys.stdin + image_type = None #"image/jpeg" + image_data = cover_fh.read() + for audiofile in files: + import_cover(audiofile, image_data, image_type) elif mode == "export": - if len(files) > 1: - print >> sys.stderr, "Error: cannot export multiple covers to one file" - usage() + if len(files) > 1: + print >> sys.stderr, "Error: cannot export multiple covers to one file" + usage() - ret = export_cover(files[0], cover_file) + ret = export_cover(files[0], cover_file) - sys.exit(0 if ret else 1) + sys.exit(0 if ret else 1) elif mode == "kill": - ret = True + ret = True - for audiofile in files: - ret = remove_cover(audiofile) and ret - - sys.exit(0 if ret else 1) + for audiofile in files: + ret = remove_cover(audiofile) and ret + + sys.exit(0 if ret else 1) else: - usage() + usage() diff --git a/music/id3-lyrics b/music/id3-lyrics index 454be156b..4e734b425 100755 --- a/music/id3-lyrics +++ b/music/id3-lyrics @@ -5,101 +5,101 @@ import sys import getopt try: - import mutagen.mp3, mutagen.id3 + import mutagen.mp3, mutagen.id3 except ImportError: - print("The mutagen library is not installed.", file=sys.stderr) - sys.exit(42) + print("The mutagen library is not installed.", file=sys.stderr) + sys.exit(42) def usage(): - print("usage:") - print(" import: lyrics -i [-f lyricfile] audiofile") - print(" export: lyrics -e [-f lyricfile] audiofile") - print(" remove: lyrics -x audiofile") - sys.exit(2) + print("usage:") + print(" import: lyrics -i [-f lyricfile] audiofile") + print(" export: lyrics -e [-f lyricfile] audiofile") + print(" remove: lyrics -x audiofile") + sys.exit(2) def trace(s): - global verbose - if verbose: - print(s, file=sys.stderr) + global verbose + if verbose: + print(s, file=sys.stderr) def to_crlf(s): - return s.replace("\r\n", "\n").replace("\n", "\r\n") + return s.replace("\r\n", "\n").replace("\n", "\r\n") def from_crlf(s): - return s.replace("\r\n", "\n") + return s.replace("\r\n", "\n") # Turn off text mode stdio on Windows (otherwise it writes CR CR LF) if sys.platform == "win32": - import os, msvcrt - for fd in (sys.stdin, sys.stdout, sys.stderr): - msvcrt.setmode(fd.fileno(), os.O_BINARY) + import os, msvcrt + for fd in (sys.stdin, sys.stdout, sys.stderr): + msvcrt.setmode(fd.fileno(), os.O_BINARY) def write_id3(file, lyrics): - tag = mutagen.mp3.MP3(file) - atom = u"USLT::'eng'" - if lyrics is None: - if atom in tag: - del tag[atom] - else: - tag[atom] = mutagen.id3.USLT() - tag[atom].text = lyrics - tag[atom].encoding = 1 - tag[atom].lang = "eng" - tag[atom].desc = u"" - trace("Writing %s" % file) - tag.save() + tag = mutagen.mp3.MP3(file) + atom = u"USLT::'eng'" + if lyrics is None: + if atom in tag: + del tag[atom] + else: + tag[atom] = mutagen.id3.USLT() + tag[atom].text = lyrics + tag[atom].encoding = 1 + tag[atom].lang = "eng" + tag[atom].desc = u"" + trace("Writing %s" % file) + tag.save() def read_id3(file): - tag = mutagen.mp3.MP3(file) - try: - return tag[u"USLT::'eng'"].text - except KeyError: - return None + tag = mutagen.mp3.MP3(file) + try: + return tag[u"USLT::'eng'"].text + except KeyError: + return None mode = None lyricsfile = None verbose = False try: - options, audiofiles = getopt.gnu_getopt(sys.argv[1:], "ef:iovx") + options, audiofiles = getopt.gnu_getopt(sys.argv[1:], "ef:iovx") except getopt.GetoptError as e: - print(e, file=sys.stderr) - usage() + print(e, file=sys.stderr) + usage() for opt, value in options: - if None: pass - elif opt == "-e": mode = "output" - elif opt == "-f": lyricsfile = value - elif opt == "-i": mode = "input" - elif opt == "-o": mode = "output" - elif opt == "-v": verbose = True - elif opt == "-x": mode = "kill" + if None: pass + elif opt == "-e": mode = "output" + elif opt == "-f": lyricsfile = value + elif opt == "-i": mode = "input" + elif opt == "-o": mode = "output" + elif opt == "-v": verbose = True + elif opt == "-x": mode = "kill" if len(audiofiles) == 0: - print("Error: no .mp3 files specified", file=sys.stderr) - usage() + print("Error: no .mp3 files specified", file=sys.stderr) + usage() if mode == "input": - if lyricsfile is None: - f = sys.stdin - else: - f = open(lyricsfile, "r") - lyrics = f.read().decode("utf-8") - lyrics = to_crlf(lyrics) - for file in audiofiles: - write_id3(file, lyrics) + if lyricsfile is None: + f = sys.stdin + else: + f = open(lyricsfile, "r") + lyrics = f.read().decode("utf-8") + lyrics = to_crlf(lyrics) + for file in audiofiles: + write_id3(file, lyrics) elif mode == "output": - if lyricsfile is None: - f = sys.stdout - else: - f = open(lyricsfile, "w") - for file in audiofiles: - lyrics = read_id3(file) - if lyrics: - lyrics = from_crlf(lyrics) - sys.stdout.write(lyrics.encode("utf-8")) + if lyricsfile is None: + f = sys.stdout + else: + f = open(lyricsfile, "w") + for file in audiofiles: + lyrics = read_id3(file) + if lyrics: + lyrics = from_crlf(lyrics) + sys.stdout.write(lyrics.encode("utf-8")) elif mode == "kill": - for file in audiofiles: - write_id3(file, None) + for file in audiofiles: + write_id3(file, None) else: - usage() + usage() diff --git a/music/id3-purge-priv b/music/id3-purge-priv index 141a39e36..011eb0f75 100755 --- a/music/id3-purge-priv +++ b/music/id3-purge-priv @@ -27,6 +27,6 @@ for fname in args: print repr(frame) if remove: del ftag[name] - + if remove: ftag.save() diff --git a/music/id3-sync-rg b/music/id3-sync-rg index a3e31d738..4b059c888 100755 --- a/music/id3-sync-rg +++ b/music/id3-sync-rg @@ -11,16 +11,16 @@ args = sys.argv[1:] for fname in args: print("updating %s" % fname) ftag = mutagen.mp3.MP3(fname) - + trackgain = GainValue.import_tag(ftag, 'track') if trackgain: #print trackgain trackgain.export_id3(ftag) - + albumgain = GainValue.import_tag(ftag, 'album') if albumgain: #print albumgain albumgain.export_id3(ftag) - + if trackgain or albumgain: ftag.save() diff --git a/music/rg-copytags b/music/rg-copytags index 488ea6c11..c105accc9 100755 --- a/music/rg-copytags +++ b/music/rg-copytags @@ -3,14 +3,13 @@ # ffmpeg does not copy RVA2 automatically) import sys import mutagen - from nullroute.mp3tags import * srcfile = sys.argv[1] try: - dstfile = sys.argv[2] + dstfile = sys.argv[2] except IndexError: - dstfile = None + dstfile = None srctag = mutagen.File(srcfile) dsttag = mutagen.mp3.MP3(dstfile) if dstfile else None @@ -18,10 +17,10 @@ dsttag = mutagen.mp3.MP3(dstfile) if dstfile else None gv = GainValue.import_tag(srctag, 'track') if gv: - if dsttag: - gv.export_id3(dsttag) - print dsttag.keys() - dsttag.save() + if dsttag: + gv.export_id3(dsttag) + print dsttag.keys() + dsttag.save() else: - print "No ReplayGain tag found." - print srctag + print "No ReplayGain tag found." + print srctag diff --git a/net/freenet-monitor.py b/net/freenet-monitor.py index 17925d382..d36d2ca40 100755 --- a/net/freenet-monitor.py +++ b/net/freenet-monitor.py @@ -151,7 +151,7 @@ def __init__(self): def display_progress(self, reqid, cooldown=False, failed=False): if cooldown and not (self.last_id == reqid and self.last_kind == "progress"): return - + screen_width, _ = get_screen_size_wxh() remaining_width = screen_width @@ -247,7 +247,7 @@ def display_progress(self, reqid, cooldown=False, failed=False): # create reqid item item_rightside = Item(item_indicators + [" "] + item_mainbar + item_progress) - + remaining_width -= len(" ") id_short = truncate(reqid, remaining_width, pad=True) @@ -688,5 +688,3 @@ def main(): main() except KeyboardInterrupt: sys.exit() - -# vim: ts=4:sw=4:et diff --git a/net/obml-parser b/net/obml-parser index c3b0aa15b..98eee1f87 100755 --- a/net/obml-parser +++ b/net/obml-parser @@ -1,6 +1,5 @@ #!/usr/bin/env python - -# A barebones parser for OBML files used by Opera Mini. +# A barebones parser for OBML files used by Opera Mini # # Originally intended to extract original URLs from saved pages, after Opera dropped # binary compatibilty between minor releases and left me with a bunch of unreadable @@ -10,42 +9,40 @@ import sys import struct class Parser(): - def __init__(self, path): - self.path = path - self.fh = open(path, 'rb') - - def debug(self, typ, data): - print(typ, repr(data), file=sys.stderr) - return data - - def read(self, length): - buf = self.fh.read(length) - if len(buf) < length: - raise IOError("Hit EOF after %d/%d bytes" - % (len(buf), length)) - return self.debug("raw[%d]" % length, buf) - - def read_byte(self): - buf = self.fh.read(1) - data, = struct.unpack('>B', buf) - return self.debug("byte", data) - - def read_short(self): - buf = self.fh.read(2) - data, = struct.unpack('>H', buf) - return self.debug("short", data) - - def read_chunk(self): - length = self.read_short() - buf = self.fh.read(length) - if len(buf) < length: - raise IOError("Hit EOF after %d/%d bytes" - % (len(buf), length)) - return self.debug("chunk[%d]" % length, buf) - - def read_string(self): - buf = self.read_chunk() - return buf.decode('utf-8') + def __init__(self, path): + self.path = path + self.fh = open(path, 'rb') + + def debug(self, typ, data): + print(typ, repr(data), file=sys.stderr) + return data + + def read(self, length): + buf = self.fh.read(length) + if len(buf) < length: + raise IOError("Hit EOF after %d/%d bytes" % (len(buf), length)) + return self.debug("raw[%d]" % length, buf) + + def read_byte(self): + buf = self.fh.read(1) + data, = struct.unpack('>B', buf) + return self.debug("byte", data) + + def read_short(self): + buf = self.fh.read(2) + data, = struct.unpack('>H', buf) + return self.debug("short", data) + + def read_chunk(self): + length = self.read_short() + buf = self.fh.read(length) + if len(buf) < length: + raise IOError("Hit EOF after %d/%d bytes" % (len(buf), length)) + return self.debug("chunk[%d]" % length, buf) + + def read_string(self): + buf = self.read_chunk() + return buf.decode('utf-8') d = {} @@ -53,7 +50,7 @@ f = Parser(sys.argv[1]) magic = f.read(4) if magic != b'\x02\xd3U\x10': - raise IOError('Bad magic number (old OBML format?)') + raise IOError('Bad magic number (old OBML format?)') f.read(3) @@ -61,11 +58,11 @@ version = f.read_byte() f.read_short() # always 240 if version == 15: - f.read(8) + f.read(8) elif version == 16: - f.read(5) + f.read(5) else: - raise IOError('Unknown version %d' % version) + raise IOError('Unknown version %d' % version) page_title = f.read_string() @@ -76,7 +73,7 @@ page_referer = f.read_string() page_url = f.read_string() if page_url[0] == "\0": - page_url = page_referer + page_url[1:] + page_url = page_referer + page_url[1:] print(page_title) print(page_url) diff --git a/net/old-protocols/in.rfingerd b/net/old-protocols/in.rfingerd index 34398ea6d..651f55eac 100755 --- a/net/old-protocols/in.rfingerd +++ b/net/old-protocols/in.rfingerd @@ -9,248 +9,248 @@ import subprocess #s.environ["DEBUG"] = "y" # Syntax: -# [] +# [] # -# is a Python 're'-compatible regexp, matching the raw query received. +# is a Python 're'-compatible regexp, matching the raw query received. # -# First match wins. +# First match wins. # # Next hop: -# If starts with "$", it is parsed as "$," -# and the selected () group is forwarded instead of entire request. -# If "x" in flags, is an executable command. -# If begins with a "|", raw query is given as stdin, -# otherwise - in argv[]. -# If starts with "/", it is a file to be sent. -# If is "!", the query is refused. -# If is "*", the query is transparently forwarded to the host. -# Otherwise, is a host or host:port to forward the query to. +# If starts with "$", it is parsed as "$," +# and the selected () group is forwarded instead of entire request. +# If "x" in flags, is an executable command. +# If begins with a "|", raw query is given as stdin, +# otherwise in argv[]. +# If starts with "/", it is a file to be sent. +# If is "!", the query is refused. +# If is "*", the query is transparently forwarded to the host. +# Otherwise, is a host or host:port to forward the query to. # # Flags: -# "h": do not strip off "@host" part from query when forwarding -# "q": do not display messages about forwarding to another host -# "x": treat as a command to execute +# "h": do not strip off "@host" part from query when forwarding +# "q": do not display messages about forwarding to another host +# "x": treat as a command to execute if os.environ.get("DEBUG", ""): - def DEBUG(fmt, *args): - print("(" + (fmt % args) + ")") + def DEBUG(fmt, *args): + print("(" + (fmt % args) + ")") else: - def DEBUG(fmt, *args): - pass + def DEBUG(fmt, *args): + pass def print(*args): - args = map(str, args) - line = " ".join(args)+"\r\n" - fh = sys.stdout - fh.write(line.encode("utf-8")) - fh.flush() + args = map(str, args) + line = " ".join(args)+"\r\n" + fh = sys.stdout + fh.write(line.encode("utf-8")) + fh.flush() def accept(): - request = sys.stdin.readline().strip() - DEBUG("accept(%r)", request) - result = route(request) - if result is None: - DEBUG("routing failed") - refuse() - else: - forward(*result) + request = sys.stdin.readline().strip() + DEBUG("accept(%r)", request) + result = route(request) + if result is None: + DEBUG("routing failed") + refuse() + else: + forward(*result) def route(request): - DEBUG("route(%r)", request) - - if request == b"/W" or request[:3] == b"/W ": - prefix = b"/W " - request = request[3:] - else: - prefix = b"" - - for rtent in get_routes(): - rule = rtent[0] - nexthop = rtent[1] - try: - flags = rtent[2] - except IndexError: - flags = b"" - - DEBUG("trying route %r", rule) - result = re.search(rule, request) - if not result: - continue - - if b"@" in request: - local, host = request.rsplit(b"@", 1) - else: - local, host = request, b"" - - if nexthop == b"!": - return None - elif nexthop == b"*": - nexthop = host - flags += b"R" - - if nexthop[0] == b"$" and b"," in nexthop: - group, nexthop = nexthop.split(",", 1) - try: - group = int(group[1:]) - except ValueError: - DEBUG("invalid group %r", group) - return None - DEBUG("nextreq = group(%d)", group) - nextreq = result.group(group) - elif b"@" in request and b"h" not in flags: - DEBUG("nextreq = local") - nextreq = local - else: - DEBUG("nextreq = req") - nextreq = request - - return nexthop, prefix+nextreq, flags - return None + DEBUG("route(%r)", request) + + if request == b"/W" or request[:3] == b"/W ": + prefix = b"/W " + request = request[3:] + else: + prefix = b"" + + for rtent in get_routes(): + rule = rtent[0] + nexthop = rtent[1] + try: + flags = rtent[2] + except IndexError: + flags = b"" + + DEBUG("trying route %r", rule) + result = re.search(rule, request) + if not result: + continue + + if b"@" in request: + local, host = request.rsplit(b"@", 1) + else: + local, host = request, b"" + + if nexthop == b"!": + return None + elif nexthop == b"*": + nexthop = host + flags += b"R" + + if nexthop[0] == b"$" and b"," in nexthop: + group, nexthop = nexthop.split(",", 1) + try: + group = int(group[1:]) + except ValueError: + DEBUG("invalid group %r", group) + return None + DEBUG("nextreq = group(%d)", group) + nextreq = result.group(group) + elif b"@" in request and b"h" not in flags: + DEBUG("nextreq = local") + nextreq = local + else: + DEBUG("nextreq = req") + nextreq = request + + return nexthop, prefix+nextreq, flags + return None def forward(addr, req, flags): - DEBUG("forward(%r, %r, %r)", req, addr, flags) - - if b"x" in flags: - if addr.startswith(b"|"): - stdin = True - addr = addr[1:] - else: - stdin = False - - cmdline = [addr] - if req and not stdin: - cmdline += [req] - DEBUG('exec: using %r', cmdline) - - try: - if stdin: - proc = subprocess.Popen(cmdline, stdin=subprocess.PIPE) - DEBUG('exec: writing %r', req) - proc.stdin.write(req + b'\n') - proc.stdin.close() - proc.wait() - else: - subprocess.Popen(cmdline, stdin=open(os.devnull, "w")).wait() - except OSError as e: - print("finger: internal error") - raise - - elif addr.startswith(b"/"): - DEBUG('file: using %r', addr) - try: - for line in open(addr, "rb"): - sys.stdout.write(line) - sys.stdout.flush() - except IOError as e: - print("finger: cannot open plan file") - raise - - else: - if b"R" in flags: - # use raw address as received - addr = (addr, "finger") - else: - addr = parseaddr(addr) - DEBUG('tcp: using %r', addr) - - try: - gai = socket.getaddrinfo(addr[0], addr[1], socket.AF_UNSPEC, - socket.SOCK_STREAM, 0, socket.AI_CANONNAME) - except socket.gaierror as e: - print("finger: getaddrinfo: %s" % e.strerror) - return - - canonname = addr - for family, socktype, proto, _canonname, addr in gai: - if _canonname: canonname = _canonname - DEBUG('tcp: trying %r ', addr, family) - - sock = socket.socket(family, socktype, proto) - sock.settimeout(3) - try: - sock.connect(addr) - except socket.error as e: - straddr = formataddr(family, addr, ignoreport=79) - strerr = e.strerror or e.args[0] - print("Trying %s... %s" % (straddr, strerr)) - continue - - if not b"q" in flags: - print("[%s]" % canonname) - - sock.send(req + b"\r\n") - buf = True - while buf: - buf = sock.recv(4096) - sys.stdout.write(buf) - sock.close() - break + DEBUG("forward(%r, %r, %r)", req, addr, flags) + + if b"x" in flags: + if addr.startswith(b"|"): + stdin = True + addr = addr[1:] + else: + stdin = False + + cmdline = [addr] + if req and not stdin: + cmdline += [req] + DEBUG('exec: using %r', cmdline) + + try: + if stdin: + proc = subprocess.Popen(cmdline, stdin=subprocess.PIPE) + DEBUG('exec: writing %r', req) + proc.stdin.write(req + b'\n') + proc.stdin.close() + proc.wait() + else: + subprocess.Popen(cmdline, stdin=open(os.devnull, "w")).wait() + except OSError as e: + print("finger: internal error") + raise + + elif addr.startswith(b"/"): + DEBUG('file: using %r', addr) + try: + for line in open(addr, "rb"): + sys.stdout.write(line) + sys.stdout.flush() + except IOError as e: + print("finger: cannot open plan file") + raise + + else: + if b"R" in flags: + # use raw address as received + addr = (addr, "finger") + else: + addr = parseaddr(addr) + DEBUG('tcp: using %r', addr) + + try: + gai = socket.getaddrinfo(addr[0], addr[1], socket.AF_UNSPEC, + socket.SOCK_STREAM, 0, socket.AI_CANONNAME) + except socket.gaierror as e: + print("finger: getaddrinfo: %s" % e.strerror) + return + + canonname = addr + for family, socktype, proto, _canonname, addr in gai: + if _canonname: canonname = _canonname + DEBUG('tcp: trying %r ', addr, family) + + sock = socket.socket(family, socktype, proto) + sock.settimeout(3) + try: + sock.connect(addr) + except socket.error as e: + straddr = formataddr(family, addr, ignoreport=79) + strerr = e.strerror or e.args[0] + print("Trying %s... %s" % (straddr, strerr)) + continue + + if not b"q" in flags: + print("[%s]" % canonname) + + sock.send(req + b"\r\n") + buf = True + while buf: + buf = sock.recv(4096) + sys.stdout.write(buf) + sock.close() + break def refuse(): - print("finger: query refused") + print("finger: query refused") def formataddr(family, addr, ignoreport=None): - DEBUG("formataddr(%r, %r, %r)", family, addr, ignoreport) - # LAME - if family == socket.AF_INET: - host, port = addr - if port == ignoreport: - return "%s" % host - else: - return "%s:%s" % (host, port) - elif family == socket.AF_INET6: - host, port, flow, scope = addr - if scope: - host = "%s%%%s" % (host, scope) - if port == ignoreport: - return "%s" % host - else: - return "[%s]:%s" % (host, port) - else: - return repr(addr) + DEBUG("formataddr(%r, %r, %r)", family, addr, ignoreport) + # LAME + if family == socket.AF_INET: + host, port = addr + if port == ignoreport: + return "%s" % host + else: + return "%s:%s" % (host, port) + elif family == socket.AF_INET6: + host, port, flow, scope = addr + if scope: + host = "%s%%%s" % (host, scope) + if port == ignoreport: + return "%s" % host + else: + return "[%s]:%s" % (host, port) + else: + return repr(addr) def parseaddr(addr): - DEBUG("parseaddr(%r)", addr) - ## LAAAAME - host, port = None, "finger" - if addr.startswith(b"[") and b"]" in addr: - endpos = addr.find(b"]") - host = addr[1:endpos] - addr = addr[endpos+1:] - if addr.startswith(b":"): - port = addr[1:] - elif b":" in addr: - host, port = addr.split(b":", 1) - port = port - elif not host: - host = addr - - return host, port + DEBUG("parseaddr(%r)", addr) + ## LAAAAME + host, port = None, "finger" + if addr.startswith(b"[") and b"]" in addr: + endpos = addr.find(b"]") + host = addr[1:endpos] + addr = addr[endpos+1:] + if addr.startswith(b":"): + port = addr[1:] + elif b":" in addr: + host, port = addr.split(b":", 1) + port = port + elif not host: + host = addr + + return host, port def get_routes(): - DEBUG("get_routes(%r)", rules_file) - try: - for line in open(rules_file, "rb"): - line = line.strip() - if line and not line.startswith(b"#"): - yield line.split() - except IOError as e: - print("finger: cannot open configuration file") - raise + DEBUG("get_routes(%r)", rules_file) + try: + for line in open(rules_file, "rb"): + line = line.strip() + if line and not line.startswith(b"#"): + yield line.split() + except IOError as e: + print("finger: cannot open configuration file") + raise try: - rules_file = sys.argv.pop(1) + rules_file = sys.argv.pop(1) except IndexError: - rules_file = os.devnull + rules_file = os.devnull if hasattr(sys.stdin, "detach"): - sys.stdin = sys.stdin.detach() + sys.stdin = sys.stdin.detach() if hasattr(sys.stdout, "detach"): - sys.stdout = sys.stdout.detach() + sys.stdout = sys.stdout.detach() try: - accept() + accept() except Exception as e: - print("finger: internal error") - raise - sys.exit(1) + print("finger: internal error") + raise + sys.exit(1) diff --git a/net/scrape-reddit-comment b/net/scrape-reddit-comment index 9ab74d23f..252c106dc 100755 --- a/net/scrape-reddit-comment +++ b/net/scrape-reddit-comment @@ -18,111 +18,111 @@ rx_msgid = re.compile(r'.*/comments/(\w+)/.*/(\w+)/?$') DELTA_ZERO = datetime.timedelta(0) class FixedOffset(datetime.tzinfo): - def __init__(self, stroffset): - self._name = stroffset - self._offset = datetime.timedelta(hours=int(stroffset[:3]), - minutes=int(stroffset[4:6])) - - def utcoffset(self, dt): - return self._offset - - def tzname(self, dt): - return self._name - - def dst(self, dt): - return DELTA_ZERO + def __init__(self, stroffset): + self._name = stroffset + self._offset = datetime.timedelta(hours=int(stroffset[:3]), + minutes=int(stroffset[4:6])) + + def utcoffset(self, dt): + return self._offset + + def tzname(self, dt): + return self._name + + def dst(self, dt): + return DELTA_ZERO def scrape(url): - req = urllib2.Request(url + "?limit=1", headers={"User-Agent": "Mozilla/5.0"}) - page = urllib2.urlopen(req).read() - body = BeautifulSoup(page, convertEntities=BeautifulSoup.HTML_ENTITIES) - - title = body.find("title").renderContents() - - commentarea = body.find("div", {"class": "commentarea"}) - infobar = commentarea.find("div", {"class": "infobar"}) - - if infobar is None: - thing = body.find("div", {"class": re.compile(r'\bthing\b.*\blink\b')}) - else: - thing = commentarea - - entry = thing.find("div", {"class": re.compile(r'\bentry\b')}) - - # .entry > .tagline > {.author, time} - - tagline = entry.find("p", {"class": "tagline"}) - - author = tagline.find("a", {"class": re.compile(r'\bauthor\b')}) - if author: - author = author.renderContents() - else: - author = '"(deleted user)"' - - strtime = tagline.find("time")["datetime"] - - mx = rx_time.match(strtime) - if mx: - ntime = datetime.datetime( - year=int(mx.group(1)), - month=int(mx.group(2)), - day=int(mx.group(3)), - hour=int(mx.group(4)), - minute=int(mx.group(5)), - second=int(mx.group(6)), - microsecond=int(mx.group(7) or 0), - tzinfo=FixedOffset(mx.group(8)), - ) - else: - raise Exception("rx_time not matched in %r" % strtime) - - # .entry > {.title | .bylink} - - link = entry.find("a", {"class": re.compile(r'\btitle\b')}) \ - or entry.find("a", {"class": "bylink"}) - purl = link["href"] - if purl.startswith("/"): - purl = "http://www.reddit.com" + purl - - # .entry > .usertext-body > .md - - txtbody = entry.find("div", {"class": "usertext-body"}) - if txtbody: - text = txtbody.find("div", {"class": "md"}).renderContents() .strip() - else: - text = "(No content)" - - # output - - yield {"url": url, - "subject": title, - "author": author, - "date": ntime, - "text": text} + req = urllib2.Request(url + "?limit=1", headers={"User-Agent": "Mozilla/5.0"}) + page = urllib2.urlopen(req).read() + body = BeautifulSoup(page, convertEntities=BeautifulSoup.HTML_ENTITIES) + + title = body.find("title").renderContents() + + commentarea = body.find("div", {"class": "commentarea"}) + infobar = commentarea.find("div", {"class": "infobar"}) + + if infobar is None: + thing = body.find("div", {"class": re.compile(r'\bthing\b.*\blink\b')}) + else: + thing = commentarea + + entry = thing.find("div", {"class": re.compile(r'\bentry\b')}) + + # .entry > .tagline > {.author, time} + + tagline = entry.find("p", {"class": "tagline"}) + + author = tagline.find("a", {"class": re.compile(r'\bauthor\b')}) + if author: + author = author.renderContents() + else: + author = '"(deleted user)"' + + strtime = tagline.find("time")["datetime"] + + mx = rx_time.match(strtime) + if mx: + ntime = datetime.datetime( + year=int(mx.group(1)), + month=int(mx.group(2)), + day=int(mx.group(3)), + hour=int(mx.group(4)), + minute=int(mx.group(5)), + second=int(mx.group(6)), + microsecond=int(mx.group(7) or 0), + tzinfo=FixedOffset(mx.group(8)), + ) + else: + raise Exception("rx_time not matched in %r" % strtime) + + # .entry > {.title | .bylink} + + link = entry.find("a", {"class": re.compile(r'\btitle\b')}) \ + or entry.find("a", {"class": "bylink"}) + purl = link["href"] + if purl.startswith("/"): + purl = "http://www.reddit.com" + purl + + # .entry > .usertext-body > .md + + txtbody = entry.find("div", {"class": "usertext-body"}) + if txtbody: + text = txtbody.find("div", {"class": "md"}).renderContents() .strip() + else: + text = "(No content)" + + # output + + yield {"url": url, + "subject": title, + "author": author, + "date": ntime, + "text": text} TIMEFMT_MBOX = "%a %b %_d %H:%M:%S %Y" TIMEFMT_MIME = "%a, %d %b %Y %H:%M:%S %z" for url in sys.argv[1:]: - for comment in scrape(url): - mboxdate = comment["date"].strftime(TIMEFMT_MBOX) - mimedate = comment["date"].strftime(TIMEFMT_MIME) - - msgid = "%s.%s@reddit" % (comment["author"], - comment["date"].isoformat()) - author = "%s@reddit" % comment["author"] - length = len(comment["text"]) + 1 - - print("From %s %s" % (author, mboxdate)) - print("URL: %s" % comment["url"]) - print("Message-ID: <%s>" % msgid) - print("From: <%s>" % author) - print("Date: %s (%s)" % (mimedate, comment["date"].isoformat())) - print("Subject: %s" % comment["subject"]) - #print("Content-Type: text/plain; charset=utf-8") - print("Content-Type: text/html; charset=utf-8") - print("Content-Length: %d" % length) - print("") - print(comment["text"]) - print("") - sys.stdout.flush() + for comment in scrape(url): + mboxdate = comment["date"].strftime(TIMEFMT_MBOX) + mimedate = comment["date"].strftime(TIMEFMT_MIME) + + msgid = "%s.%s@reddit" % (comment["author"], + comment["date"].isoformat()) + author = "%s@reddit" % comment["author"] + length = len(comment["text"]) + 1 + + print("From %s %s" % (author, mboxdate)) + print("URL: %s" % comment["url"]) + print("Message-ID: <%s>" % msgid) + print("From: <%s>" % author) + print("Date: %s (%s)" % (mimedate, comment["date"].isoformat())) + print("Subject: %s" % comment["subject"]) + #print("Content-Type: text/plain; charset=utf-8") + print("Content-Type: text/html; charset=utf-8") + print("Content-Length: %d" % length) + print("") + print(comment["text"]) + print("") + sys.stdout.flush() diff --git a/net/unixify b/net/unixify index d2477574c..6c1e90ec4 100755 --- a/net/unixify +++ b/net/unixify @@ -9,159 +9,159 @@ import sys import threading class UnixifyRelay(threading.Thread): - def __init__(self, listenaf=None, listenaddr=None, serveraf=None, serveraddr=None): - threading.Thread.__init__(self) - self.listenaf = listenaf - self.listenaddr = listenaddr - self.serveraf = serveraf - self.serveraddr = serveraddr - - self.debug = False - self.maxconns = 0 - self.listencookie = None - self.servercookie = None - - self.poolsem = None - - def run(self): - if self.maxconns: - self.poolsem = threading.Semaphore(value=self.maxconns) - - self.cleanup() - self.listenfd = socket.socket(self.listenaf, socket.SOCK_STREAM) - self.listenfd.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) - self.listenfd.bind(self.listenaddr) - self.listenfd.listen(64) - - while True: - try: - clientfd, clientaddr = self.listenfd.accept() - except KeyboardInterrupt: - self.listenfd.close() - self.cleanup() - sys.exit() - - worker = UnixifyRelayWorker(self, clientfd) - worker.daemon = True - worker.start() - - def cleanup(self): - if self.listenaf == socket.AF_UNIX \ - and self.listenaddr[0] != '\0' \ - and os.path.exists(self.listenaddr): - os.unlink(self.listenaddr) + def __init__(self, listenaf=None, listenaddr=None, serveraf=None, serveraddr=None): + threading.Thread.__init__(self) + self.listenaf = listenaf + self.listenaddr = listenaddr + self.serveraf = serveraf + self.serveraddr = serveraddr + + self.debug = False + self.maxconns = 0 + self.listencookie = None + self.servercookie = None + + self.poolsem = None + + def run(self): + if self.maxconns: + self.poolsem = threading.Semaphore(value=self.maxconns) + + self.cleanup() + self.listenfd = socket.socket(self.listenaf, socket.SOCK_STREAM) + self.listenfd.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) + self.listenfd.bind(self.listenaddr) + self.listenfd.listen(64) + + while True: + try: + clientfd, clientaddr = self.listenfd.accept() + except KeyboardInterrupt: + self.listenfd.close() + self.cleanup() + sys.exit() + + worker = UnixifyRelayWorker(self, clientfd) + worker.daemon = True + worker.start() + + def cleanup(self): + if self.listenaf == socket.AF_UNIX \ + and self.listenaddr[0] != '\0' \ + and os.path.exists(self.listenaddr): + os.unlink(self.listenaddr) class UnixifyRelayWorker(threading.Thread): - def __init__(self, listener, clientfd): - threading.Thread.__init__(self) - self.listener = listener - self.clientfd = clientfd - - def run(self): - if self.listener.poolsem: - self.listener.poolsem.acquire() - - if self.listener.listencookie: - length = len(self.listener.listencookie) - data = self.clientfd.recv(length) - if data != self.listener.listencookie: - print(self.clientfd.fileno(), "bad cookie") - self.clientfd.close() - return - - self.serverfd = socket.socket(self.listener.serveraf, socket.SOCK_STREAM) - try: - self.serverfd.connect(self.listener.serveraddr) - except socket.error: - self.clientfd.close() - raise - - if self.listener.servercookie: - self.send(self.serverfd, self.listener.servercookie) - - while True: - r, w, x = [self.clientfd, self.serverfd], [], [] - r, w, x = select.select(r, w, x) - for ifd in r: - if ifd == self.clientfd: - ofd = self.serverfd - else: - ofd = self.clientfd - - data = ifd.recv(65536) - if self.listener.debug: - print(ifd.fileno(), repr(data)) - if data: - self.send(ofd, data) - else: - ifd.close() - ofd.close() - return - - if self.listener.poolsem: - self.listener.poolsem.release() - - def send(self, fd, data): - pos = 0 - size = len(data) - while size: - sent = fd.send(data[pos:]) - pos += sent - size -= sent + def __init__(self, listener, clientfd): + threading.Thread.__init__(self) + self.listener = listener + self.clientfd = clientfd + + def run(self): + if self.listener.poolsem: + self.listener.poolsem.acquire() + + if self.listener.listencookie: + length = len(self.listener.listencookie) + data = self.clientfd.recv(length) + if data != self.listener.listencookie: + print(self.clientfd.fileno(), "bad cookie") + self.clientfd.close() + return + + self.serverfd = socket.socket(self.listener.serveraf, socket.SOCK_STREAM) + try: + self.serverfd.connect(self.listener.serveraddr) + except socket.error: + self.clientfd.close() + raise + + if self.listener.servercookie: + self.send(self.serverfd, self.listener.servercookie) + + while True: + r, w, x = [self.clientfd, self.serverfd], [], [] + r, w, x = select.select(r, w, x) + for ifd in r: + if ifd == self.clientfd: + ofd = self.serverfd + else: + ofd = self.clientfd + + data = ifd.recv(65536) + if self.listener.debug: + print(ifd.fileno(), repr(data)) + if data: + self.send(ofd, data) + else: + ifd.close() + ofd.close() + return + + if self.listener.poolsem: + self.listener.poolsem.release() + + def send(self, fd, data): + pos = 0 + size = len(data) + while size: + sent = fd.send(data[pos:]) + pos += sent + size -= sent def usage(): - print("Usage: unixify [-c listencookie] [-C servercookie] ") - return 2 + print("Usage: unixify [-c listencookie] [-C servercookie] ") + return 2 def parse_addr(address): - if address[0] == '/': - return socket.AF_UNIX, address - elif address[0] == '@': - return socket.AF_UNIX, '\0'+address[1:] - elif address[0] == '[': - # TODO TODO TODO TODO TODO - pos = address.index(']') - host = address[1:pos-1] - if address[pos+1] == ':': - port = int(address[pos+2:]) - if ':' in host: - return socket.AF_INET6, (host, port) - # FIXME FIXME FIXME FIXME - else: - host, port = address.rsplit(':', 1) - return socket.AF_INET, (host, int(port)) - - raise ValueError("invalid address format: %r" % address) + if address[0] == '/': + return socket.AF_UNIX, address + elif address[0] == '@': + return socket.AF_UNIX, '\0'+address[1:] + elif address[0] == '[': + # TODO TODO TODO TODO TODO + pos = address.index(']') + host = address[1:pos-1] + if address[pos+1] == ':': + port = int(address[pos+2:]) + if ':' in host: + return socket.AF_INET6, (host, port) + # FIXME FIXME FIXME FIXME + else: + host, port = address.rsplit(':', 1) + return socket.AF_INET, (host, int(port)) + + raise ValueError("invalid address format: %r" % address) def read_cookie_file(path): - return open(path, 'rb').read(1024) + return open(path, 'rb').read(1024) def main(): - relay = UnixifyRelay() - - try: - opts, args = getopt.gnu_getopt(sys.argv[1:], 'C:Dc:') - except getopt.GetoptError as e: - print("unixify:", e.msg, file=sys.stderr) - return usage() - - for opt, optarg in opts: - if False: - pass - elif opt == '-C': - relay.servercookie = read_cookie_file(optarg) - elif opt == '-D': - relay.debug = True - elif opt == '-c': - relay.listencookie = read_cookie_file(optarg) - - try: - relay.listenaf, relay.listenaddr = parse_addr(args.pop(0)) - relay.serveraf, relay.serveraddr = parse_addr(args.pop(0)) - except IndexError: - print("unixify: not enough arguments", file=sys.stderr) - return usage() - - relay.run() + relay = UnixifyRelay() + + try: + opts, args = getopt.gnu_getopt(sys.argv[1:], 'C:Dc:') + except getopt.GetoptError as e: + print("unixify:", e.msg, file=sys.stderr) + return usage() + + for opt, optarg in opts: + if False: + pass + elif opt == '-C': + relay.servercookie = read_cookie_file(optarg) + elif opt == '-D': + relay.debug = True + elif opt == '-c': + relay.listencookie = read_cookie_file(optarg) + + try: + relay.listenaf, relay.listenaddr = parse_addr(args.pop(0)) + relay.serveraf, relay.serveraddr = parse_addr(args.pop(0)) + except IndexError: + print("unixify: not enough arguments", file=sys.stderr) + return usage() + + relay.run() sys.exit(main()) diff --git a/net/update-etchosts.py b/net/update-etchosts.py index 1ac5e8ab2..d2ede39af 100755 --- a/net/update-etchosts.py +++ b/net/update-etchosts.py @@ -5,57 +5,57 @@ import sys if sys.platform == "win32": - HOSTS_PATH = os.path.expandvars("%SystemRoot%/System32/drivers/etc/hosts") + HOSTS_PATH = os.path.expandvars("%SystemRoot%/System32/drivers/etc/hosts") else: - HOSTS_PATH = "/etc/hosts" + HOSTS_PATH = "/etc/hosts" local_domains = (".home", - ".nullroute.eu.org",) + ".nullroute.eu.org",) local_prefixes = ("192.168.",) def is_local_name(name): - return any(name.endswith(i) for i in local_domains) + return any(name.endswith(i) for i in local_domains) def is_local_addr(af, addr): - return any(addr.startswith(i) for i in local_prefixes) + return any(addr.startswith(i) for i in local_prefixes) def resolve_addr(name): - gai = socket.getaddrinfo(name, None) - for af, sol, proto, canon, sa in gai: - if af in (socket.AF_INET, socket.AF_INET6): - addr = sa[0] - else: - continue - print("... %s" % addr) - if is_local_addr(af, addr) and not is_local_name(name): - continue - else: - yield addr + gai = socket.getaddrinfo(name, None) + for af, sol, proto, canon, sa in gai: + if af in (socket.AF_INET, socket.AF_INET6): + addr = sa[0] + else: + continue + print("... %s" % addr) + if is_local_addr(af, addr) and not is_local_name(name): + continue + else: + yield addr def update_names(input): - fixup = False - pastnames = set() - for line in input: - if line == "#begin fixup": - fixup = True - yield line - elif line == "#end fixup": - fixup = False - yield line - elif not line or line.startswith("#"): - yield line - elif fixup: - names = line.split() - addr = names.pop(0) - if names[0] in pastnames: - continue - print("Updating %r" % names[0]) - for addr in resolve_addr(names[0]): - yield "\t".join([addr]+names) - pastnames.update(names) - else: - yield line + fixup = False + pastnames = set() + for line in input: + if line == "#begin fixup": + fixup = True + yield line + elif line == "#end fixup": + fixup = False + yield line + elif not line or line.startswith("#"): + yield line + elif fixup: + names = line.split() + addr = names.pop(0) + if names[0] in pastnames: + continue + print("Updating %r" % names[0]) + for addr in resolve_addr(names[0]): + yield "\t".join([addr]+names) + pastnames.update(names) + else: + yield line # Stage 1: remove old entries to avoid breaking getaddrinfo @@ -66,39 +66,39 @@ def update_names(input): fixup = False for line in open(HOSTS_PATH, "r"): - if fixup and line.startswith("##"): - line = line[7:] - input += line - - line = line.rstrip('\r\n') - if line == "#begin fixup": - fixup = True - elif line == "#end fixup": - fixup = False - elif fixup: - line = "##" + line - output += line + "\n" + if fixup and line.startswith("##"): + line = line[7:] + input += line + + line = line.rstrip('\r\n') + if line == "#begin fixup": + fixup = True + elif line == "#end fixup": + fixup = False + elif fixup: + line = "##" + line + output += line + "\n" try: - print("Temporarily removing dynamic entries") - open(HOSTS_PATH, "w").write(output) + print("Temporarily removing dynamic entries") + open(HOSTS_PATH, "w").write(output) except: - print("Failed. Recovering old file") - open(HOSTS_PATH, "w").write(input) - raise + print("Failed. Recovering old file") + open(HOSTS_PATH, "w").write(input) + raise # Stage 2: add new entries output = "" try: - print("Updating entries") - for line in update_names(input.splitlines()): - output += line + "\n" + print("Updating entries") + for line in update_names(input.splitlines()): + output += line + "\n" except: - print("Failed. Recovering old file") - open(HOSTS_PATH, "w").write(input) - raise + print("Failed. Recovering old file") + open(HOSTS_PATH, "w").write(input) + raise else: - print("Writing new hosts file") - open(HOSTS_PATH, "w").write(output) + print("Writing new hosts file") + open(HOSTS_PATH, "w").write(output) diff --git a/security/git-credential-lib b/security/git-credential-lib index ec47fd3c1..d28daf719 100755 --- a/security/git-credential-lib +++ b/security/git-credential-lib @@ -1,460 +1,459 @@ #!/usr/bin/env python2 -# -*- mode: python -*- from __future__ import print_function import getopt import sys def usage(): - print("usage: %s [options] " % sys.argv[0]) - print() - print("options:") - print(" -I, --[no-]interactive allow store to prompt for credentials") - list_stores() - print() - print("operations: get, store, erase") + print("usage: %s [options] " % sys.argv[0]) + print() + print("options:") + print(" -I, --[no-]interactive allow store to prompt for credentials") + list_stores() + print() + print("operations: get, store, erase") def list_stores(): - print() - print("supported credential stores: ('+' = available)") - for name, klass in sorted(stores.items()): - flag = "+" if klass().available() else " " - descr = getattr(klass, "_description_", "Undocumented") - print(" %1s %-15s %s" % (flag, name, descr)) + print() + print("supported credential stores: ('+' = available)") + for name, klass in sorted(stores.items()): + flag = "+" if klass().available() else " " + descr = getattr(klass, "_description_", "Undocumented") + print(" %1s %-15s %s" % (flag, name, descr)) def attr_is_private(k): - return k == "password" + return k == "password" def attr_public(attr): - #return dict((k, attr[k]) for k in attr if not attr_is_private(k)) - return {k: attr[k] for k in attr if not attr_is_private(k)} + #return dict((k, attr[k]) for k in attr if not attr_is_private(k)) + return {k: attr[k] for k in attr if not attr_is_private(k)} def dict_encode(d): - return {k.encode("utf-8"): v.encode("utf-8") for k, v in d.items()} + return {k.encode("utf-8"): v.encode("utf-8") for k, v in d.items()} def dict_decode(d): - return {k.decode("utf-8"): v.decode("utf-8") for k, v in d.items()} + return {k.decode("utf-8"): v.decode("utf-8") for k, v in d.items()} def make_uri(attr, anonymous=False, nullpath=False): - uri = "" - if "protocol" in attr: - uri += "%(protocol)s://" % attr - if "host" in attr: - if "username" in attr and anonymous is False: - uri += "%(username)s@" % attr - uri += attr["host"] - if "port" in attr: - uri += ":%(port)s" % attr - if "path" in attr: - uri += "/%(path)s" % attr - elif not nullpath: - uri += "/" - return uri + uri = "" + if "protocol" in attr: + uri += "%(protocol)s://" % attr + if "host" in attr: + if "username" in attr and anonymous is False: + uri += "%(username)s@" % attr + uri += attr["host"] + if "port" in attr: + uri += ":%(port)s" % attr + if "path" in attr: + uri += "/%(path)s" % attr + elif not nullpath: + uri += "/" + return uri def read_description(): - attr = {} - for line in sys.stdin.read().splitlines(): - if not len(line): - break - try: - if hasattr(line, "decode"): - line = line.decode("utf-8") - key, val = line.rstrip("\n").split("=", 1) - except ValueError: - print("error: invalid input %r" % line, file=sys.stderr) - raise - else: - attr[key] = val - return attr + attr = {} + for line in sys.stdin.read().splitlines(): + if not len(line): + break + try: + if hasattr(line, "decode"): + line = line.decode("utf-8") + key, val = line.rstrip("\n").split("=", 1) + except ValueError: + print("error: invalid input %r" % line, file=sys.stderr) + raise + else: + attr[key] = val + return attr def write_description(attr): - for key, val in attr.items(): - line = "%s=%s\n" % (key, val) - sys.stdout.write(line) + for key, val in attr.items(): + line = "%s=%s\n" % (key, val) + sys.stdout.write(line) class CredStore(object): - _description_ = None - _required_ = [] - - def __init__(self, options=None): - raise NotImplementedError() - - def available(self): - return False - - def handle(self, op, attr): - if not self.available(): - return {} - if not all(n in attr for n in self._required_): - return {} - if op == "store" and "password" not in attr: - return {} - try: - func = getattr(self, "_cred_%s" % op) - except AttributeError: - # ignore unknown operations for future extensibility - return {} - else: - return func(attr) or {} + _description_ = None + _required_ = [] + + def __init__(self, options=None): + raise NotImplementedError() + + def available(self): + return False + + def handle(self, op, attr): + if not self.available(): + return {} + if not all(n in attr for n in self._required_): + return {} + if op == "store" and "password" not in attr: + return {} + try: + func = getattr(self, "_cred_%s" % op) + except AttributeError: + # ignore unknown operations for future extensibility + return {} + else: + return func(attr) or {} class DummyLoopbackStore(CredStore): - _description_ = "dummy test store" + _description_ = "dummy test store" - def __init__(self, options=None): - pass + def __init__(self, options=None): + pass - def available(self): - return True + def available(self): + return True - def _cred_get(self, attr): - print("dummy: get", attr, file=sys.stderr) - return attr + def _cred_get(self, attr): + print("dummy: get", attr, file=sys.stderr) + return attr - def _cred_store(self, attr): - print("dummy: store", attr, file=sys.stderr) - return None + def _cred_store(self, attr): + print("dummy: store", attr, file=sys.stderr) + return None - def _cred_erase(self, attr): - print("dummy: erase", attr, file=sys.stderr) - return None + def _cred_erase(self, attr): + print("dummy: erase", attr, file=sys.stderr) + return None class GnomeKeyringStore(CredStore): - _description_ = "GNOME Keyring" - _required_ = ["protocol", "host"] - - def __init__(self, options=None): - try: - from gi.repository import GnomeKeyring, GLib - GLib.set_application_name("git") - self.gk = GnomeKeyring - self.gi = True - except ImportError: - try: - import glib - import gnomekeyring - glib.set_application_name("git") - self.gk = gnomekeyring - except ImportError: - self.gk = None - self.gi = False - - def available(self): - return self.gk and self.gk.is_available() - - def map_attributes(self, inattr): - outattr = {} - for key, val in inattr.items(): - if key == "host": - outattr["server"] = val - elif key in ("port", "protocol"): - outattr[key] = val - elif key == "username": - outattr["user"] = val - else: - outattr["git:%s" % key] = val - return outattr - - def unmap_attributes(self, outattr): - inattr = {} - for key, val in outattr.items(): - if key.startswith("git:"): - inattr[key[4:]] = val - elif key in ("port", "protocol"): - inattr[key] = val - elif key == "server": - inattr["host"] = val - elif key == "user": - inattr["username"] = val - else: - inattr[key] = val - return inattr - - def make_keyring_object(self, attr): - # TODO: This is ported from git//contrib/credential/gnome-keyring, - # but not used yet because putting host in "object" is redundant - - host = attr.get("host", "") - port = attr.get("port", 0) - path = attr.get("path", "") - - if port: - return "%s:%d/%s" % (host, port, path) - else: - return "%s/%s" % (host, path) - - def _gi_find(self, attr): - # TODO: Does not yet find nonstandard attributes... - """ - find_type = self.gk.ItemType.NETWORK_PASSWORD - #find_attr = self.gk.AttributeList(find_attr) - #find_attr = self.GLib.Array() - #for k, v in self.map_attributes(attr_public(attr)).items(): - # find_attr. - err, results = self.gk.find_items_sync(find_type, find_attr) - print(repr(err), repr(results)) - """ - find_attr = self.map_attributes(attr_public(attr)) - - err, results = self.gk.find_network_password_sync( - find_attr.get("user", None), - find_attr.get("domain", None), - find_attr.get("server", None), - find_attr.get("path", None), - find_attr.get("protocol", None), - find_attr.get("authtype", None), - attr.get("port", 0)) - return results - - def _gkr_find(self, attr): - find_type = self.gk.ITEM_NETWORK_PASSWORD - find_attr = self.map_attributes(attr_public(attr)) - find_attr = dict_encode(find_attr) - try: - return self.gk.find_items_sync(find_type, find_attr) - except self.gk.NoMatchError: - return [] - - def _cred_get(self, attr): - if self.gi: - results = self._gi_find(attr) - if not results: - return None - for res in results: - for k in ("user", "domain", "server", "object", - "protocol", "authtype", "port", "password"): - v = getattr(res, k) - if v: - attr[k] = v - return self.unmap_attributes(attr) - else: - results = self._gkr_find(attr) - if not results: - return None - for res in results: - attr.update(res.attributes) - attr["password"] = res.secret - return self.unmap_attributes(attr) - - def _cred_store(self, attr): - if self.gi: - err, keyring = self.gk.get_default_keyring_sync() - - item_attr = self.map_attributes(attr_public(attr)) - - err = self.gk.set_network_password_sync( - keyring, - item_attr.get("user", None), - item_attr.get("domain", None), - item_attr.get("server", None), - item_attr.get("path", None), - item_attr.get("protocol", None), - item_attr.get("authtype", None), - item_attr.get("port", 0), - attr["password"]) - else: - keyring = self.gk.get_default_keyring_sync() - item_type = self.gk.ITEM_NETWORK_PASSWORD - display_name = attr["host"] - item_attr = self.map_attributes(attr_public(attr)) - secret = attr["password"] - update = True - self.gk.item_create_sync(keyring, item_type, display_name, - item_attr, secret, update) - - def _cred_erase(self, attr): - if self.gi: - results = self._gi_find(attr) - for res in results: - if "password" in attr and res.password != attr["password"]: - continue - self.gk.item_delete_sync(res.keyring, res.item_id) - else: - results = self._gkr_find(attr) - for res in results: - if "password" in attr and res.secret != attr["password"]: - continue - self.gk.item_delete_sync(res.keyring, res.item_id) + _description_ = "GNOME Keyring" + _required_ = ["protocol", "host"] + + def __init__(self, options=None): + try: + from gi.repository import GnomeKeyring, GLib + GLib.set_application_name("git") + self.gk = GnomeKeyring + self.gi = True + except ImportError: + try: + import glib + import gnomekeyring + glib.set_application_name("git") + self.gk = gnomekeyring + except ImportError: + self.gk = None + self.gi = False + + def available(self): + return self.gk and self.gk.is_available() + + def map_attributes(self, inattr): + outattr = {} + for key, val in inattr.items(): + if key == "host": + outattr["server"] = val + elif key in ("port", "protocol"): + outattr[key] = val + elif key == "username": + outattr["user"] = val + else: + outattr["git:%s" % key] = val + return outattr + + def unmap_attributes(self, outattr): + inattr = {} + for key, val in outattr.items(): + if key.startswith("git:"): + inattr[key[4:]] = val + elif key in ("port", "protocol"): + inattr[key] = val + elif key == "server": + inattr["host"] = val + elif key == "user": + inattr["username"] = val + else: + inattr[key] = val + return inattr + + def make_keyring_object(self, attr): + # TODO: This is ported from git//contrib/credential/gnome-keyring, + # but not used yet because putting host in "object" is redundant + + host = attr.get("host", "") + port = attr.get("port", 0) + path = attr.get("path", "") + + if port: + return "%s:%d/%s" % (host, port, path) + else: + return "%s/%s" % (host, path) + + def _gi_find(self, attr): + # TODO: Does not yet find nonstandard attributes... + """ + find_type = self.gk.ItemType.NETWORK_PASSWORD + #find_attr = self.gk.AttributeList(find_attr) + #find_attr = self.GLib.Array() + #for k, v in self.map_attributes(attr_public(attr)).items(): + # find_attr. + err, results = self.gk.find_items_sync(find_type, find_attr) + print(repr(err), repr(results)) + """ + find_attr = self.map_attributes(attr_public(attr)) + + err, results = self.gk.find_network_password_sync( + find_attr.get("user", None), + find_attr.get("domain", None), + find_attr.get("server", None), + find_attr.get("path", None), + find_attr.get("protocol", None), + find_attr.get("authtype", None), + attr.get("port", 0)) + return results + + def _gkr_find(self, attr): + find_type = self.gk.ITEM_NETWORK_PASSWORD + find_attr = self.map_attributes(attr_public(attr)) + find_attr = dict_encode(find_attr) + try: + return self.gk.find_items_sync(find_type, find_attr) + except self.gk.NoMatchError: + return [] + + def _cred_get(self, attr): + if self.gi: + results = self._gi_find(attr) + if not results: + return None + for res in results: + for k in ("user", "domain", "server", "object", + "protocol", "authtype", "port", "password"): + v = getattr(res, k) + if v: + attr[k] = v + return self.unmap_attributes(attr) + else: + results = self._gkr_find(attr) + if not results: + return None + for res in results: + attr.update(res.attributes) + attr["password"] = res.secret + return self.unmap_attributes(attr) + + def _cred_store(self, attr): + if self.gi: + err, keyring = self.gk.get_default_keyring_sync() + + item_attr = self.map_attributes(attr_public(attr)) + + err = self.gk.set_network_password_sync( + keyring, + item_attr.get("user", None), + item_attr.get("domain", None), + item_attr.get("server", None), + item_attr.get("path", None), + item_attr.get("protocol", None), + item_attr.get("authtype", None), + item_attr.get("port", 0), + attr["password"]) + else: + keyring = self.gk.get_default_keyring_sync() + item_type = self.gk.ITEM_NETWORK_PASSWORD + display_name = attr["host"] + item_attr = self.map_attributes(attr_public(attr)) + secret = attr["password"] + update = True + self.gk.item_create_sync(keyring, item_type, display_name, + item_attr, secret, update) + + def _cred_erase(self, attr): + if self.gi: + results = self._gi_find(attr) + for res in results: + if "password" in attr and res.password != attr["password"]: + continue + self.gk.item_delete_sync(res.keyring, res.item_id) + else: + results = self._gkr_find(attr) + for res in results: + if "password" in attr and res.secret != attr["password"]: + continue + self.gk.item_delete_sync(res.keyring, res.item_id) class Win32CredentialStore(CredStore): - _description_ = "Windows Credential Manager" - _required_ = ["protocol", "host"] - # WCM doesn't support wildcard matching for generic credentials, - # I can't be bothered to expand the code to retry without protocol, - # and the only relevant protocol is HTTPS anyway. - - ERROR_NOT_FOUND = 1168 - ERROR_CANCELLED = 1223 - SILENT_ERRORS = {ERROR_NOT_FOUND, ERROR_CANCELLED} - - interactive = True - - def __init__(self, options=None): - if options: - self.interactive = options.get("interactive", True) - - try: - import win32con, win32cred, pywintypes - self._con = win32con - self._cred = win32cred - self._types = pywintypes - except ImportError: - self._cred = None - - def available(self): - return bool(self._cred and self._cred.CredUIPromptForCredentials) - - def make_target_name(self, attr): - # Windows credential manager identifies generic credentials by a - # "target name", which can be any string. We use an URI here. - # - # The name is also prefixed with "git:" for compatibility with - # `git-credential-winstore`. This may or may not be temporary; - # see https://github.com/anurse/git-credential-winstore/pull/19 - return "git:%s" % make_uri(attr, anonymous=True, nullpath=True) - - def _cred_get(self, attr): - target = self.make_target_name(attr) - - try: - cred = self._cred.CredRead(target, self._cred.CRED_TYPE_GENERIC) - except self._types.error as e: - if e.winerror == self.ERROR_NOT_FOUND: - cred = None - else: - print("Error: %s(): %s" % (e.funcname, e.strerror), - file=sys.stderr) - return None - - if cred: - if cred["UserName"] is not None: - attr["username"] = cred["UserName"] - if len(cred["CredentialBlob"]): - attr["password"] = cred["CredentialBlob"].decode("utf-16-le") - for cattr in cred["Attributes"]: - attr[cattr["Keyword"]] = cattr["Value"].decode("utf-8") - return attr - elif self.interactive: - user = attr.get("username", None) - - flags = self._cred.CREDUI_FLAGS_GENERIC_CREDENTIALS - flags |= self._cred.CREDUI_FLAGS_EXCLUDE_CERTIFICATES - # Git will store the credentials itself after confirming them - flags |= self._cred.CREDUI_FLAGS_DO_NOT_PERSIST - - ui_info = dict(Parent=None, - #CaptionText="", - MessageText="Please enter login details for %s:" % target, - Banner=None) - - try: - result = self._cred.CredUIPromptForCredentials( - TargetName=target, - UserName=user, - Save=False, - Flags=flags, - UiInfo=ui_info) - except self._types.error as e: - if e.winerror == self.ERROR_CANCELLED: - return None - else: - print("Error: %s(): %s" % (e.funcname, e.strerror), - file=sys.stderr) - - user, passwd, persist = result - if len(user) and len(passwd): - attr["username"] = user - attr["password"] = passwd - return attr - else: - return None - else: - return None - - def _cred_store(self, attr): - target = self.make_target_name(attr) - - cred = { - "Type": self._cred.CRED_TYPE_GENERIC, - "Flags": 0, - "TargetName": target, - "Persist": self._cred.CRED_PERSIST_ENTERPRISE, - "Attributes": [], - } - for key, val in attr.items(): - if key == "host": - # do not store 'host' attribute, it will be - # provided in all queries anyway - continue - elif key == "username": - cred["UserName"] = val - elif key == "password": - cred["CredentialBlob"] = val - elif not attr_is_private(key): - cattr = {"Keyword": key, - "Flags": 0, - "Value": val.encode("utf-8")} - cred["Attributes"].append(cattr) - - try: - self._cred.CredWrite(cred, 0) - except self._types.error as e: - if e.winerror not in self.SILENT_ERRORS: - print("Error: %s(): %s" % (e.funcname, e.strerror), - file=sys.stderr) - - return None - - def _cred_erase(self, attr): - target = self.make_target_name(attr) - - try: - self._cred.CredDelete(target, self._cred.CRED_TYPE_GENERIC) - except self._types.error as e: - if e.winerror not in self.SILENT_ERRORS: - print("Error: %s(): %s" % (e.funcname, e.strerror), - file=sys.stderr) - - return None + _description_ = "Windows Credential Manager" + _required_ = ["protocol", "host"] + # WCM doesn't support wildcard matching for generic credentials, + # I can't be bothered to expand the code to retry without protocol, + # and the only relevant protocol is HTTPS anyway. + + ERROR_NOT_FOUND = 1168 + ERROR_CANCELLED = 1223 + SILENT_ERRORS = {ERROR_NOT_FOUND, ERROR_CANCELLED} + + interactive = True + + def __init__(self, options=None): + if options: + self.interactive = options.get("interactive", True) + + try: + import win32con, win32cred, pywintypes + self._con = win32con + self._cred = win32cred + self._types = pywintypes + except ImportError: + self._cred = None + + def available(self): + return bool(self._cred and self._cred.CredUIPromptForCredentials) + + def make_target_name(self, attr): + # Windows credential manager identifies generic credentials by a + # "target name", which can be any string. We use an URI here. + # + # The name is also prefixed with "git:" for compatibility with + # `git-credential-winstore`. This may or may not be temporary; + # see https://github.com/anurse/git-credential-winstore/pull/19 + return "git:%s" % make_uri(attr, anonymous=True, nullpath=True) + + def _cred_get(self, attr): + target = self.make_target_name(attr) + + try: + cred = self._cred.CredRead(target, self._cred.CRED_TYPE_GENERIC) + except self._types.error as e: + if e.winerror == self.ERROR_NOT_FOUND: + cred = None + else: + print("Error: %s(): %s" % (e.funcname, e.strerror), + file=sys.stderr) + return None + + if cred: + if cred["UserName"] is not None: + attr["username"] = cred["UserName"] + if len(cred["CredentialBlob"]): + attr["password"] = cred["CredentialBlob"].decode("utf-16-le") + for cattr in cred["Attributes"]: + attr[cattr["Keyword"]] = cattr["Value"].decode("utf-8") + return attr + elif self.interactive: + user = attr.get("username", None) + + flags = self._cred.CREDUI_FLAGS_GENERIC_CREDENTIALS + flags |= self._cred.CREDUI_FLAGS_EXCLUDE_CERTIFICATES + # Git will store the credentials itself after confirming them + flags |= self._cred.CREDUI_FLAGS_DO_NOT_PERSIST + + ui_info = dict(Parent=None, + #CaptionText="", + MessageText="Please enter login details for %s:" % target, + Banner=None) + + try: + result = self._cred.CredUIPromptForCredentials( + TargetName=target, + UserName=user, + Save=False, + Flags=flags, + UiInfo=ui_info) + except self._types.error as e: + if e.winerror == self.ERROR_CANCELLED: + return None + else: + print("Error: %s(): %s" % (e.funcname, e.strerror), + file=sys.stderr) + + user, passwd, persist = result + if len(user) and len(passwd): + attr["username"] = user + attr["password"] = passwd + return attr + else: + return None + else: + return None + + def _cred_store(self, attr): + target = self.make_target_name(attr) + + cred = { + "Type": self._cred.CRED_TYPE_GENERIC, + "Flags": 0, + "TargetName": target, + "Persist": self._cred.CRED_PERSIST_ENTERPRISE, + "Attributes": [], + } + for key, val in attr.items(): + if key == "host": + # do not store 'host' attribute, it will be + # provided in all queries anyway + continue + elif key == "username": + cred["UserName"] = val + elif key == "password": + cred["CredentialBlob"] = val + elif not attr_is_private(key): + cattr = {"Keyword": key, + "Flags": 0, + "Value": val.encode("utf-8")} + cred["Attributes"].append(cattr) + + try: + self._cred.CredWrite(cred, 0) + except self._types.error as e: + if e.winerror not in self.SILENT_ERRORS: + print("Error: %s(): %s" % (e.funcname, e.strerror), + file=sys.stderr) + + return None + + def _cred_erase(self, attr): + target = self.make_target_name(attr) + + try: + self._cred.CredDelete(target, self._cred.CRED_TYPE_GENERIC) + except self._types.error as e: + if e.winerror not in self.SILENT_ERRORS: + print("Error: %s(): %s" % (e.funcname, e.strerror), + file=sys.stderr) + + return None stores = { - "dummy": DummyLoopbackStore, - "gnomekeyring": GnomeKeyringStore, - "windows": Win32CredentialStore, + "dummy": DummyLoopbackStore, + "gnomekeyring": GnomeKeyringStore, + "windows": Win32CredentialStore, } if __name__ == "__main__": - try: - optlist, args = getopt.getopt(sys.argv[1:], - "iI", ["interactive", "no-interactive"]) - store_name, op = args - except getopt.GetoptError as err: - print(err) - sys.exit(2) - except ValueError: - usage() - sys.exit(2) - - options = {"interactive": True} - - for opt, optarg in optlist: - if opt in ("-i", "--interactive"): - options["interactive"] = True - elif opt in ("-I", "--no-interactive"): - options["interactive"] = False - - if store_name not in stores: - print("error: store %r not supported" % store_name, file=sys.stderr) - list_stores() - sys.exit(1) - - store = stores[store_name](options) - if not store.available(): - sys.exit(2) - - try: - attr = read_description() - except ValueError: - sys.exit(1) - - cred = store.handle(op, attr) - if cred: - write_description(cred) + try: + optlist, args = getopt.getopt(sys.argv[1:], + "iI", ["interactive", "no-interactive"]) + store_name, op = args + except getopt.GetoptError as err: + print(err) + sys.exit(2) + except ValueError: + usage() + sys.exit(2) + + options = {"interactive": True} + + for opt, optarg in optlist: + if opt in ("-i", "--interactive"): + options["interactive"] = True + elif opt in ("-I", "--no-interactive"): + options["interactive"] = False + + if store_name not in stores: + print("error: store %r not supported" % store_name, file=sys.stderr) + list_stores() + sys.exit(1) + + store = stores[store_name](options) + if not store.available(): + sys.exit(2) + + try: + attr = read_description() + except ValueError: + sys.exit(1) + + cred = store.handle(op, attr) + if cred: + write_description(cred) diff --git a/security/gpg-sync-keyrings b/security/gpg-sync-keyrings index 89bed2287..fa2158f12 100755 --- a/security/gpg-sync-keyrings +++ b/security/gpg-sync-keyrings @@ -6,110 +6,110 @@ from operator import attrgetter from socket import gethostname class GpgKeyring(dict): - def __init__(self): - self.gpgoptions = None - self.last_key = None - - def add_key(self, key_id): - key = GpgKey(key_id) - self[key.id] = key - self.last_key = key - - @classmethod - def load(self, *gpg_args): - gpg_args = list(gpg_args) - gpg_args += ["--with-colons", "--fast-list-mode", "--list-sigs"] - proc = subprocess.Popen(gpg_args, - stdout=subprocess.PIPE) - - keyring = self() - - for line in proc.stdout: - line = line.strip().decode("utf-8").split(":") - if line[0] == "pub": - id = line[4] - keyring.add_key(id) - elif line[0] == "sig": - signer_id = line[4] - timestamp = int(line[5]) - keyring.last_key.add_sig(signer_id, timestamp) - - return keyring + def __init__(self): + self.gpgoptions = None + self.last_key = None + + def add_key(self, key_id): + key = GpgKey(key_id) + self[key.id] = key + self.last_key = key + + @classmethod + def load(self, *gpg_args): + gpg_args = list(gpg_args) + gpg_args += ["--with-colons", "--fast-list-mode", "--list-sigs"] + proc = subprocess.Popen(gpg_args, + stdout=subprocess.PIPE) + + keyring = self() + + for line in proc.stdout: + line = line.strip().decode("utf-8").split(":") + if line[0] == "pub": + id = line[4] + keyring.add_key(id) + elif line[0] == "sig": + signer_id = line[4] + timestamp = int(line[5]) + keyring.last_key.add_sig(signer_id, timestamp) + + return keyring class GpgKey(object): - def __init__(self, key_id): - self.id = key_id - self.sigs = set() + def __init__(self, key_id): + self.id = key_id + self.sigs = set() - def __repr__(self): - return "Key(id=%r, sigs=%r)" % (self.id, self.sigs) + def __repr__(self): + return "Key(id=%r, sigs=%r)" % (self.id, self.sigs) - def add_sig(self, signer_id, timestamp): - sig = signer_id, timestamp - self.sigs.add(sig) + def add_sig(self, signer_id, timestamp): + sig = signer_id, timestamp + self.sigs.add(sig) def keyring_diff(local, remote): - local_keys = set(local) - remote_keys = set(remote) + local_keys = set(local) + remote_keys = set(remote) - # TODO: sync key removal + # TODO: sync key removal - to_remote = local_keys - remote_keys - to_local = remote_keys - local_keys + to_remote = local_keys - remote_keys + to_local = remote_keys - local_keys - for id in local_keys & remote_keys: - if local[id].sigs - remote[id].sigs: - to_remote.add(id) - if remote[id].sigs - local[id].sigs: - to_local.add(id) + for id in local_keys & remote_keys: + if local[id].sigs - remote[id].sigs: + to_remote.add(id) + if remote[id].sigs - local[id].sigs: + to_local.add(id) - return to_remote, to_local + return to_remote, to_local def gpg_transport(src_args, dst_args, key_ids): - export_args = ["--export-options", "export-local-sigs,export-sensitive-revkeys"] - import_args = ["--verbose", - "--allow-non-selfsigned-uid", - "--import-options", "import-local-sigs"] - - charset_args = ["--charset", "utf-8", - "--display-charset", "utf-8", - "--utf8-strings"] + export_args = ["--export-options", "export-local-sigs,export-sensitive-revkeys"] + import_args = ["--verbose", + "--allow-non-selfsigned-uid", + "--import-options", "import-local-sigs"] - export_args += charset_args - import_args += charset_args + charset_args = ["--charset", "utf-8", + "--display-charset", "utf-8", + "--utf8-strings"] - export_cmd = src_args + export_args + ["--export"] + ["0x%s" % id for id in key_ids] - import_cmd = dst_args + import_args + ["--import"] + export_args += charset_args + import_args += charset_args - gpg_pipe(export_cmd, import_cmd) + export_cmd = src_args + export_args + ["--export"] + ["0x%s" % id for id in key_ids] + import_cmd = dst_args + import_args + ["--import"] + + gpg_pipe(export_cmd, import_cmd) def gpg_merge_ownertrust(src_args, dst_args): - export_cmd = src_args + ["--export-ownertrust"] - import_cmd = dst_args + ["--import-ownertrust"] + export_cmd = src_args + ["--export-ownertrust"] + import_cmd = dst_args + ["--import-ownertrust"] - gpg_pipe(export_cmd, import_cmd) + gpg_pipe(export_cmd, import_cmd) def gpg_pipe(export_cmd, import_cmd): - print(export_cmd) - print(import_cmd) + print(export_cmd) + print(import_cmd) - exporter = subprocess.Popen(export_cmd, stdout=subprocess.PIPE) - importer = subprocess.Popen(import_cmd, stdin=exporter.stdout) + exporter = subprocess.Popen(export_cmd, stdout=subprocess.PIPE) + importer = subprocess.Popen(import_cmd, stdin=exporter.stdout) - r = importer.wait() - if r > 0: - exporter.terminate() - else: - exporter.wait() + r = importer.wait() + if r > 0: + exporter.terminate() + else: + exporter.wait() h = gethostname().lower() if h == "rain": - local_args = ["gpg"] - remote_args = ["ssh", "snow.home", "gpg"] + local_args = ["gpg"] + remote_args = ["ssh", "snow.home", "gpg"] elif h == "snow": - local_args = ["gpg"] - remote_args = ["plink", "rain.home", "gpg"] + local_args = ["gpg"] + remote_args = ["plink", "rain.home", "gpg"] local = GpgKeyring.load(*local_args) print("Local: %d keys" % len(local)) @@ -120,12 +120,12 @@ print("Remote: %d keys" % len(remote)) to_remote, to_local = keyring_diff(local, remote) if to_remote: - print("Exporting %d keys" % len(to_remote)) - gpg_transport(local_args, remote_args, to_remote) + print("Exporting %d keys" % len(to_remote)) + gpg_transport(local_args, remote_args, to_remote) if to_local: - print("Importing %d keys" % len(to_local)) - gpg_transport(remote_args, local_args, to_local) + print("Importing %d keys" % len(to_local)) + gpg_transport(remote_args, local_args, to_local) print("Exporting all ownertrusts") gpg_merge_ownertrust(local_args, remote_args) diff --git a/security/ssh-duphosts b/security/ssh-duphosts index 0de668c19..9471f2504 100755 --- a/security/ssh-duphosts +++ b/security/ssh-duphosts @@ -12,111 +12,111 @@ def xprint(*args, **kwargs): file.flush() class Hostname(object): - def __init__(self, value): - self.value = value - self.comparable = value.split(",", 1)[0] + def __init__(self, value): + self.value = value + self.comparable = value.split(",", 1)[0] - def __hash__(self): - return hash(self.value) + def __hash__(self): + return hash(self.value) - def __str__(self): - return str(self.value) + def __str__(self): + return str(self.value) - def __gt__(self, other): - self_d = is_ip(self.comparable) - other_d = is_ip(other.comparable) + def __gt__(self, other): + self_d = is_ip(self.comparable) + other_d = is_ip(other.comparable) - if self_d and not other_d: - return True - elif other_d and not self_d: - return False - else: - return self.comparable > other.comparable + if self_d and not other_d: + return True + elif other_d and not self_d: + return False + else: + return self.comparable > other.comparable def is_ip(addr): - if not addr: - return False + if not addr: + return False - if addr[0] == "[": - addr = addr[1:addr.find("]")] + if addr[0] == "[": + addr = addr[1:addr.find("]")] - if ":" in addr: - return True + if ":" in addr: + return True - if all(x.isdigit() for x in addr.split(".")): - return True + if all(x.isdigit() for x in addr.split(".")): + return True - return False + return False def find_duplicates(fh): - keys = {} - - for line in fh: - line = line.strip() - if line == "" or line[0] == "#": - continue - - try: - if line[0] == "@": - tag, host, ktype, key = line.split(" ", 3) - host = tag + " " + host - else: - host, ktype, key = line.split(" ", 2) - except ValueError: - xprint("bad line %r" % line, file=sys.stderr) - continue - - if ktype.isdigit(): - key = ktype + " " + key - ktype = "(sshv1-rsa)" - - if (ktype, key) in keys: - keys[ktype, key].append(host) - else: - keys[ktype, key] = [host] - - return keys + keys = {} + + for line in fh: + line = line.strip() + if line == "" or line[0] == "#": + continue + + try: + if line[0] == "@": + tag, host, ktype, key = line.split(" ", 3) + host = tag + " " + host + else: + host, ktype, key = line.split(" ", 2) + except ValueError: + xprint("bad line %r" % line, file=sys.stderr) + continue + + if ktype.isdigit(): + key = ktype + " " + key + ktype = "(sshv1-rsa)" + + if (ktype, key) in keys: + keys[ktype, key].append(host) + else: + keys[ktype, key] = [host] + + return keys def print_duplicates(keys): - for entry in keys: - hosts = keys[entry] - ktype, key = entry - if len(hosts) > 1: - xprint("Key [%(shortkey)s] has %(count)d entries:" % { - "shortkey": ktype + " ..." + key[-15:], - "count": len(hosts) - }) - xprint("\t%s" % "\n\t".join(hosts)) + for entry in keys: + hosts = keys[entry] + ktype, key = entry + if len(hosts) > 1: + xprint("Key [%(shortkey)s] has %(count)d entries:" % { + "shortkey": ktype + " ..." + key[-15:], + "count": len(hosts) + }) + xprint("\t%s" % "\n\t".join(hosts)) def print_merged(bykey, unmerge=False): - byhost = {} - - for entry in bykey: - ktype, key = entry - hosts = set() - for item in bykey[entry]: - hosts |= set(item.split(",")) - hosts.discard("") - if unmerge: - for host in hosts: - host = Hostname(host) - byhost[host, ktype] = key - else: - hosts = sorted(hosts, key=Hostname) - host = Hostname(",".join(hosts)) - byhost[host, ktype] = key - - hosts = list(byhost.keys()) - hosts.sort(key=itemgetter(1)) - hosts.sort(key=itemgetter(0)) - - for entry in hosts: - host, ktype = entry - key = byhost[entry] - if ktype == "(sshv1-rsa)": - xprint(host, key) - else: - xprint(host, ktype, key) + byhost = {} + + for entry in bykey: + ktype, key = entry + hosts = set() + for item in bykey[entry]: + hosts |= set(item.split(",")) + hosts.discard("") + if unmerge: + for host in hosts: + host = Hostname(host) + byhost[host, ktype] = key + else: + hosts = sorted(hosts, key=Hostname) + host = Hostname(",".join(hosts)) + byhost[host, ktype] = key + + hosts = list(byhost.keys()) + hosts.sort(key=itemgetter(1)) + hosts.sort(key=itemgetter(0)) + + for entry in hosts: + host, ktype = entry + key = byhost[entry] + if ktype == "(sshv1-rsa)": + xprint(host, key) + else: + xprint(host, ktype, key) opt_input = os.path.expanduser("~/.ssh/known_hosts") opt_merge = False @@ -124,22 +124,22 @@ opt_unmerge = False opts, args = getopt.getopt(sys.argv[1:], 'mM') for opt, optarg in opts: - if opt in ('m', '-m'): - opt_merge = True - elif opt in ('M', '-M'): - opt_unmerge = True + if opt in ('m', '-m'): + opt_merge = True + elif opt in ('M', '-M'): + opt_unmerge = True if args: - opt_input = args.pop(0) + opt_input = args.pop(0) if opt_input == "-": - keys = find_duplicates(sys.stdin) + keys = find_duplicates(sys.stdin) else: - fh = open(opt_input, "r") - keys = find_duplicates(fh) - fh.close() + fh = open(opt_input, "r") + keys = find_duplicates(fh) + fh.close() if opt_merge or opt_unmerge: - print_merged(keys, opt_unmerge) + print_merged(keys, opt_unmerge) else: - print_duplicates(keys) + print_duplicates(keys) diff --git a/security/ssh-publickeyd b/security/ssh-publickeyd index 6620c0a00..11e37146a 100755 --- a/security/ssh-publickeyd +++ b/security/ssh-publickeyd @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# vim: ts=4:sw=4 # Server-side of SecureCRT's "public key assistant" subsystem, # as defined in RFC 4819. # @@ -33,531 +32,531 @@ import struct from nullroute import authorized_keys def trace(*args): - print(*args, file=sys.stderr) + print(*args, file=sys.stderr) class Keystore(object): - pass + pass class YamlKeystore(Keystore): - def __init__(self, path=None): - import yaml + def __init__(self, path=None): + import yaml - self.path = path or os.path.expanduser("~/ssh-keys.yaml") + self.path = path or os.path.expanduser("~/ssh-keys.yaml") - def load(self): - import yaml + def load(self): + import yaml - keys = {} + keys = {} - try: - with open(self.path, "r") as fh: - data = yaml.load(fh) - except: - return keys + try: + with open(self.path, "r") as fh: + data = yaml.load(fh) + except: + return keys - for item in data: - keys[item["algo"], item["pubkey"]] = item["attributes"] + for item in data: + keys[item["algo"], item["pubkey"]] = item["attributes"] - return keys + return keys - def save(self, keys): - import yaml + def save(self, keys): + import yaml - data = [] + data = [] - for (kalgo, kblob), attrs in keys.items(): - data.append({"algo": kalgo, "pubkey": kblob, "attributes": attrs}) + for (kalgo, kblob), attrs in keys.items(): + data.append({"algo": kalgo, "pubkey": kblob, "attributes": attrs}) - with open(self.path, "w") as fh: - fh.write("# vim: ft=yaml:nowrap:noet\n") - yaml.dump(data, fh) + with open(self.path, "w") as fh: + fh.write("# vim: ft=yaml:nowrap:noet\n") + yaml.dump(data, fh) - def list(self): - keys = self.load() + def list(self): + keys = self.load() - for (kalgo, kblob), attrs in keys.items(): - yield kalgo, kblob, attrs + for (kalgo, kblob), attrs in keys.items(): + yield kalgo, kblob, attrs - def has_key(self, kalgo, kblob): - keys = self.load() + def has_key(self, kalgo, kblob): + keys = self.load() - return (kalgo, kblob) in keys + return (kalgo, kblob) in keys - def add(self, kalgo, kblob, attrs): - keys = self.load() + def add(self, kalgo, kblob, attrs): + keys = self.load() - keys[kalgo, kblob] = attrs - self.save(keys) + keys[kalgo, kblob] = attrs + self.save(keys) - def remove(self, kalgo, kblob): - keys = self.load() + def remove(self, kalgo, kblob): + keys = self.load() - if (kalgo, kblob) in keys: - del keys[kalgo, kblob] - self.save(keys) - return True - else: - return False + if (kalgo, kblob) in keys: + del keys[kalgo, kblob] + self.save(keys) + return True + else: + return False - @classmethod - def knows_attribute(self, attr): - return True + @classmethod + def knows_attribute(self, attr): + return True class OpenSSHKeystore(Keystore): # {{{ - def __init__(self, path=None): - self.path = path or os.path.expanduser("~/.ssh/authorized_keys") - - def load(self): - keys = {} - extra_attrs = [] - - ATTR_PREFIX = "# attribute: " - - for line in open(self.path, "r"): - line = line.strip() - if not line: - pass - elif line.startswith(ATTR_PREFIX): - # hack to store attributes not supported by authorized_keys - name, value = line[len(ATTR_PREFIX):].split("=", 2) - extra_attrs.append({ - "name": name, - "value": value, - "critical": False, - }) - elif line.startswith("#:"): - pass - elif line.startswith("#"): - extra_attrs.append({ - "name": "x-comment", - "value": line[1:], - "critical": False, - }) - else: - key = authorized_keys.PublicKey(line) - attrs = self.convopt_openssh_to_vandyke(key.options) - if len(key.comment): - attrs.append({ - "name": "comment", - "value": key.comment, - "critical": False, - }) - attrs += extra_attrs - extra_attrs = [] - keys[key.algo, key.blob] = attrs - return keys - - def save(self, keys): - with open(self.path, "w") as fh: - for (kalgo, kblob), attrs in keys.items(): - self._append_key(fh, kalgo, kblob, attrs) - - def _append_key(self, fh, kalgo, kblob, attrs): - key = authorized_keys.PublicKey() - key.algo = kalgo - key.blob = kblob - for attr in attrs: - if attr["name"] == "comment": - key.comment = attr["value"] - key.options, unknown_attrs = self.convopt_vandyke_to_openssh(attrs) - if len(key.comment): - fh.write("#: %s\n" % key.comment) - fpr = ":".join("%02x" % ord(c) for c in key.fingerprint()) - print("#: %s" % fpr, file=fh) - for attr in unknown_attrs: - if attr["name"] == "x-comment": - print("#%s" % attr["value"], file=fh) - else: - print("# attr: %(name)s=%(value)s" % attr, file=fh) - print(key, file=fh) - print("", file=fh) - - def list(self): - keys = self.load() - - for (kalgo, kblob), attrs in keys.items(): - yield kalgo, kblob, attrs - - def has_key(self, kalgo, kblob): - keys = self.load() - - return (kalgo, kblob) in keys - - def add(self, kalgo, kblob, attrs): - with open(self.path, "a") as fh: - self._append_key(fh, kalgo, kblob, attrs) - - #keys = self.load() - #keys[kalgo, kblob] = attrs - #return self.save(keys) - - def remove(self, kalgo, kblob): - keys = self.load() - - if (kalgo, kblob) in keys: - del keys[kalgo, kblob] - self.save(keys) - return True - else: - return False - - attributes = ("agent", "command-override", "comment", "from", - "port-forward", "x11", "x-openssh-option") - - @classmethod - def knows_attribute(self, name): - return name in self.attributes - - @staticmethod - def convopt_openssh_to_vandyke(in_opts): - tmp_attrs = [] - - for opt, value in in_opts: - if opt == "command": - tmp_attrs.append(("command-override", value)) - elif opt == "from": - tmp_attrs.append(("from", value)) - elif opt == "no-agent-forwarding": - tmp_attrs.append(("agent", "")) - elif opt == "no-port-forwarding": - tmp_attrs.append(("port-forward", "")) - tmp_attrs.append(("reverse-forward", "")) - elif opt == "no-x11-forwarding": - tmp_attrs.append(("x11", "")) - else: - if value is True: - attr_value = opt - else: - attr_value = "%s=%s" % (opt, value) - tmp_attrs.append(("x-openssh-option", attr_value)) - - out_attrs = [{"name": attr[0], "value": attr[1], "critical": False} - for attr in tmp_attrs] - - return out_attrs - - @staticmethod - def convopt_vandyke_to_openssh(in_attrs): - out_opts = authorized_keys.PublicKeyOptions() - unknown_attrs = [] - - for attr in in_attrs: - # TODO: - name, value, _ = attr["name"], attr["value"], attr["critical"] - if name == "agent": - out_opts.append(("no-agent-forwarding", True)) - elif name == "command-override": - out_opts.append(("command", value)) - elif name == "comment": - pass - elif name == "from": - out_opts.append(("from", value)) - elif name == "port-forward": - out_opts.append(("no-port-forwarding", True)) - elif name == "x11": - out_opts.append(("no-x11-forwarding", True)) - elif name == "x-openssh-option": - if "=" in value: - out_opts.append(value.split("=", 1)) - else: - out_opts.append((value, True)) - else: - unknown_attrs.append(attr) - - return out_opts, unknown_attrs + def __init__(self, path=None): + self.path = path or os.path.expanduser("~/.ssh/authorized_keys") + + def load(self): + keys = {} + extra_attrs = [] + + ATTR_PREFIX = "# attribute: " + + for line in open(self.path, "r"): + line = line.strip() + if not line: + pass + elif line.startswith(ATTR_PREFIX): + # hack to store attributes not supported by authorized_keys + name, value = line[len(ATTR_PREFIX):].split("=", 2) + extra_attrs.append({ + "name": name, + "value": value, + "critical": False, + }) + elif line.startswith("#:"): + pass + elif line.startswith("#"): + extra_attrs.append({ + "name": "x-comment", + "value": line[1:], + "critical": False, + }) + else: + key = authorized_keys.PublicKey(line) + attrs = self.convopt_openssh_to_vandyke(key.options) + if len(key.comment): + attrs.append({ + "name": "comment", + "value": key.comment, + "critical": False, + }) + attrs += extra_attrs + extra_attrs = [] + keys[key.algo, key.blob] = attrs + return keys + + def save(self, keys): + with open(self.path, "w") as fh: + for (kalgo, kblob), attrs in keys.items(): + self._append_key(fh, kalgo, kblob, attrs) + + def _append_key(self, fh, kalgo, kblob, attrs): + key = authorized_keys.PublicKey() + key.algo = kalgo + key.blob = kblob + for attr in attrs: + if attr["name"] == "comment": + key.comment = attr["value"] + key.options, unknown_attrs = self.convopt_vandyke_to_openssh(attrs) + if len(key.comment): + fh.write("#: %s\n" % key.comment) + fpr = ":".join("%02x" % ord(c) for c in key.fingerprint()) + print("#: %s" % fpr, file=fh) + for attr in unknown_attrs: + if attr["name"] == "x-comment": + print("#%s" % attr["value"], file=fh) + else: + print("# attr: %(name)s=%(value)s" % attr, file=fh) + print(key, file=fh) + print("", file=fh) + + def list(self): + keys = self.load() + + for (kalgo, kblob), attrs in keys.items(): + yield kalgo, kblob, attrs + + def has_key(self, kalgo, kblob): + keys = self.load() + + return (kalgo, kblob) in keys + + def add(self, kalgo, kblob, attrs): + with open(self.path, "a") as fh: + self._append_key(fh, kalgo, kblob, attrs) + + #keys = self.load() + #keys[kalgo, kblob] = attrs + #return self.save(keys) + + def remove(self, kalgo, kblob): + keys = self.load() + + if (kalgo, kblob) in keys: + del keys[kalgo, kblob] + self.save(keys) + return True + else: + return False + + attributes = ("agent", "command-override", "comment", "from", + "port-forward", "x11", "x-openssh-option") + + @classmethod + def knows_attribute(self, name): + return name in self.attributes + + @staticmethod + def convopt_openssh_to_vandyke(in_opts): + tmp_attrs = [] + + for opt, value in in_opts: + if opt == "command": + tmp_attrs.append(("command-override", value)) + elif opt == "from": + tmp_attrs.append(("from", value)) + elif opt == "no-agent-forwarding": + tmp_attrs.append(("agent", "")) + elif opt == "no-port-forwarding": + tmp_attrs.append(("port-forward", "")) + tmp_attrs.append(("reverse-forward", "")) + elif opt == "no-x11-forwarding": + tmp_attrs.append(("x11", "")) + else: + if value is True: + attr_value = opt + else: + attr_value = "%s=%s" % (opt, value) + tmp_attrs.append(("x-openssh-option", attr_value)) + + out_attrs = [{"name": attr[0], "value": attr[1], "critical": False} + for attr in tmp_attrs] + + return out_attrs + + @staticmethod + def convopt_vandyke_to_openssh(in_attrs): + out_opts = authorized_keys.PublicKeyOptions() + unknown_attrs = [] + + for attr in in_attrs: + # TODO: + name, value, _ = attr["name"], attr["value"], attr["critical"] + if name == "agent": + out_opts.append(("no-agent-forwarding", True)) + elif name == "command-override": + out_opts.append(("command", value)) + elif name == "comment": + pass + elif name == "from": + out_opts.append(("from", value)) + elif name == "port-forward": + out_opts.append(("no-port-forwarding", True)) + elif name == "x11": + out_opts.append(("no-x11-forwarding", True)) + elif name == "x-openssh-option": + if "=" in value: + out_opts.append(value.split("=", 1)) + else: + out_opts.append((value, True)) + else: + unknown_attrs.append(attr) + + return out_opts, unknown_attrs # }}} #class LshKeystore(Keystore): # {{{ -# def __init__(self, path=None): -# self.path = path or os.path.expanduser("~/.lsh/authorized_keys_sha1") -# #raise NotImplemented() +# def __init__(self, path=None): +# self.path = path or os.path.expanduser("~/.lsh/authorized_keys_sha1") +# #raise NotImplemented() # -# def load(self): -# keys = {} -# for f in os.listdir(self.path): -# path = os.path.join(self.path, f) -# kalgo, kblob = self.parseSexp(open(path, "rb")) -# keys[kalgo, kblob] = [] -# return keys +# def load(self): +# keys = {} +# for f in os.listdir(self.path): +# path = os.path.join(self.path, f) +# kalgo, kblob = self.parseSexp(open(path, "rb")) +# keys[kalgo, kblob] = [] +# return keys # -# def save(self, keys): -# import hashlib -# storedKeys = set(os.listdir(self.path)) -# currentKeys = set() -# for (kalgo, kblob), attrs in keys.items(): -# exp = self.unparseSexp(kalgo, kblob, attrs) -# sha = hashlib.sha1(exp.canonical()).hexdigest() -# currentKeys.add(sha) -# if sha not in storedKeys: -# path = os.path.join(self.path, sha) -# open(path, "wb").write(exp.sexp()) +# def save(self, keys): +# import hashlib +# storedKeys = set(os.listdir(self.path)) +# currentKeys = set() +# for (kalgo, kblob), attrs in keys.items(): +# exp = self.unparseSexp(kalgo, kblob, attrs) +# sha = hashlib.sha1(exp.canonical()).hexdigest() +# currentKeys.add(sha) +# if sha not in storedKeys: +# path = os.path.join(self.path, sha) +# open(path, "wb").write(exp.sexp()) # -# for sha in storedKeys-currentKeys: -# path = os.path.join(self.path, sha) -# os.unlink(path) +# for sha in storedKeys-currentKeys: +# path = os.path.join(self.path, sha) +# os.unlink(path) # -# def add(self, kalgo, kblob, attrs): -# import hashlib -# exp = self.unparseSexp(kalgo, kblob, attrs) -# sha = hashlib.sha1(exp.canonical()).hexdigest() -# path = os.path.join(self.path, sha) -# if os.path.exists(path): -# return vdproto.KEY_ALREADY_PRESENT -# else: -# open(path, "wb").write(exp.sexp()) -# return vdproto.SUCCESS +# def add(self, kalgo, kblob, attrs): +# import hashlib +# exp = self.unparseSexp(kalgo, kblob, attrs) +# sha = hashlib.sha1(exp.canonical()).hexdigest() +# path = os.path.join(self.path, sha) +# if os.path.exists(path): +# return vdproto.KEY_ALREADY_PRESENT +# else: +# open(path, "wb").write(exp.sexp()) +# return vdproto.SUCCESS # -# def remove(self, kalgo, kblob): -# raise NotImplemented +# def remove(self, kalgo, kblob): +# raise NotImplemented # -# @classmethod -# def parseSexp(self, expr): -# import sexp -# tree = sexp.Sexp(expr).tree -# assert tree[0] == "public-key" -# tree = tree[1] -# algo = tree[0] -# if algo == "rsa-pkcs1-sha1": -# kalgo = "ssh-rsa" -# n = tree.find("n").next()[1] -# e = tree.find("e").next()[1] -# # todo: SshProtocol class? -# string = lambda s: struct.pack("!L", len(s)) + s -# kblob = string("ssh-rsa") + string(e) + string(n) -# else: -# raise NotImplemented("unknown key type %s" % algo) -# return kalgo, kblob +# @classmethod +# def parseSexp(self, expr): +# import sexp +# tree = sexp.Sexp(expr).tree +# assert tree[0] == "public-key" +# tree = tree[1] +# algo = tree[0] +# if algo == "rsa-pkcs1-sha1": +# kalgo = "ssh-rsa" +# n = tree.find("n").next()[1] +# e = tree.find("e").next()[1] +# # todo: SshProtocol class? +# string = lambda s: struct.pack("!L", len(s)) + s +# kblob = string("ssh-rsa") + string(e) + string(n) +# else: +# raise NotImplemented("unknown key type %s" % algo) +# return kalgo, kblob # -# @classmethod -# def unparseSexp(self, kalgo, kblob, attrs): -# import sexp -# from StringIO import StringIO -# buf = StringIO(kblob) -# if kalgo == "ssh-rsa": -# # TODO -# length, = struct.unpack("!L", buf.read(4)) -# buf.read(length) -# length, = struct.unpack("!L", buf.read(4)) -# e = buf.read(length) -# length, = struct.unpack("!L", buf.read(4)) -# n = buf.read(length) +# @classmethod +# def unparseSexp(self, kalgo, kblob, attrs): +# import sexp +# from StringIO import StringIO +# buf = StringIO(kblob) +# if kalgo == "ssh-rsa": +# # TODO +# length, = struct.unpack("!L", buf.read(4)) +# buf.read(length) +# length, = struct.unpack("!L", buf.read(4)) +# e = buf.read(length) +# length, = struct.unpack("!L", buf.read(4)) +# n = buf.read(length) # -# key = sexp.List() -# key.append(sexp.String("rsa-pkcs1-sha1")) -# key.append(sexp.List([sexp.String("n"), sexp.String(n)])) -# key.append(sexp.List([sexp.String("e"), sexp.String(e)])) +# key = sexp.List() +# key.append(sexp.String("rsa-pkcs1-sha1")) +# key.append(sexp.List([sexp.String("n"), sexp.String(n)])) +# key.append(sexp.List([sexp.String("e"), sexp.String(e)])) # -# else: -# raise NotImplemented("keys of type %s not supported yet" % kalgo) +# else: +# raise NotImplemented("keys of type %s not supported yet" % kalgo) # -# exp = sexp.List() -# exp.append(sexp.String("public-key")) -# exp.append(key) -# return exp +# exp = sexp.List() +# exp.append(sexp.String("public-key")) +# exp.append(key) +# return exp # -# @classmethod -# def knows_attribute(self, name): -# return name in ("comment") +# @classmethod +# def knows_attribute(self, name): +# return name in ("comment") # }}} class SshEndOfStream(Exception): - pass + pass class SshStream(object): - def __init__(self, inputfd, outputfd=None): - self.inputfd = inputfd - self.outputfd = outputfd or inputfd - - def read(self, *args): - buf = self.inputfd.read(*args) - if not buf: - raise SshEndOfStream - return buf - - def write(self, *args): - return self.outputfd.write(*args) - - def writef(self, *args): - r = self.write(*args) - if r: - r = self.outputfd.flush() - return r - - def read_u32(self): - buf = self.read(4) - val, = struct.unpack("!L", buf) - return val - - def read_bool(self): - buf = self.read(1) - val, = struct.unpack("!?", buf) - return val - - def read_string(self): - length = self.read_u32() - buf = self.read(length) - return buf - - def read_packet(self): - packet_length = self.read_u32() - name = self.read_string() - data_length = packet_length - (4 + len(name)) - return name, data_length - - def write_packet(self, *data): - fmt = "!L" - packed = [] - for datum in data: - if isinstance(datum, int): - fmt += "L" - packed += [datum] - elif isinstance(datum, bool): - fmt += "?" - packed += [datum] - elif isinstance(datum, str): - buf = datum.encode("utf-8") - fmt += "L%ds" % len(buf) - packed += [len(buf), buf] - elif isinstance(datum, bytes): - fmt += "L%ds" % len(datum) - packed += [len(datum), datum] - else: - trace("write_packet(): unknown type %r of %r" % (type(datum), datum)) - data_length = struct.calcsize(fmt) - 4 - buf = struct.pack(fmt, data_length, *packed) - self.writef(buf) - - def write_status(self, code, msg): - self.write_packet("status", code, msg, "en_US") + def __init__(self, inputfd, outputfd=None): + self.inputfd = inputfd + self.outputfd = outputfd or inputfd + + def read(self, *args): + buf = self.inputfd.read(*args) + if not buf: + raise SshEndOfStream + return buf + + def write(self, *args): + return self.outputfd.write(*args) + + def writef(self, *args): + r = self.write(*args) + if r: + r = self.outputfd.flush() + return r + + def read_u32(self): + buf = self.read(4) + val, = struct.unpack("!L", buf) + return val + + def read_bool(self): + buf = self.read(1) + val, = struct.unpack("!?", buf) + return val + + def read_string(self): + length = self.read_u32() + buf = self.read(length) + return buf + + def read_packet(self): + packet_length = self.read_u32() + name = self.read_string() + data_length = packet_length - (4 + len(name)) + return name, data_length + + def write_packet(self, *data): + fmt = "!L" + packed = [] + for datum in data: + if isinstance(datum, int): + fmt += "L" + packed += [datum] + elif isinstance(datum, bool): + fmt += "?" + packed += [datum] + elif isinstance(datum, str): + buf = datum.encode("utf-8") + fmt += "L%ds" % len(buf) + packed += [len(buf), buf] + elif isinstance(datum, bytes): + fmt += "L%ds" % len(datum) + packed += [len(datum), datum] + else: + trace("write_packet(): unknown type %r of %r" % (type(datum), datum)) + data_length = struct.calcsize(fmt) - 4 + buf = struct.pack(fmt, data_length, *packed) + self.writef(buf) + + def write_status(self, code, msg): + self.write_packet("status", code, msg, "en_US") # }}} class PublicKeySubsystem(object): - SUCCESS = 0 - ACCESS_DENIED = 1 - STORAGE_EXCEEDED = 2 - VERSION_NOT_SUPPORTED = 3 - KEY_NOT_FOUND = 4 - KEY_NOT_SUPPORTED = 5 - KEY_ALREADY_PRESENT = 6 - GENERAL_FAILURE = 7 - REQUEST_NOT_SUPPORTED = 8 - ATTRIBUTE_NOT_SUPPORTED = 9 - - statuses = { - SUCCESS: "Success", - ACCESS_DENIED: "Access denied", - VERSION_NOT_SUPPORTED: "Protocol version not supported", - KEY_NOT_FOUND: "Key not found", - KEY_NOT_SUPPORTED: "Key type not supported", - KEY_ALREADY_PRESENT: "Key already present", - GENERAL_FAILURE: "General failure", - REQUEST_NOT_SUPPORTED: "Request not supported", - ATTRIBUTE_NOT_SUPPORTED: "Attribute not supported", - } - - fatal_codes = { VERSION_NOT_SUPPORTED } - - def __init__(self, stream): - self.keystore = None - self.stream = stream - - def recv_list(self): - for kalgo, kblob, attrs in self.keystore.list(): - data = [kalgo, kblob, len(attrs)] - for attr in attrs: - data += attr["name"], attr["value"] - self.stream.write_packet("publickey", *data) - - return self.SUCCESS - - def recv_add(self): - kalgo = self.stream.read_string() - kblob = self.stream.read_string() - overwrite = self.stream.read_bool() - num_attrs = self.stream.read_u32() - attrs = [] - - while num_attrs: - attrs.append({ - "name": self.stream.read_string(), - "value": self.stream.read_string(), - "critical": self.stream.read_bool(), - }) - num_attrs -= 1 - - if self.keystore.has_key(kalgo, kblob) and not overwrite: - return self.KEY_ALREADY_PRESENT - - for attr in attrs: - if attr["critical"] and not keystore.knows_attribute(attr["name"]): - return self.ATTRIBUTE_NOT_SUPPORTED - - self.keystore.add(kalgo, kblob, attrs) - - return self.SUCCESS - - def recv_remove(self): - kalgo = self.stream.read_string() - kblob = self.stream.read_string() - - if self.keystore.remove(kalgo, kblob): - return self.SUCCESS - else: - return self.KEY_NOT_FOUND - - def recv_listattributes(self): - for attr in KnownAttributes: - self.stream.write_packet("attribute", attr, False) - - return self.SUCCESS - - def handle_message(self, name, data_length): - if name == b"version": - ver = self.stream.read_u32() - if ver == 2: - self.stream.write_packet("version", ver) - return None - else: - return self.VERSION_NOT_SUPPORTED - elif name == b"list": - return self.recv_list() - elif name == b"add": - return self.recv_add() - elif name == b"remove": - return self.recv_remove() - elif name == b"listattributes": - return self.recv_listattributes() - else: - trace("received unknown message %r [%r bytes]" % (name, data_length)) - sys.stdin.read(data_length) - return self.REQUEST_NOT_SUPPORTED - - def loop(self): - while True: - name, data_length = self.stream.read_packet() - code = self.handle_message(name, data_length) - if code is not None: - msg = self.statuses.get(code, self.GENERAL_FAILURE) - self.stream.write_status(code, msg) - if code in self.fatal_codes: - break - - def run(self): - try: - self.loop() - except SshEndOfStream: - trace("end of stream") - sys.exit() + SUCCESS = 0 + ACCESS_DENIED = 1 + STORAGE_EXCEEDED = 2 + VERSION_NOT_SUPPORTED = 3 + KEY_NOT_FOUND = 4 + KEY_NOT_SUPPORTED = 5 + KEY_ALREADY_PRESENT = 6 + GENERAL_FAILURE = 7 + REQUEST_NOT_SUPPORTED = 8 + ATTRIBUTE_NOT_SUPPORTED = 9 + + statuses = { + SUCCESS: "Success", + ACCESS_DENIED: "Access denied", + VERSION_NOT_SUPPORTED: "Protocol version not supported", + KEY_NOT_FOUND: "Key not found", + KEY_NOT_SUPPORTED: "Key type not supported", + KEY_ALREADY_PRESENT: "Key already present", + GENERAL_FAILURE: "General failure", + REQUEST_NOT_SUPPORTED: "Request not supported", + ATTRIBUTE_NOT_SUPPORTED: "Attribute not supported", + } + + fatal_codes = { VERSION_NOT_SUPPORTED } + + def __init__(self, stream): + self.keystore = None + self.stream = stream + + def recv_list(self): + for kalgo, kblob, attrs in self.keystore.list(): + data = [kalgo, kblob, len(attrs)] + for attr in attrs: + data += attr["name"], attr["value"] + self.stream.write_packet("publickey", *data) + + return self.SUCCESS + + def recv_add(self): + kalgo = self.stream.read_string() + kblob = self.stream.read_string() + overwrite = self.stream.read_bool() + num_attrs = self.stream.read_u32() + attrs = [] + + while num_attrs: + attrs.append({ + "name": self.stream.read_string(), + "value": self.stream.read_string(), + "critical": self.stream.read_bool(), + }) + num_attrs -= 1 + + if self.keystore.has_key(kalgo, kblob) and not overwrite: + return self.KEY_ALREADY_PRESENT + + for attr in attrs: + if attr["critical"] and not keystore.knows_attribute(attr["name"]): + return self.ATTRIBUTE_NOT_SUPPORTED + + self.keystore.add(kalgo, kblob, attrs) + + return self.SUCCESS + + def recv_remove(self): + kalgo = self.stream.read_string() + kblob = self.stream.read_string() + + if self.keystore.remove(kalgo, kblob): + return self.SUCCESS + else: + return self.KEY_NOT_FOUND + + def recv_listattributes(self): + for attr in KnownAttributes: + self.stream.write_packet("attribute", attr, False) + + return self.SUCCESS + + def handle_message(self, name, data_length): + if name == b"version": + ver = self.stream.read_u32() + if ver == 2: + self.stream.write_packet("version", ver) + return None + else: + return self.VERSION_NOT_SUPPORTED + elif name == b"list": + return self.recv_list() + elif name == b"add": + return self.recv_add() + elif name == b"remove": + return self.recv_remove() + elif name == b"listattributes": + return self.recv_listattributes() + else: + trace("received unknown message %r [%r bytes]" % (name, data_length)) + sys.stdin.read(data_length) + return self.REQUEST_NOT_SUPPORTED + + def loop(self): + while True: + name, data_length = self.stream.read_packet() + code = self.handle_message(name, data_length) + if code is not None: + msg = self.statuses.get(code, self.GENERAL_FAILURE) + self.stream.write_status(code, msg) + if code in self.fatal_codes: + break + + def run(self): + try: + self.loop() + except SshEndOfStream: + trace("end of stream") + sys.exit() def is_interactive(): - try: - return os.isatty(sys.stdin.fileno()) - except AttributeError: - return false + try: + return os.isatty(sys.stdin.fileno()) + except AttributeError: + return false if is_interactive(): - trace("This tool is intended to be run as a SSH subsystem, not interactively.") - sys.exit(2) + trace("This tool is intended to be run as a SSH subsystem, not interactively.") + sys.exit(2) os.umask(0o077) sys.stdin = open("/dev/stdin", "rb") diff --git a/security/update-k5login b/security/update-k5login index f026e4fe6..f78beb15c 100755 --- a/security/update-k5login +++ b/security/update-k5login @@ -6,82 +6,82 @@ import socket import sys def getfqdn(host=None): - host = host or socket.gethostname() - try: - ai = socket.getaddrinfo(host, None, 0, 0, 0, socket.AI_CANONNAME) - return ai[0][3] if ai else host - except: - return host + host = host or socket.gethostname() + try: + ai = socket.getaddrinfo(host, None, 0, 0, 0, socket.AI_CANONNAME) + return ai[0][3] if ai else host + except: + return host def read_rules(rules_file): - defprinc = None - principals = [] - rules = {} - rgroups = {} - isgroup = False - lineno = 0 - for line in open(rules_file, "r"): - lineno += 1 - line = line.rstrip() - if not line or line.startswith("#"): - continue - - if line[0].isspace(): - masks = line.split() - elif line[0] == "+": - isgroup = True - line = line.split() - defprinc = line[0] - masks = line[1:] - else: - isgroup = False - line = line.split() - defprinc = line[0] - masks = line[1:] - - if not defprinc: - sys.stderr.write("warning: missing principal on line %d\n" % lineno) - continue - - if isgroup: - if defprinc not in rgroups: - rgroups[defprinc] = set() - rgroups[defprinc] |= set(masks) - else: - if defprinc not in rules: - principals.append(defprinc) - rules[defprinc] = set() - rules[defprinc] |= set(masks) - - for rgroup in list(rgroups): - ngroup = "!"+rgroup - rgroups[ngroup] = set("!"+r for r in rgroups[rgroup]) - - for princ in rules: - for rgroup in rgroups: - if rgroup in rules[princ]: - rules[princ].remove(rgroup) - rules[princ] |= rgroups[rgroup] - - return principals, rules + defprinc = None + principals = [] + rules = {} + rgroups = {} + isgroup = False + lineno = 0 + for line in open(rules_file, "r"): + lineno += 1 + line = line.rstrip() + if not line or line.startswith("#"): + continue + + if line[0].isspace(): + masks = line.split() + elif line[0] == "+": + isgroup = True + line = line.split() + defprinc = line[0] + masks = line[1:] + else: + isgroup = False + line = line.split() + defprinc = line[0] + masks = line[1:] + + if not defprinc: + sys.stderr.write("warning: missing principal on line %d\n" % lineno) + continue + + if isgroup: + if defprinc not in rgroups: + rgroups[defprinc] = set() + rgroups[defprinc] |= set(masks) + else: + if defprinc not in rules: + principals.append(defprinc) + rules[defprinc] = set() + rules[defprinc] |= set(masks) + + for rgroup in list(rgroups): + ngroup = "!"+rgroup + rgroups[ngroup] = set("!"+r for r in rgroups[rgroup]) + + for princ in rules: + for rgroup in rgroups: + if rgroup in rules[princ]: + rules[princ].remove(rgroup) + rules[princ] |= rgroups[rgroup] + + return principals, rules def test_mask(fqdn, mask): - if mask.startswith("!"): - return not fnmatch.fnmatch(fqdn, mask[1:]) - else: - return fnmatch.fnmatch(fqdn, mask) + if mask.startswith("!"): + return not fnmatch.fnmatch(fqdn, mask[1:]) + else: + return fnmatch.fnmatch(fqdn, mask) def test_rule(fqdn, masks): - blacklist = [m[1:] for m in masks if m.startswith("!")] - whitelist = [m for m in masks if not m.startswith("!")] + blacklist = [m[1:] for m in masks if m.startswith("!")] + whitelist = [m for m in masks if not m.startswith("!")] - func = lambda mask: test_mask(fqdn, mask) - ok = True - if blacklist: - ok = ok and not any(map(func, blacklist)) - if whitelist: - ok = ok and any(map(func, whitelist)) - return ok + func = lambda mask: test_mask(fqdn, mask) + ok = True + if blacklist: + ok = ok and not any(map(func, blacklist)) + if whitelist: + ok = ok and any(map(func, whitelist)) + return ok fqdn = getfqdn() rules_file = os.path.expanduser("~/lib/dotfiles/k5login.rules") @@ -90,21 +90,21 @@ verbose = False opts, args = getopt.getopt(sys.argv[1:], "F:i:o:v") for opt, optarg in opts: - if opt == "-v": verbose = True - elif opt == "-F": fqdn = optarg - elif opt == "-i": rules_file = optarg - elif opt == "-o": k5login_file = optarg + if opt == "-v": verbose = True + elif opt == "-F": fqdn = optarg + elif opt == "-i": rules_file = optarg + elif opt == "-o": k5login_file = optarg if verbose: - sys.stderr.write("fqdn: %s\n" % fqdn) - sys.stderr.write("rules_file: %s\n" % rules_file) - sys.stderr.write("k5login_file: %s\n" % k5login_file) + sys.stderr.write("fqdn: %s\n" % fqdn) + sys.stderr.write("rules_file: %s\n" % rules_file) + sys.stderr.write("k5login_file: %s\n" % k5login_file) principals, rules = read_rules(rules_file) outfd = open(k5login_file, "w") for princ in principals: - masks = rules[princ] - if test_rule(fqdn, masks): - outfd.write(princ+"\n") + masks = rules[princ] + if test_rule(fqdn, masks): + outfd.write(princ+"\n") outfd.close() diff --git a/security/win32-cred.py b/security/win32-cred.py index df1b64b14..780e15c19 100644 --- a/security/win32-cred.py +++ b/security/win32-cred.py @@ -6,231 +6,231 @@ import pywintypes from win32cred import * -ERROR_NOT_FOUND = 0x490 -ERROR_NO_SUCH_LOGON_SESSION = 0x520 -ERROR_INVALID_FLAGS = 0x3ec +ERROR_NOT_FOUND = 0x490 +ERROR_NO_SUCH_LOGON_SESSION = 0x520 +ERROR_INVALID_FLAGS = 0x3ec -CRED_TYPE_DOMAIN_EXTENDED = 0x6 -CRED_ENUMERATE_ALL_CREDENTIALS = 0x1 +CRED_TYPE_DOMAIN_EXTENDED = 0x6 +CRED_ENUMERATE_ALL_CREDENTIALS = 0x1 CredPersist = { - "no": 0, - "session": CRED_PERSIST_SESSION, - "local": CRED_PERSIST_LOCAL_MACHINE, - "enterprise": CRED_PERSIST_ENTERPRISE, + "no": 0, + "session": CRED_PERSIST_SESSION, + "local": CRED_PERSIST_LOCAL_MACHINE, + "enterprise": CRED_PERSIST_ENTERPRISE, } CredType = { - "generic": CRED_TYPE_GENERIC, - "domain": CRED_TYPE_DOMAIN_PASSWORD, - "domcert": CRED_TYPE_DOMAIN_CERTIFICATE, - "passport": CRED_TYPE_DOMAIN_VISIBLE_PASSWORD, - "domext": CRED_TYPE_DOMAIN_EXTENDED, + "generic": CRED_TYPE_GENERIC, + "domain": CRED_TYPE_DOMAIN_PASSWORD, + "domcert": CRED_TYPE_DOMAIN_CERTIFICATE, + "passport": CRED_TYPE_DOMAIN_VISIBLE_PASSWORD, + "domext": CRED_TYPE_DOMAIN_EXTENDED, } CredFlags = { - "username-target": CRED_FLAGS_USERNAME_TARGET + "username-target": CRED_FLAGS_USERNAME_TARGET } def handleWinError(e): - (code, func, error) = e - print("error: %s: [%d] %s" % (func, code, error), file=sys.stderr) + (code, func, error) = e + print("error: %s: [%d] %s" % (func, code, error), file=sys.stderr) def findkey(haystack, needle, default=None): - for k, v in haystack.items(): - if v == needle: - return k - return default + for k, v in haystack.items(): + if v == needle: + return k + return default def display(cred, full=True): - cType = findkey(CredType, cred["Type"], "unknown") - cPersist = findkey(CredPersist, cred["Persist"], "unknown") - cFlags = [flag for flag, value in CredFlags.items() if cred["Flags"] & value] - cBlob = cred["CredentialBlob"] - - if full: - fmt = "%12s %s" - print(fmt % ("target:", cred["TargetName"])) - if cred["TargetAlias"] is not None: - for a in cred["TargetAlias"]: - print(fmt % ("alias:", a)) - print(fmt % ("type:", cType)) - print(fmt % ("user:", cred["UserName"])) - print(fmt % ("comment:", cred["Comment"])) - print(fmt % ("persist:", cPersist)) - for flag in cFlags: - print(fmt % ("flags:", flag)) - for attr in cred["Attributes"]: - text = "%(Keyword)s=%(Value)s" % attr - print(fmt % ("attribute:", text)) - if cBlob: - print(fmt % ("blob:", "<%d bytes>" % len(cBlob))) - #print(fmt % ("", repr(cBlob))) - print() - else: - trim = lambda string, n: string[:n-3]+"..." if len(string) > n else string - print("%(TargetName)-30s %(UserName)-30s %(Type)-8s %(Persist)-3s" % { - "TargetName": trim(cred["TargetName"], 30), - "UserName": trim(cred["UserName"], 30), - "Type": cType, - "Persist": cPersist[:7], - }) - + cType = findkey(CredType, cred["Type"], "unknown") + cPersist = findkey(CredPersist, cred["Persist"], "unknown") + cFlags = [flag for flag, value in CredFlags.items() if cred["Flags"] & value] + cBlob = cred["CredentialBlob"] + + if full: + fmt = "%12s %s" + print(fmt % ("target:", cred["TargetName"])) + if cred["TargetAlias"] is not None: + for a in cred["TargetAlias"]: + print(fmt % ("alias:", a)) + print(fmt % ("type:", cType)) + print(fmt % ("user:", cred["UserName"])) + print(fmt % ("comment:", cred["Comment"])) + print(fmt % ("persist:", cPersist)) + for flag in cFlags: + print(fmt % ("flags:", flag)) + for attr in cred["Attributes"]: + text = "%(Keyword)s=%(Value)s" % attr + print(fmt % ("attribute:", text)) + if cBlob: + print(fmt % ("blob:", "<%d bytes>" % len(cBlob))) + #print(fmt % ("", repr(cBlob))) + print() + else: + trim = lambda string, n: string[:n-3]+"..." if len(string) > n else string + print("%(TargetName)-30s %(UserName)-30s %(Type)-8s %(Persist)-3s" % { + "TargetName": trim(cred["TargetName"], 30), + "UserName": trim(cred["UserName"], 30), + "Type": cType, + "Persist": cPersist[:7], + }) + cred = { - "TargetName": None, - "UserName": None, - "Persist": CredPersist["local"], - "Type": CredType["generic"], - "Comment": None, - "Attributes": {}, - "Flags": 0, + "TargetName": None, + "UserName": None, + "Persist": CredPersist["local"], + "Type": CredType["generic"], + "Comment": None, + "Attributes": {}, + "Flags": 0, } require = set() try: - action = sys.argv[1] + action = sys.argv[1] except IndexError: - print("usage:") - print(" cred {ls | ll} [targetprefix]", file=sys.stderr) - print(" cred {new | rm | read | readdom | targetinfo} [-t type] [-r require]", file=sys.stderr) - sys.exit(2) + print("usage:") + print(" cred {ls | ll} [targetprefix]", file=sys.stderr) + print(" cred {new | rm | read | readdom | targetinfo} [-t type] [-r require]", file=sys.stderr) + sys.exit(2) options, rest = getopt.gnu_getopt(sys.argv[2:], "a:c:f:P:r:t:u:") for opt, arg in options: - if opt == "-a": - key, value = arg.split("=", 1) - cred["Attributes"][key] = value - elif opt == "-c": - cred["Comment"] = arg - elif opt == "-f": - if arg in CredFlags: - cred["Flags"] |= CredFlags[arg] - else: - raise ValueError("Unknown flag %r" % arg) - elif opt == "-P": - if arg in CredPersist: - cred["Persist"] = CredPersist[arg] - else: - raise ValueError("Invalid persist value %r" % arg) - elif opt == "-r": - if arg in ("admin", "nocert", "cert", "sc"): - require.add(arg) - elif opt == "-t": - if arg in CredType: - cred["Type"] = CredType[arg] - else: - raise ValueError("Invalid type %r" % arg) - elif opt == "-u": - cred["UserName"] = arg + if opt == "-a": + key, value = arg.split("=", 1) + cred["Attributes"][key] = value + elif opt == "-c": + cred["Comment"] = arg + elif opt == "-f": + if arg in CredFlags: + cred["Flags"] |= CredFlags[arg] + else: + raise ValueError("Unknown flag %r" % arg) + elif opt == "-P": + if arg in CredPersist: + cred["Persist"] = CredPersist[arg] + else: + raise ValueError("Invalid persist value %r" % arg) + elif opt == "-r": + if arg in ("admin", "nocert", "cert", "sc"): + require.add(arg) + elif opt == "-t": + if arg in CredType: + cred["Type"] = CredType[arg] + else: + raise ValueError("Invalid type %r" % arg) + elif opt == "-u": + cred["UserName"] = arg if action in ("ls", "ll"): - full = (action == "ll") - try: - filter = rest.pop(0)+"*" - except IndexError: - filter = None - flags = 0 - try: - if full: - for cred in CredEnumerate(filter, flags): - display(cred, True) - print - else: - print("%-30s %-30s %-8s %-3s" % ("Target", "User", "Type", "Persist")) - print("-"*79) - for cred in CredEnumerate(filter, flags): - display(cred, False) - except pywintypes.error as e: - if e[0] == ERROR_NOT_FOUND: - print("No credentials stored.") - else: - handleWinError(e) + full = (action == "ll") + try: + filter = rest.pop(0)+"*" + except IndexError: + filter = None + flags = 0 + try: + if full: + for cred in CredEnumerate(filter, flags): + display(cred, True) + print + else: + print("%-30s %-30s %-8s %-3s" % ("Target", "User", "Type", "Persist")) + print("-"*79) + for cred in CredEnumerate(filter, flags): + display(cred, False) + except pywintypes.error as e: + if e[0] == ERROR_NOT_FOUND: + print("No credentials stored.") + else: + handleWinError(e) elif action == "new": - cred["TargetName"] = rest.pop(0) - flags = 0 - flags |= CREDUI_FLAGS_DO_NOT_PERSIST - - if cred["Type"] == CRED_TYPE_GENERIC: - flags |= CREDUI_FLAGS_GENERIC_CREDENTIALS - flags |= CREDUI_FLAGS_ALWAYS_SHOW_UI - elif cred["Type"] == CRED_TYPE_DOMAIN_PASSWORD: - flags |= CREDUI_FLAGS_EXCLUDE_CERTIFICATES - elif cred["Type"] == CRED_TYPE_DOMAIN_CERTIFICATE: - flags |= CREDUI_FLAGS_REQUIRE_CERTIFICATE - - if cred["Flags"] & CRED_FLAGS_USERNAME_TARGET: - flags |= CREDUI_FLAGS_USERNAME_TARGET_CREDENTIALS - cred["UserName"] = cred["TargetName"] - - if "cert" in require: - flags |= CREDUI_FLAGS_REQUIRE_CERTIFICATE - if "sc" in require: - flags |= CREDUI_FLAGS_REQUIRE_SMARTCARD - if "nocert" in require: - flags |= CREDUI_FLAGS_EXCLUDE_CERTIFICATES - if "admin" in require: - flags |= CREDUI_FLAGS_REQUEST_ADMINISTRATOR - - try: - user, blob, persist = CredUIPromptForCredentials( - cred["TargetName"], 0, cred["UserName"], None, False, flags) - cred["UserName"], cred["CredentialBlob"] = user, blob - CredWrite(cred) - except pywintypes.error as e: - handleWinError(e) - else: - cred = CredRead(cred["TargetName"], cred["Type"]) - display(cred) + cred["TargetName"] = rest.pop(0) + flags = 0 + flags |= CREDUI_FLAGS_DO_NOT_PERSIST + + if cred["Type"] == CRED_TYPE_GENERIC: + flags |= CREDUI_FLAGS_GENERIC_CREDENTIALS + flags |= CREDUI_FLAGS_ALWAYS_SHOW_UI + elif cred["Type"] == CRED_TYPE_DOMAIN_PASSWORD: + flags |= CREDUI_FLAGS_EXCLUDE_CERTIFICATES + elif cred["Type"] == CRED_TYPE_DOMAIN_CERTIFICATE: + flags |= CREDUI_FLAGS_REQUIRE_CERTIFICATE + + if cred["Flags"] & CRED_FLAGS_USERNAME_TARGET: + flags |= CREDUI_FLAGS_USERNAME_TARGET_CREDENTIALS + cred["UserName"] = cred["TargetName"] + + if "cert" in require: + flags |= CREDUI_FLAGS_REQUIRE_CERTIFICATE + if "sc" in require: + flags |= CREDUI_FLAGS_REQUIRE_SMARTCARD + if "nocert" in require: + flags |= CREDUI_FLAGS_EXCLUDE_CERTIFICATES + if "admin" in require: + flags |= CREDUI_FLAGS_REQUEST_ADMINISTRATOR + + try: + user, blob, persist = CredUIPromptForCredentials( + cred["TargetName"], 0, cred["UserName"], None, False, flags) + cred["UserName"], cred["CredentialBlob"] = user, blob + CredWrite(cred) + except pywintypes.error as e: + handleWinError(e) + else: + cred = CredRead(cred["TargetName"], cred["Type"]) + display(cred) elif action == "add": - cred["TargetName"] = rest.pop(0) - CredWrite(cred) + cred["TargetName"] = rest.pop(0) + CredWrite(cred) elif action == "rm": - cred["TargetName"] = rest.pop(0) - try: - CredDelete(cred["TargetName"], cred["Type"]) - except pywintypes.error as e: - handleWinError(e) + cred["TargetName"] = rest.pop(0) + try: + CredDelete(cred["TargetName"], cred["Type"]) + except pywintypes.error as e: + handleWinError(e) elif action == "read": - cred["TargetName"] = rest.pop(0) - try: - cred = CredRead(cred["TargetName"], cred["Type"]) - display(cred) - except pywintypes.error as e: - handleWinError(e) + cred["TargetName"] = rest.pop(0) + try: + cred = CredRead(cred["TargetName"], cred["Type"]) + display(cred) + except pywintypes.error as e: + handleWinError(e) elif action == "readdom": - ttype, tname = rest.pop(0).split(":", 1) - keys = { - "target": "TargetName", - "nbserver": "NetbiosServerName", - "nbdomain": "NetbiosDomainName", - "server": "DnsServerName", - "domain": "DnsDomainName", - "tree": "DnsTreeName", - } - key = keys.get(ttype, keys["target"]) - try: - for cred in CredReadDomainCredentials({key: tname}): - display(cred) - except pywintypes.error as e: - handleWinError(e) + ttype, tname = rest.pop(0).split(":", 1) + keys = { + "target": "TargetName", + "nbserver": "NetbiosServerName", + "nbdomain": "NetbiosDomainName", + "server": "DnsServerName", + "domain": "DnsDomainName", + "tree": "DnsTreeName", + } + key = keys.get(ttype, keys["target"]) + try: + for cred in CredReadDomainCredentials({key: tname}): + display(cred) + except pywintypes.error as e: + handleWinError(e) elif action == "targetinfo": - for target in rest: - info = CredGetTargetInfo(target) - keys = info.keys() - keys.sort() - keys.remove("TargetName") - keys.insert(0, "TargetName") - for key in keys: - value = info[key] - if key == "CredTypes": - value = ", ".join(findkey(CredType, i, str(i)) - for i in value) if value else None - elif key == "Flags": - flags = set() - if value & CRED_ALLOW_NAME_RESOLUTION: - flags.add("allow name resolution") - value = ", ".join(flags) if flags else None - print("%18s: %s" % (key, value or "")) - print() + for target in rest: + info = CredGetTargetInfo(target) + keys = info.keys() + keys.sort() + keys.remove("TargetName") + keys.insert(0, "TargetName") + for key in keys: + value = info[key] + if key == "CredTypes": + value = ", ".join(findkey(CredType, i, str(i)) + for i in value) if value else None + elif key == "Flags": + flags = set() + if value & CRED_ALLOW_NAME_RESOLUTION: + flags.add("allow name resolution") + value = ", ".join(flags) if flags else None + print("%18s: %s" % (key, value or "")) + print() else: - print("Error: Unknown action %r" % action) + print("Error: Unknown action %r" % action) diff --git a/system/envcp b/system/envcp index c01a08dcf..3679cbbc9 100755 --- a/system/envcp +++ b/system/envcp @@ -4,35 +4,34 @@ import os import sys def usage(): - print("Usage: envcp pid command [args ...]", file=sys.stderr) - sys.exit(2) + print("Usage: envcp pid command [args ...]", file=sys.stderr) + sys.exit(2) def read_env(pid): - try: - fd = open("/proc/%d/environ" % pid, "rb") - except IOError as e: - print("error: [%d] %s: %s" % (e.errno, e.strerror, - e.filename)) - return None - env = fd.read().split(b'\0') - return dict(k.split(b'=', 1) for k in env if k != b'') + try: + fd = open("/proc/%d/environ" % pid, "rb") + except IOError as e: + print("error: [%d] %s: %s" % (e.errno, e.strerror, e.filename)) + return None + env = fd.read().split(b'\0') + return dict(k.split(b'=', 1) for k in env if k != b'') try: - pid = int(sys.argv[1]) - cmd = sys.argv[2:] + pid = int(sys.argv[1]) + cmd = sys.argv[2:] except IndexError: - # not enough arguments - usage() + # not enough arguments + usage() except ValueError: - # pid is not integer - print("error: pid must be an integer", file=sys.stderr()) - usage() + # pid is not integer + print("error: pid must be an integer", file=sys.stderr()) + usage() if not len(cmd): - cmd = ["/usr/bin/env"] + cmd = ["/usr/bin/env"] env = read_env(pid) if env: - os.execvpe(cmd[0], cmd, env) + os.execvpe(cmd[0], cmd, env) else: - sys.exit(1) + sys.exit(1) diff --git a/system/ldmissing b/system/ldmissing index 8043cbcaf..9a850554f 100755 --- a/system/ldmissing +++ b/system/ldmissing @@ -10,250 +10,249 @@ needed_re = re.compile(r'Shared library: \[(.+)\]$') rpath_re = re.compile(r'Library rpath: \[(.+)\]$') def parse_ldconf(conf_path): - if not os.path.exists(conf_path): - print("warning: config file %s not found" % conf_path, file=sys.stderr) - return - for line in open(conf_path): - line = line.strip() - if not line or line.startswith("#"): - continue - line = line.split() - if line[0] == "include" and line[1]: - for incl_path in glob.glob(line[1]): - yield from parse_ldconf(incl_path) - else: - yield line[0] + if not os.path.exists(conf_path): + print("warning: config file %s not found" % conf_path, file=sys.stderr) + return + for line in open(conf_path): + line = line.strip() + if not line or line.startswith("#"): + continue + line = line.split() + if line[0] == "include" and line[1]: + for incl_path in glob.glob(line[1]): + yield from parse_ldconf(incl_path) + else: + yield line[0] def get_lib_paths(): - """ - Return an array of paths where ld.so searches for libraries. - """ - paths = [] - if "LD_LIBRARY_PATH" in os.environ: - paths += os.environ["LD_LIBRARY_PATH"].split(":") - paths += parse_ldconf("/etc/ld.so.conf") - paths += ["/lib", "/usr/lib"] - return paths + """ + Return an array of paths where ld.so searches for libraries. + """ + paths = [] + if "LD_LIBRARY_PATH" in os.environ: + paths += os.environ["LD_LIBRARY_PATH"].split(":") + paths += parse_ldconf("/etc/ld.so.conf") + paths += ["/lib", "/usr/lib"] + return paths def find_in_path(paths, basename): - """ - Return the first existing path of a file `basename` in given paths. - Return None if not found. + """ + Return the first existing path of a file `basename` in given paths. + Return None if not found. - >>> find_in_path(os.environ["PATH"], "sh") - "/bin/sh" - """ - if "/" in basename: - return os.path.abspath(basename) - for _dir in paths: - path = os.path.join(_dir, basename) - if os.path.exists(path): - return path + >>> find_in_path(os.environ["PATH"], "sh") + "/bin/sh" + """ + if "/" in basename: + return os.path.abspath(basename) + for _dir in paths: + path = os.path.join(_dir, basename) + if os.path.exists(path): + return path def read_needed(path): - """ - Read direct dependencies of an ELF file. - """ - deps = set() - rpaths = [] - proc = subprocess.Popen(["readelf", "-d", path], stdout=subprocess.PIPE) - for line in proc.stdout: - line = line.decode("utf-8") - m = needed_re.search(line) - if m: - deps.add(m.group(1)) - continue - m = rpath_re.search(line) - if m: - rpaths += m.group(1).split(":") - # TODO: parse $ORIGIN, $LIB, relative rpath - continue - return deps, rpaths + """ + Read direct dependencies of an ELF file. + """ + deps = set() + rpaths = [] + proc = subprocess.Popen(["readelf", "-d", path], stdout=subprocess.PIPE) + for line in proc.stdout: + line = line.decode("utf-8") + m = needed_re.search(line) + if m: + deps.add(m.group(1)) + continue + m = rpath_re.search(line) + if m: + rpaths += m.group(1).split(":") + # TODO: parse $ORIGIN, $LIB, relative rpath + continue + return deps, rpaths # Functions for displaying dependency trees # (will not work for "normal" trees or graphs) def is_terminal(): - return hasattr(sys.stdout, "isatty") and sys.stdout.isatty() + return hasattr(sys.stdout, "isatty") and sys.stdout.isatty() def show_tree(root, tree, indent=0, highlight=None, ctx=None): - """ - Print dict `tree` {item: [children...]} starting at a given `root` - as a textual tree. Recurse for each item in `tree[root]` as new root. + """ + Print dict `tree` {item: [children...]} starting at a given `root` + as a textual tree. Recurse for each item in `tree[root]` as new root. - >>> tree = {"a": {"b", "c"}, "b": {"d"}, "c": {"b", "d"}} - >>> show_tree("a", tree) - a - ├─b - │ └─d - └─c - ├─b - │ └─d - └─d - """ - depth, branches = ctx or (0, []) - if depth == 0: - print(" "*indent + root) - if root not in tree: - return - branches += [None] - if not highlight: - highlight = dict() - children = tree[root] - more = len(children) - for child in sorted(children): - more -= 1 - branches[depth] = ("├" if more else "└") + "─" - prefix, suffix = "", "" - if is_terminal() and child in highlight: - color = highlight[child] - prefix = "\033[%sm" % color - suffix = "\033[m" - print(" "*indent + "".join(branches) + prefix + child + suffix) - if child in tree: - branches[depth] = ("│" if more else " ") + " " - ctx = depth + 1, branches.copy() - show_tree(child, tree, indent, highlight, ctx) + >>> tree = {"a": {"b", "c"}, "b": {"d"}, "c": {"b", "d"}} + >>> show_tree("a", tree) + a + ├─b + │ └─d + └─c + ├─b + │ └─d + └─d + """ + depth, branches = ctx or (0, []) + if depth == 0: + print(" "*indent + root) + if root not in tree: + return + branches += [None] + if not highlight: + highlight = dict() + children = tree[root] + more = len(children) + for child in sorted(children): + more -= 1 + branches[depth] = ("├" if more else "└") + "─" + prefix, suffix = "", "" + if is_terminal() and child in highlight: + color = highlight[child] + prefix = "\033[%sm" % color + suffix = "\033[m" + print(" "*indent + "".join(branches) + prefix + child + suffix) + if child in tree: + branches[depth] = ("│" if more else " ") + " " + ctx = depth + 1, branches.copy() + show_tree(child, tree, indent, highlight, ctx) def walk_tree(prefix, tree): - """ - Return a list containing all possible paths that start with `prefix` - and exist in the `tree` dict. (`prefix` must be a list, even if it - consists of a single item only.) + """ + Return a list containing all possible paths that start with `prefix` + and exist in the `tree` dict. (`prefix` must be a list, even if it + consists of a single item only.) - >>> tree = {"a": {"b", "c"}, "b": {"c"}, "c": {"b", "d"}} - >>> tree - {'a': {'b', 'c'}, - 'b': {'c'}, - 'c': {'b', 'd'}} - >>> list(walk_tree(["a"], tree)) - [['a', 'c', 'd'], - ['a', 'c', 'b', 'd'], - ['a', 'b', 'd']] - """ - children = tree[prefix[-1]] - for child in children: - if child in prefix: - raise ValueError("dependency loop detected at %r + %r" \ - % (prefix, child)) - chain = prefix + [child] - if child in tree and tree[child]: - yield from walk_tree(chain, tree) - else: - yield chain + >>> tree = {"a": {"b", "c"}, "b": {"c"}, "c": {"b", "d"}} + >>> tree + {'a': {'b', 'c'}, + 'b': {'c'}, + 'c': {'b', 'd'}} + >>> list(walk_tree(["a"], tree)) + [['a', 'c', 'd'], + ['a', 'c', 'b', 'd'], + ['a', 'b', 'd']] + """ + children = tree[prefix[-1]] + for child in children: + if child in prefix: + raise ValueError("dependency loop detected at %r + %r" % (prefix, child)) + chain = prefix + [child] + if child in tree and tree[child]: + yield from walk_tree(chain, tree) + else: + yield chain def flip_tree(roots, tree): - """ - Return a dict of reverse dependencies where each root in `roots` - becomes a leaf if formatted as a tree. (The output is usually - formatted as *multiple* trees, one for each key in resulting tree.) + """ + Return a dict of reverse dependencies where each root in `roots` + becomes a leaf if formatted as a tree. (The output is usually + formatted as *multiple* trees, one for each key in resulting tree.) - >>> tree - {'a': {'b', 'c'}, - 'b': {'c'}, - 'c': {'b', 'd'}} - >>> flip_tree(["a"], tree) - {'d': {'c', 'b'}, - 'c': {'a'}, - 'b': {'a', 'c'}} - >>> show_tree("d", flip_tree(["a"], tree)) - d - ├─b - │ ├─a - │ └─c - │ └─a - └─c - └─a - """ - flipped_tree = dict() - for root in roots: - for chain in walk_tree([root], tree): - user = chain.pop() - while chain: - dep = chain.pop() - if user not in flipped_tree: - flipped_tree[user] = set() - flipped_tree[user].add(dep) - user = dep - return flipped_tree + >>> tree + {'a': {'b', 'c'}, + 'b': {'c'}, + 'c': {'b', 'd'}} + >>> flip_tree(["a"], tree) + {'d': {'c', 'b'}, + 'c': {'a'}, + 'b': {'a', 'c'}} + >>> show_tree("d", flip_tree(["a"], tree)) + d + ├─b + │ ├─a + │ └─c + │ └─a + └─c + └─a + """ + flipped_tree = dict() + for root in roots: + for chain in walk_tree([root], tree): + user = chain.pop() + while chain: + dep = chain.pop() + if user not in flipped_tree: + flipped_tree[user] = set() + flipped_tree[user].add(dep) + user = dep + return flipped_tree class LdMissing(object): - def __init__(self, exe_path=None): - self.exe_path = exe_path - self.lib_paths = get_lib_paths() - self.lib_rpaths = dict() - self.resolved_paths = dict() - self.forward_deps = dict() - self.reverse_deps = dict() - self.missing_libs = set() + def __init__(self, exe_path=None): + self.exe_path = exe_path + self.lib_paths = get_lib_paths() + self.lib_rpaths = dict() + self.resolved_paths = dict() + self.forward_deps = dict() + self.reverse_deps = dict() + self.missing_libs = set() - def find_missing(self): - if not self.exe_path.startswith("/"): - raise ValueError("exe_path must be an absolute path") + def find_missing(self): + if not self.exe_path.startswith("/"): + raise ValueError("exe_path must be an absolute path") - self.missing_libs = set() + self.missing_libs = set() - todo = {(None, self.exe_path)} + todo = {(None, self.exe_path)} - while todo: - parent, elf_name = todo.pop() + while todo: + parent, elf_name = todo.pop() - if elf_name in self.forward_deps: - continue + if elf_name in self.forward_deps: + continue - if elf_name in self.resolved_paths: - elf_path = self.resolved_paths[elf_name] - else: - paths = self.lib_rpaths.get(parent, []) + self.lib_paths - elf_path = find_in_path(paths, elf_name) - self.resolved_paths[elf_name] = elf_path + if elf_name in self.resolved_paths: + elf_path = self.resolved_paths[elf_name] + else: + paths = self.lib_rpaths.get(parent, []) + self.lib_paths + elf_path = find_in_path(paths, elf_name) + self.resolved_paths[elf_name] = elf_path - if elf_path is None: - self.missing_libs.add(elf_name) - continue + if elf_path is None: + self.missing_libs.add(elf_name) + continue - deps, rpaths = read_needed(elf_path) - self.forward_deps[elf_name] = deps - self.lib_rpaths[elf_path] = rpaths + deps, rpaths = read_needed(elf_path) + self.forward_deps[elf_name] = deps + self.lib_rpaths[elf_path] = rpaths - for dep in deps: - if dep not in self.reverse_deps: - self.reverse_deps[dep] = set() - self.reverse_deps[dep].add(elf_name) + for dep in deps: + if dep not in self.reverse_deps: + self.reverse_deps[dep] = set() + self.reverse_deps[dep].add(elf_name) - todo |= {(elf_path, dep) for dep in deps} + todo |= {(elf_path, dep) for dep in deps} - return self.missing_libs + return self.missing_libs - def show_missing(self): - for lib in self.missing_libs: - print("Missing: %s" % lib) - for user in self.reverse_deps[lib]: - print(" ← %s" % user) + def show_missing(self): + for lib in self.missing_libs: + print("Missing: %s" % lib) + for user in self.reverse_deps[lib]: + print(" ← %s" % user) - def show_reverse_tree(self): - print("Reverse dependencies:") - for lib in self.missing_libs: - show_tree(lib, self.reverse_deps, indent=2) + def show_reverse_tree(self): + print("Reverse dependencies:") + for lib in self.missing_libs: + show_tree(lib, self.reverse_deps, indent=2) - def show_forward_tree(self): - flipped_deps = flip_tree(self.missing_libs, self.reverse_deps) - print("Forward dependencies:") - show_tree(self.exe_path, flipped_deps, indent=2, - highlight={lib: "38;5;11" for lib in self.missing_libs}) + def show_forward_tree(self): + flipped_deps = flip_tree(self.missing_libs, self.reverse_deps) + print("Forward dependencies:") + show_tree(self.exe_path, flipped_deps, indent=2, + highlight={lib: "38;5;11" for lib in self.missing_libs}) if __name__ == "__main__": - os_paths = os.environ.get("PATH", "").split(":") - ldm = LdMissing() + os_paths = os.environ.get("PATH", "").split(":") + ldm = LdMissing() - for exe_path in sys.argv[1:]: - ldm.exe_path = find_in_path(os_paths, exe_path) - print("Scanning %s" % ldm.exe_path) - missing = ldm.find_missing() - m = os.environ.get("PRETEND_MISSING", "") - if m: - missing |= set(m.split(",")) - if missing: - ldm.show_missing() - #ldm.show_reverse_tree() - ldm.show_forward_tree() + for exe_path in sys.argv[1:]: + ldm.exe_path = find_in_path(os_paths, exe_path) + print("Scanning %s" % ldm.exe_path) + missing = ldm.find_missing() + m = os.environ.get("PRETEND_MISSING", "") + if m: + missing |= set(m.split(",")) + if missing: + ldm.show_missing() + #ldm.show_reverse_tree() + ldm.show_forward_tree() diff --git a/system/systemd-coredump-extract b/system/systemd-coredump-extract index 0ac9efc2a..6e724848a 100755 --- a/system/systemd-coredump-extract +++ b/system/systemd-coredump-extract @@ -15,90 +15,90 @@ import tempfile BUF_MAX = 80 << 20 def journal_read(match): - global BUF_MAX - proc = subprocess.Popen( - ["journalctl", "-o", "export"] + match, - stdout=subprocess.PIPE) - journal = proc.stdout - record = {} - while True: - line = journal.readline() - if not line: - break - elif line == b'\n': - yield record - record = {} - elif b'=' in line: - line = line.rstrip(b'\n') - key, val = line.split(b'=', 1) - key = key.decode("utf-8") - val = val.decode("utf-8") - # TODO: if needed, add support for multiple values - record[key] = val - else: - line = line.rstrip(b'\n') - key = line.decode("utf-8") - size = journal.read(8) - size, = struct.unpack(" BUF_MAX: - file = tempfile.NamedTemporaryFile(dir="/var/tmp", - delete=False) - while size > 0: - left = min(BUF_MAX, size) - try: - buf = journal.read(left) - except MemoryError: - BUF_MAX -= (8 << 20) - if BUF_MAX <= 0: - raise - continue - file.write(buf) - size -= len(buf) - journal.read(1) - # TODO: as above - record[key] = (file.tell(), file.name) - file.close() - else: - val = b"" - while size > 0: - buf = journal.read(size) - val += buf - size -= len(buf) - journal.read(1) - # TODO: as above - record[key] = val - if record: - yield record + global BUF_MAX + proc = subprocess.Popen( + ["journalctl", "-o", "export"] + match, + stdout=subprocess.PIPE) + journal = proc.stdout + record = {} + while True: + line = journal.readline() + if not line: + break + elif line == b'\n': + yield record + record = {} + elif b'=' in line: + line = line.rstrip(b'\n') + key, val = line.split(b'=', 1) + key = key.decode("utf-8") + val = val.decode("utf-8") + # TODO: if needed, add support for multiple values + record[key] = val + else: + line = line.rstrip(b'\n') + key = line.decode("utf-8") + size = journal.read(8) + size, = struct.unpack(" BUF_MAX: + file = tempfile.NamedTemporaryFile(dir="/var/tmp", + delete=False) + while size > 0: + left = min(BUF_MAX, size) + try: + buf = journal.read(left) + except MemoryError: + BUF_MAX -= (8 << 20) + if BUF_MAX <= 0: + raise + continue + file.write(buf) + size -= len(buf) + journal.read(1) + # TODO: as above + record[key] = (file.tell(), file.name) + file.close() + else: + val = b"" + while size > 0: + buf = journal.read(size) + val += buf + size -= len(buf) + journal.read(1) + # TODO: as above + record[key] = val + if record: + yield record match_id = "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1" match = [match_id] for arg in sys.argv[1:]: - match.append(arg) - if arg == "+": - match.append(match_id) + match.append(arg) + if arg == "+": + match.append(match_id) for record in journal_read(match): - try: - pid = int(record["COREDUMP_PID"]) - comm = record["COREDUMP_COMM"] - core = record["COREDUMP"] - except KeyError as e: - print("skipped incomplete record (missing %r field)" % e.args) - continue + try: + pid = int(record["COREDUMP_PID"]) + comm = record["COREDUMP_COMM"] + core = record["COREDUMP"] + except KeyError as e: + print("skipped incomplete record (missing %r field)" % e.args) + continue - path = "%s.%d.core" % (comm, pid) + path = "%s.%d.core" % (comm, pid) - if type(core) is tuple: - size, temp_path = core - with open(temp_path, "rb") as in_fh: - os.unlink(temp_path) - with open(path, "wb") as out_fh: - shutil.copyfileobj(in_fh, out_fh) - else: - size = len(core) - with open(path, "wb") as out_fh: - out_fh.write(core) + if type(core) is tuple: + size, temp_path = core + with open(temp_path, "rb") as in_fh: + os.unlink(temp_path) + with open(path, "wb") as out_fh: + shutil.copyfileobj(in_fh, out_fh) + else: + size = len(core) + with open(path, "wb") as out_fh: + out_fh.write(core) - print("saved core dump of pid=%d comm=%r (%d bytes) to %r" % \ - (pid, comm, size, path)) + print("saved core dump of pid=%d comm=%r (%d bytes) to %r" % \ + (pid, comm, size, path)) diff --git a/term/xterm-color-chooser b/term/xterm-color-chooser index 117baa847..4d91b28bc 100755 --- a/term/xterm-color-chooser +++ b/term/xterm-color-chooser @@ -7,236 +7,233 @@ import termios import json modes = { - "rgb": "256-color pallette – 6*6*6 RGB subset", - "gray": "256-color pallette – grayscale subset", - "sys": "256-color pallette – ansicolor subset", - "iso": "ISO 8-color pallette", + "rgb": "256-color pallette – 6*6*6 RGB subset", + "gray": "256-color pallette – grayscale subset", + "sys": "256-color pallette – ansicolor subset", + "iso": "ISO 8-color pallette", } properties = { - "mode": (None, lambda: "rgb"), - "flags": (None, set), - "red": (0, 5), - "green": (0, 5), - "blue": (0, 5), - "color": (0, 15), - "gray": (0, 23), + "mode": (None, lambda: "rgb"), + "flags": (None, set), + "red": (0, 5), + "green": (0, 5), + "blue": (0, 5), + "color": (0, 15), + "gray": (0, 23), } state_dir = os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")) state_path = os.path.join(state_dir, "xterm-color-chooser.json") class State(dict): - def copy(self): - return State(self) - - def init(self): - self["mode"] = "rgb" - self["flags"] = set() - self["favcolors"] = [] - self["favstates"] = {} - self.reset("red", "green", "blue", "color", "gray") - - def load(self, data, recurse=True): - self.init() - for prop in properties: - self[prop] = data[prop] - self["flags"] = set(data["flags"]) - if "favstates" in data: - self["favcolors"] = data["favcolors"][:] - self["favstates"] = {st: State().load(data["favstates"][st]) - for st in data["favstates"]} - return self - - def save(self, recurse=True): - data = {} - for prop in properties: - data[prop] = self[prop] - data["flags"] = list(self["flags"]) - if recurse: - data["favcolors"] = self["favcolors"][:] - data["favstates"] = {st: self["favstates"][st].save(False) - for st in self["favstates"]} - return data - - def load_persistent(self): - try: - with open(state_path, "r") as fh: - self.load(json.load(fh)) - except: - pass - - def save_persistent(self): - with open(state_path, "w") as fh: - json.dump(self.save(), fh) - - def incr(self, *props): - for prop in props: - minval, maxval = properties[prop] - if self[prop] < maxval: - self[prop] += 1 - - def decr(self, *props): - for prop in props: - minval, maxval = properties[prop] - if self[prop] > minval: - self[prop] -= 1 - - def incr_carry(self, *props): - for prop in props: - minval, maxval = properties[prop] - if self[prop] < maxval: - self[prop] += 1 - break - else: - self[prop] = 0 - - def decr_carry(self, *props): - for prop in props: - minval, maxval = properties[prop] - if self[prop] > minval: - self[prop] -= 1 - break - else: - self[prop] = maxval - - def reset(self, *props): - for prop in props: - minval, maxval = properties[prop] - if minval is None: - self[prop] = maxval() - else: - self[prop] = int((minval+maxval)/2.0) - - def toggle(self, *flags): - for flag in flags: - if flag in self["flags"]: - self["flags"].remove(flag) - else: - self["flags"].add(flag) - - def toggle_fav(self): - col = str(self.getcolor()) - if col in self["favstates"]: - del self["favstates"][col] - self["favcolors"].remove(col) - else: - self["favstates"][col] = self.copy() - self["favcolors"].append(col) - - def load_fav(self, pos): - try: - col = self["favcolors"][pos] - except IndexError: - return - - for prop in properties: - if prop == "flags": - continue - self[prop] = self["favstates"][col][prop] - - def getcolor(self, iso=False): - if iso and self["mode"] == "iso": - return self["color"] - elif self["mode"] == "sys": - return self["color"] - elif self["mode"] == "rgb": - return 16 + self["red"]*36 + self["green"]*6 + self["blue"] - elif self["mode"] == "gray": - return 232 + self["gray"] - else: - return None - - def setcolor(self, color): - if color <= 15: - self["mode"] = iso - self["color"] = color - elif color <= 231: - self["mode"] = "rgb" - color -= 16 - self["blue"] = color % 6 - color = (color - self["blue"]) / 6 - self["green"] = color % 6 - color = (color - self["green"]) / 6 - self["red"] = color - elif color <= 255: - self["mode"] = "gray" - self["gray"] = color - 232 - - def fmt(self, flags=True, bg=False): - out = "" - - # output basic SGR - - sgr = [] - - if flags: - sgr += self["flags"] - - if self["mode"] == "iso": - color = self["color"] - if color > 7: - color -= 8 - if 1 not in sgr: - sgr.append(1) - color += 40 if bg else 30 - sgr.append(color) - - if len(sgr) > 0: - sgr.sort() - out += "\033[%sm" % ";".join(map(str, sgr)) - - # output 256-color - - color = self.getcolor() - - if color is not None: - out += "\033[%d;5;%dm" % (48 if bg else 38, color) - - return out - - @property - def ansi(self): - return self.fmt(True) + def copy(self): + return State(self) + + def init(self): + self["mode"] = "rgb" + self["flags"] = set() + self["favcolors"] = [] + self["favstates"] = {} + self.reset("red", "green", "blue", "color", "gray") + + def load(self, data, recurse=True): + self.init() + for prop in properties: + self[prop] = data[prop] + self["flags"] = set(data["flags"]) + if "favstates" in data: + self["favcolors"] = data["favcolors"][:] + self["favstates"] = {st: State().load(data["favstates"][st]) + for st in data["favstates"]} + return self + + def save(self, recurse=True): + data = {} + for prop in properties: + data[prop] = self[prop] + data["flags"] = list(self["flags"]) + if recurse: + data["favcolors"] = self["favcolors"][:] + data["favstates"] = {st: self["favstates"][st].save(False) + for st in self["favstates"]} + return data + + def load_persistent(self): + try: + with open(state_path, "r") as fh: + self.load(json.load(fh)) + except: + pass + + def save_persistent(self): + with open(state_path, "w") as fh: + json.dump(self.save(), fh) + + def incr(self, *props): + for prop in props: + minval, maxval = properties[prop] + if self[prop] < maxval: + self[prop] += 1 + + def decr(self, *props): + for prop in props: + minval, maxval = properties[prop] + if self[prop] > minval: + self[prop] -= 1 + + def incr_carry(self, *props): + for prop in props: + minval, maxval = properties[prop] + if self[prop] < maxval: + self[prop] += 1 + break + else: + self[prop] = 0 + + def decr_carry(self, *props): + for prop in props: + minval, maxval = properties[prop] + if self[prop] > minval: + self[prop] -= 1 + break + else: + self[prop] = maxval + + def reset(self, *props): + for prop in props: + minval, maxval = properties[prop] + if minval is None: + self[prop] = maxval() + else: + self[prop] = int((minval+maxval)/2.0) + + def toggle(self, *flags): + for flag in flags: + if flag in self["flags"]: + self["flags"].remove(flag) + else: + self["flags"].add(flag) + + def toggle_fav(self): + col = str(self.getcolor()) + if col in self["favstates"]: + del self["favstates"][col] + self["favcolors"].remove(col) + else: + self["favstates"][col] = self.copy() + self["favcolors"].append(col) + + def load_fav(self, pos): + try: + col = self["favcolors"][pos] + except IndexError: + return + + for prop in properties: + if prop == "flags": + continue + self[prop] = self["favstates"][col][prop] + + def getcolor(self, iso=False): + if iso and self["mode"] == "iso": + return self["color"] + elif self["mode"] == "sys": + return self["color"] + elif self["mode"] == "rgb": + return 16 + self["red"]*36 + self["green"]*6 + self["blue"] + elif self["mode"] == "gray": + return 232 + self["gray"] + else: + return None + + def setcolor(self, color): + if color <= 15: + self["mode"] = iso + self["color"] = color + elif color <= 231: + self["mode"] = "rgb"; color -= 16 + self["blue"] = color % 6; color = (color - self["blue"]) / 6 + self["green"] = color % 6; color = (color - self["green"]) / 6 + self["red"] = color + elif color <= 255: + self["mode"] = "gray" + self["gray"] = color - 232 + + def fmt(self, flags=True, bg=False): + out = "" + + # output basic SGR + + sgr = [] + + if flags: + sgr += self["flags"] + + if self["mode"] == "iso": + color = self["color"] + if color > 7: + color -= 8 + if 1 not in sgr: + sgr.append(1) + color += 40 if bg else 30 + sgr.append(color) + + if len(sgr) > 0: + sgr.sort() + out += "\033[%sm" % ";".join(map(str, sgr)) + + # output 256-color + + color = self.getcolor() + + if color is not None: + out += "\033[%d;5;%dm" % (48 if bg else 38, color) + + return out + + @property + def ansi(self): + return self.fmt(True) def getch(): - import sys, tty, termios - fd = sys.stdin.fileno() - old = termios.tcgetattr(fd) - try: - #tty.setraw(fd) - return sys.stdin.read(1) - finally: - termios.tcsetattr(fd, termios.TCSADRAIN, old) + import sys, tty, termios + fd = sys.stdin.fileno() + old = termios.tcgetattr(fd) + try: + #tty.setraw(fd) + return sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old) def icanon(mode): - fd = sys.stdin.fileno() - flags = termios.tcgetattr(fd) - if mode: - flags[3] &= ~termios.ICANON & ~termios.ECHO - else: - flags[3] |= termios.ICANON | termios.ECHO - termios.tcsetattr(fd, 0, flags) + fd = sys.stdin.fileno() + flags = termios.tcgetattr(fd) + if mode: + flags[3] &= ~termios.ICANON & ~termios.ECHO + else: + flags[3] |= termios.ICANON | termios.ECHO + termios.tcsetattr(fd, 0, flags) def bar(name, state, prop, scale=2, keys=""): - minval, maxval = properties[prop] - newstate = state.copy() - out = "%10s: [" % name - for val in range(minval, maxval+1): - newstate[prop] = val - out += newstate.fmt(flags=False) + "#"*scale - out += "\033[m] %-3d" % state[prop] - if keys: - out += " (keys: %s)" % keys - print(out) - - out = "%10s " % "" - for val in range(minval, maxval+1): - out += ("^" if val == state[prop] else " ") * scale - print(out) + minval, maxval = properties[prop] + newstate = state.copy() + out = "%10s: [" % name + for val in range(minval, maxval+1): + newstate[prop] = val + out += newstate.fmt(flags=False) + "#"*scale + out += "\033[m] %-3d" % state[prop] + if keys: + out += " (keys: %s)" % keys + print(out) + + out = "%10s " % "" + for val in range(minval, maxval+1): + out += ("^" if val == state[prop] else " ") * scale + print(out) if not sys.stdin.isatty(): - sys.stdin = open("/dev/tty", "r") + sys.stdin = open("/dev/tty", "r") state = State() state.init() @@ -251,156 +248,156 @@ char = "x" wait_fav = False class Sgr(object): - BOLD = 1 - DARK = 2 - ITALIC = 3 - UNDERLINE = 4 - REVERSE = 7 - STRIKE = 9 - - names = { - BOLD: "bold", - DARK: "dark", - ITALIC: "italic", - UNDERLINE: "underline", - REVERSE: "reverse", - STRIKE: "strike", - } + BOLD = 1 + DARK = 2 + ITALIC = 3 + UNDERLINE = 4 + REVERSE = 7 + STRIKE = 9 + + names = { + BOLD: "bold", + DARK: "dark", + ITALIC: "italic", + UNDERLINE: "underline", + REVERSE: "reverse", + STRIKE: "strike", + } while True: - print("\033[H\033[2J", end="") - - print(" ┌" + "─"*20 + "┬" + "─"*20 + "┬" + "─"*20 + "┐") - print(" │%-20s│%-20s│%-20s│" % - ("default bg", "iso black bg", "iso white bg")) - line = " │" - line += "\033[49m" + state.fmt() + char*20 + "\033[m│" - line += "\033[40m" + state.fmt() + char*20 + "\033[m│" - line += "\033[47m" + state.fmt() + char*20 + "\033[m│" - print("\n".join([line]*3)) - print(" │%-20s│%-20s│%-20s│" % - ("default fg", "iso black fg", "iso white fg")) - line = " │" - line += "\033[39m" + state.fmt(bg=True) + char*20 + "\033[m│" - line += "\033[30m" + state.fmt(bg=True) + char*20 + "\033[m│" - line += "\033[37m" + state.fmt(bg=True) + char*20 + "\033[m│" - print("\n".join([line]*3)) - print(" └" + "─"*20 + "┴" + "─"*20 + "┴" + "─"*20 + "┘") - print() - - indent = "%11s" % "" - - print("%11s" % "keys:", "mode g/G, format b/D/i/u/S/r, exit Q") - if state["mode"] == "rgb": - print(indent, "red 7/9 (q/e), green 4/6 (a/d), blue 1/3 (z/c)") - print(indent, "all +/-, reset 8/5/2 (w/s/x), reset all 0") - elif state["mode"] in {"sys", "iso"}: - print(indent, "color +/- (q/e), reset 0") - elif state["mode"] == "gray": - print(indent, "level +/- (q/e), reset 0") - print() - - if state["mode"] == "rgb": - bar("red", state, "red", 3, "7/9 or q/e") - bar("green", state, "green", 3, "4/6 or a/d") - bar("blue", state, "blue", 3, "1/3 or z/c") - elif state["mode"] in {"sys", "iso"}: - bar("color", state, "color", 2, "+/- or q/e") - elif state["mode"] == "gray": - bar("gray", state, "gray", 1, "+/- or q/e") - - print("%11s" % "mode:", modes[state["mode"]]) - fmtfgstr = state.fmt(flags=True).replace("\033", "\\e") - fmtbgstr = state.fmt(flags=True, bg=True).replace("\033", "\\e") - print("%11s" % "code:", - fmtfgstr, "(fg),", - fmtbgstr, "(bg)") - style = [Sgr.names[f] for f in state["flags"]] - style.append("color%d" % state.getcolor(iso=True)) - print("%11s" % "name:", " + ".join(style)) - - favs = state["favcolors"] - print() - line, count, prefix = "", 0, "favs:" - if favs: - for pos, col in enumerate(favs): - st = state["favstates"][col] - fmt = st.fmt(flags=False, bg=True) - num = pos+1 - if num == 10: - num = 0 - elif num > 10: - num = chr(ord('a') + num - 11) - if count == 8: - print("%11s" % prefix, line) - line, count, prefix = "", 0, "" - line += "%s" % (num) + fmt + " "*4 + "\033[m, " - count += 1 - print("%11s" % prefix, line) - prefix = "" - print("%11s" % prefix, "add F, jump f", - "(waiting for index)" if wait_fav else "+ index") - - k = getch() - if k == "Q": break - elif wait_fav: - if k in "123456789": k = int(k) - elif k == "0": k = 10 - elif ord('a') <= ord(k) <= ord('z'): - k = ord(k)-ord('a')+11 - else: k = None - if k is not None: - state.load_fav(k-1) - wait_fav = False - elif k == "n": - icanon(False) - n = input() - icanon(True) - state.setcolor(int(n)) - elif k == "b": state.toggle(Sgr.BOLD) - elif k == "D": state.toggle(Sgr.DARK) - elif k == "i": state.toggle(Sgr.ITALIC) - elif k == "u": state.toggle(Sgr.UNDERLINE) - elif k == "r": state.toggle(Sgr.REVERSE) - #elif k == "B": state.toggle(5) - elif k == "S": state.toggle(Sgr.STRIKE) - elif k == "F": state.toggle_fav() - elif k == "f": wait_fav = True - elif state["mode"] == "rgb": - if k == "g": state["mode"] = "sys" - elif k == "G": state["mode"] = "gray" - elif k in "7q": state.decr("red") - elif k in "8w": state.reset("red") - elif k in "9e": state.incr("red") - elif k in "4a": state.decr("green") - elif k in "5s": state.reset("green") - elif k in "6d": state.incr("green") - elif k in "1z": state.decr("blue") - elif k in "2x": state.reset("blue") - elif k in "3c": state.incr("blue") - elif k in "-": state.decr("red", "green", "blue") - elif k in "0": state.reset("red", "green", "blue", "flags") - elif k in "+": state.incr("red", "green", "blue") - elif k in "/": state.decr_carry("blue", "green", "red") - elif k in "*": state.incr_carry("blue", "green", "red") - elif state["mode"] == "sys": - if k == "g": state["mode"] = "iso" - elif k == "G": state["mode"] = "rgb" - elif k in "741qaz-": state.decr("color") - elif k in "852wsx0": state.reset("color", "flags") - elif k in "963edc+": state.incr("color") - elif state["mode"] == "iso": - if k == "g": state["mode"] = "gray" - elif k == "G": state["mode"] = "sys" - elif k in "741qaz-": state.decr("color") - elif k in "852wsx0": state.reset("color") - elif k in "963edc+": state.incr("color") - elif state["mode"] == "gray": - if k == "g": state["mode"] = "rgb" - elif k == "G": state["mode"] = "iso" - elif k in "741qaz-": state.decr("gray") - elif k in "852wsx0": state.reset("gray", "flags") - elif k in "963edc+": state.incr("gray") + print("\033[H\033[2J", end="") + + print(" ┌" + "─"*20 + "┬" + "─"*20 + "┬" + "─"*20 + "┐") + print(" │%-20s│%-20s│%-20s│" % + ("default bg", "iso black bg", "iso white bg")) + line = " │" + line += "\033[49m" + state.fmt() + char*20 + "\033[m│" + line += "\033[40m" + state.fmt() + char*20 + "\033[m│" + line += "\033[47m" + state.fmt() + char*20 + "\033[m│" + print("\n".join([line]*3)) + print(" │%-20s│%-20s│%-20s│" % + ("default fg", "iso black fg", "iso white fg")) + line = " │" + line += "\033[39m" + state.fmt(bg=True) + char*20 + "\033[m│" + line += "\033[30m" + state.fmt(bg=True) + char*20 + "\033[m│" + line += "\033[37m" + state.fmt(bg=True) + char*20 + "\033[m│" + print("\n".join([line]*3)) + print(" └" + "─"*20 + "┴" + "─"*20 + "┴" + "─"*20 + "┘") + print() + + indent = "%11s" % "" + + print("%11s" % "keys:", "mode g/G, format b/D/i/u/S/r, exit Q") + if state["mode"] == "rgb": + print(indent, "red 7/9 (q/e), green 4/6 (a/d), blue 1/3 (z/c)") + print(indent, "all +/-, reset 8/5/2 (w/s/x), reset all 0") + elif state["mode"] in {"sys", "iso"}: + print(indent, "color +/- (q/e), reset 0") + elif state["mode"] == "gray": + print(indent, "level +/- (q/e), reset 0") + print() + + if state["mode"] == "rgb": + bar("red", state, "red", 3, "7/9 or q/e") + bar("green", state, "green", 3, "4/6 or a/d") + bar("blue", state, "blue", 3, "1/3 or z/c") + elif state["mode"] in {"sys", "iso"}: + bar("color", state, "color", 2, "+/- or q/e") + elif state["mode"] == "gray": + bar("gray", state, "gray", 1, "+/- or q/e") + + print("%11s" % "mode:", modes[state["mode"]]) + fmtfgstr = state.fmt(flags=True).replace("\033", "\\e") + fmtbgstr = state.fmt(flags=True, bg=True).replace("\033", "\\e") + print("%11s" % "code:", + fmtfgstr, "(fg),", + fmtbgstr, "(bg)") + style = [Sgr.names[f] for f in state["flags"]] + style.append("color%d" % state.getcolor(iso=True)) + print("%11s" % "name:", " + ".join(style)) + + favs = state["favcolors"] + print() + line, count, prefix = "", 0, "favs:" + if favs: + for pos, col in enumerate(favs): + st = state["favstates"][col] + fmt = st.fmt(flags=False, bg=True) + num = pos+1 + if num == 10: + num = 0 + elif num > 10: + num = chr(ord('a') + num - 11) + if count == 8: + print("%11s" % prefix, line) + line, count, prefix = "", 0, "" + line += "%s" % (num) + fmt + " "*4 + "\033[m, " + count += 1 + print("%11s" % prefix, line) + prefix = "" + print("%11s" % prefix, "add F, jump f", + "(waiting for index)" if wait_fav else "+ index") + + k = getch() + if k == "Q": break + elif wait_fav: + if k in "123456789": k = int(k) + elif k == "0": k = 10 + elif ord('a') <= ord(k) <= ord('z'): + k = ord(k)-ord('a')+11 + else: k = None + if k is not None: + state.load_fav(k-1) + wait_fav = False + elif k == "n": + icanon(False) + n = input() + icanon(True) + state.setcolor(int(n)) + elif k == "b": state.toggle(Sgr.BOLD) + elif k == "D": state.toggle(Sgr.DARK) + elif k == "i": state.toggle(Sgr.ITALIC) + elif k == "u": state.toggle(Sgr.UNDERLINE) + elif k == "r": state.toggle(Sgr.REVERSE) + #elif k == "B": state.toggle(5) + elif k == "S": state.toggle(Sgr.STRIKE) + elif k == "F": state.toggle_fav() + elif k == "f": wait_fav = True + elif state["mode"] == "rgb": + if k == "g": state["mode"] = "sys" + elif k == "G": state["mode"] = "gray" + elif k in "7q": state.decr("red") + elif k in "8w": state.reset("red") + elif k in "9e": state.incr("red") + elif k in "4a": state.decr("green") + elif k in "5s": state.reset("green") + elif k in "6d": state.incr("green") + elif k in "1z": state.decr("blue") + elif k in "2x": state.reset("blue") + elif k in "3c": state.incr("blue") + elif k in "-": state.decr("red", "green", "blue") + elif k in "0": state.reset("red", "green", "blue", "flags") + elif k in "+": state.incr("red", "green", "blue") + elif k in "/": state.decr_carry("blue", "green", "red") + elif k in "*": state.incr_carry("blue", "green", "red") + elif state["mode"] == "sys": + if k == "g": state["mode"] = "iso" + elif k == "G": state["mode"] = "rgb" + elif k in "741qaz-": state.decr("color") + elif k in "852wsx0": state.reset("color", "flags") + elif k in "963edc+": state.incr("color") + elif state["mode"] == "iso": + if k == "g": state["mode"] = "gray" + elif k == "G": state["mode"] = "sys" + elif k in "741qaz-": state.decr("color") + elif k in "852wsx0": state.reset("color") + elif k in "963edc+": state.incr("color") + elif state["mode"] == "gray": + if k == "g": state["mode"] = "rgb" + elif k == "G": state["mode"] = "iso" + elif k in "741qaz-": state.decr("gray") + elif k in "852wsx0": state.reset("gray", "flags") + elif k in "963edc+": state.incr("gray") icanon(False) sys.stdout.write("\033[?25h") # show cursor diff --git a/win32/df.py b/win32/df.py index 77e229837..17ed66395 100644 --- a/win32/df.py +++ b/win32/df.py @@ -1,136 +1,136 @@ -#!/usr/bin/env python -# A df-like utility for Windows +#!python +# a df-like utility for Windows import os, sys -import win32api as Api -import win32file as File -import win32net as Net +import win32api as Api +import win32file as File +import win32net as Net try: - from win32con import * + from win32con import * except ImportError: - DRIVE_UNKNOWN = 0 - DRIVE_NO_ROOT_DIR = 1 - DRIVE_REMOVABLE = 2 - DRIVE_FIXED = 3 - DRIVE_REMOTE = 4 - DRIVE_CDROM = 5 - DRIVE_RAMDISK = 6 - MAX_PATH = 260 - SEM_FAILCRITICALERRORS = 1 + DRIVE_UNKNOWN = 0 + DRIVE_NO_ROOT_DIR = 1 + DRIVE_REMOVABLE = 2 + DRIVE_FIXED = 3 + DRIVE_REMOTE = 4 + DRIVE_CDROM = 5 + DRIVE_RAMDISK = 6 + MAX_PATH = 260 + SEM_FAILCRITICALERRORS = 1 from ctypes import * kernel32 = windll.kernel32 kernel32.SetErrorMode(SEM_FAILCRITICALERRORS) drivetypes = { - DRIVE_UNKNOWN: "unknown", - DRIVE_NO_ROOT_DIR: "not a volume", - DRIVE_REMOVABLE: "removable", - DRIVE_FIXED: "fixed", - DRIVE_REMOTE: "network", - DRIVE_CDROM: "CD", - DRIVE_RAMDISK: "RAM disk", - - -1: "mapped", - -2: "no media", + DRIVE_UNKNOWN: "unknown", + DRIVE_NO_ROOT_DIR: "not a volume", + DRIVE_REMOVABLE: "removable", + DRIVE_FIXED: "fixed", + DRIVE_REMOTE: "network", + DRIVE_CDROM: "CD", + DRIVE_RAMDISK: "RAM disk", + + -1: "mapped", + -2: "no media", } def prettySize(bytes): - if bytes is None: return "-" - size = float(bytes) - l = -1 - while size >= 1000: - size /= 1024. - l += 1 - return "%.2f %sB" % (size, "kMGTPEY"[l] if l >= 0 else "") + if bytes is None: return "-" + size = float(bytes) + l = -1 + while size >= 1000: + size /= 1024. + l += 1 + return "%.2f %sB" % (size, "kMGTPEY"[l] if l >= 0 else "") def EnumVolumes(): - buf = create_unicode_buffer(256) - volumes = [] + buf = create_unicode_buffer(256) + volumes = [] - h = kernel32.FindFirstVolumeW(buf, sizeof(buf)) - if h: - yield buf.value - while kernel32.FindNextVolumeW(h, buf, sizeof(buf)): - yield buf.value - kernel32.FindVolumeClose(h) + h = kernel32.FindFirstVolumeW(buf, sizeof(buf)) + if h: + yield buf.value + while kernel32.FindNextVolumeW(h, buf, sizeof(buf)): + yield buf.value + kernel32.FindVolumeClose(h) def wszarray_to_list(array): - output = [] - offset = 0 - while offset < sizeof(array): - sz = wstring_at(addressof(array) + offset*2) - if sz: - output.append(sz) - offset += len(sz)+1 - else: - break - return output + output = [] + offset = 0 + while offset < sizeof(array): + sz = wstring_at(addressof(array) + offset*2) + if sz: + output.append(sz) + offset += len(sz)+1 + else: + break + return output def GetPathNamesForVolume(volume): - buf = create_unicode_buffer(4096) - length = c_int32() - if kernel32.GetVolumePathNamesForVolumeNameW(c_wchar_p(volume), buf, sizeof(buf), byref(length)): - return wszarray_to_list(buf) - else: - raise OSError + buf = create_unicode_buffer(4096) + length = c_int32() + if kernel32.GetVolumePathNamesForVolumeNameW(c_wchar_p(volume), buf, sizeof(buf), byref(length)): + return wszarray_to_list(buf) + else: + raise OSError def QueryDosDevice(dev): - dev = dev[:dev.index(":")+1] - target = File.QueryDosDevice(dev) - return target.split("\0")[0] + dev = dev[:dev.index(":")+1] + target = File.QueryDosDevice(dev) + return target.split("\0")[0] def GetMountVolume(path): - volume_name = create_unicode_buffer(64) - res = kernel32.GetVolumeNameForVolumeMountPointW( - c_wchar_p(path), volume_name, sizeof(volume_name)) - if res: - return volume_name.value + volume_name = create_unicode_buffer(64) + res = kernel32.GetVolumeNameForVolumeMountPointW( + c_wchar_p(path), volume_name, sizeof(volume_name)) + if res: + return volume_name.value def GetCanonicalName(disk): - target = QueryDosDevice(disk) - if target is None: - print("QueryDosDevice(%r) is %r" % (letter, target)) - return None - elif target.startswith("\\??\\"): - # `subst`-mapped disk - if target.startswith("\\??\\"): - target = target[len("\\??\\"):] - return target, False - elif target.startswith("\\Device\\LanmanRedirector\\"): - # network disk - return Net.NetUseGetInfo(None, disk[0]+":")["remote"], True - elif target.startswith("UNC\\"): - # `subst`-mapped network disk - return target[4:], True - else: - return GetMountVolume(letter), False + target = QueryDosDevice(disk) + if target is None: + print("QueryDosDevice(%r) is %r" % (letter, target)) + return None + elif target.startswith("\\??\\"): + # `subst`-mapped disk + if target.startswith("\\??\\"): + target = target[len("\\??\\"):] + return target, False + elif target.startswith("\\Device\\LanmanRedirector\\"): + # network disk + return Net.NetUseGetInfo(None, disk[0]+":")["remote"], True + elif target.startswith("UNC\\"): + # `subst`-mapped network disk + return target[4:], True + else: + return GetMountVolume(letter), False def GetDriveType(root): - return kernel32.GetDriveTypeW(c_wchar_p(root)) + return kernel32.GetDriveTypeW(c_wchar_p(root)) def GetVolumeInformation(root): - volume_name = create_unicode_buffer(MAX_PATH+1) - serial_number = c_int32() - max_component_length = c_int32() - flags = c_int32() - fs_name = create_unicode_buffer(MAX_PATH+1) - if kernel32.GetVolumeInformationW(c_wchar_p(root), volume_name, - sizeof(volume_name), byref(serial_number), byref(max_component_length), - byref(flags), fs_name, sizeof(fs_name)): - return (volume_name.value, serial_number.value, byref(max_component_length), - flags.value, fs_name.value) - else: raise OSError + volume_name = create_unicode_buffer(MAX_PATH+1) + serial_number = c_int32() + max_component_length = c_int32() + flags = c_int32() + fs_name = create_unicode_buffer(MAX_PATH+1) + if kernel32.GetVolumeInformationW(c_wchar_p(root), volume_name, + sizeof(volume_name), byref(serial_number), byref(max_component_length), + byref(flags), fs_name, sizeof(fs_name)): + return (volume_name.value, serial_number.value, byref(max_component_length), + flags.value, fs_name.value) + else: raise OSError def IsVolumeReady(root): - try: GetVolumeInformation(root) - except: return False - else: return True + try: GetVolumeInformation(root) + except: return False + else: return True def GetLogicalDriveStrings(): - return Api.GetLogicalDriveStrings().split("\0")[:-1] + return Api.GetLogicalDriveStrings().split("\0")[:-1] LINE_FORMAT = "%-5s %-16s %-17s %10s %10s %5s" header = LINE_FORMAT % ("path", "label", "type", "size", "free", "used") @@ -146,119 +146,117 @@ def GetLogicalDriveStrings(): Printed = [] -Volumes = {guid: { - "pathnames": GetPathNamesForVolume(guid), - "ready": IsVolumeReady(guid), - } for guid in EnumVolumes()} +Volumes = {guid: {"pathnames": GetPathNamesForVolume(guid), + "ready": IsVolumeReady(guid)} for guid in EnumVolumes()} DosDevices = {} for letter in Letters: - target = DosDevices[letter] = QueryDosDevice(letter) - if target is None: - print("QueryDosDevice(%r) is %r" % (letter, target)) - elif target.startswith("\\??\\"): - # `subst`-mapped disk - #target = target[:target.index("\0")] - if target.startswith("\\??\\"): - target = target[len("\\??\\"):] - Maps[letter] = target - elif target.startswith("\\Device\\LanmanRedirector\\"): - # network disk - Maps[letter] = GetCanonicalName(letter) - Drives[letter] = Maps[letter] - pass - elif target.startswith("UNC\\"): - # `subst`-mapped network disk - #Maps[letter] = - pass - else: - Drives[letter] = GetMountVolume(letter) + target = DosDevices[letter] = QueryDosDevice(letter) + if target is None: + print("QueryDosDevice(%r) is %r" % (letter, target)) + elif target.startswith("\\??\\"): + # `subst`-mapped disk + #target = target[:target.index("\0")] + if target.startswith("\\??\\"): + target = target[len("\\??\\"):] + Maps[letter] = target + elif target.startswith("\\Device\\LanmanRedirector\\"): + # network disk + Maps[letter] = GetCanonicalName(letter) + Drives[letter] = Maps[letter] + pass + elif target.startswith("UNC\\"): + # `subst`-mapped network disk + #Maps[letter] = + pass + else: + Drives[letter] = GetMountVolume(letter) for letter in Letters: - isMapped = letter in Maps - - if isMapped: - target = Maps[letter] - type = -1 - free, total, diskfree = Api.GetDiskFreeSpaceEx(letter) - used = 100 - (100*diskfree/total) - - else: - root = Drives[letter] - - if root in Printed: - continue - Printed.append(root) - - pathnames = Volumes[root]["pathnames"][:] - isReady = Volumes[root]["ready"] - - if isReady: - type = GetDriveType(root) - info = GetVolumeInformation(root) - label, filesystem = info[0], info[4] - else: - type, label, filesystem = -2, "", None - - if isReady and type != DRIVE_REMOTE: - free, total, diskfree = Api.GetDiskFreeSpaceEx(root) - used = 100 - (100*diskfree/total) - else: - free, total, diskfree, used = None, None, None, None - - if filesystem: - strtype = "%s (%s)" % (drivetypes[type], filesystem) - else: - strtype = drivetypes[type] - - print(LINE_FORMAT % ( - letter, - label or "(unnamed)", - strtype, - prettySize(total), - prettySize(diskfree), - "%d%%" % used if used is not None else "-", - )) - - if isMapped: - print("%-5s ==> %s" % ("", target)) - else: - pathnames.remove(letter) - for path in pathnames: - print("%-5s <-- %s" % ("", path)) + isMapped = letter in Maps + + if isMapped: + target = Maps[letter] + type = -1 + free, total, diskfree = Api.GetDiskFreeSpaceEx(letter) + used = 100 - (100*diskfree/total) + + else: + root = Drives[letter] + + if root in Printed: + continue + Printed.append(root) + + pathnames = Volumes[root]["pathnames"][:] + isReady = Volumes[root]["ready"] + + if isReady: + type = GetDriveType(root) + info = GetVolumeInformation(root) + label, filesystem = info[0], info[4] + else: + type, label, filesystem = -2, "", None + + if isReady and type != DRIVE_REMOTE: + free, total, diskfree = Api.GetDiskFreeSpaceEx(root) + used = 100 - (100*diskfree/total) + else: + free, total, diskfree, used = None, None, None, None + + if filesystem: + strtype = "%s (%s)" % (drivetypes[type], filesystem) + else: + strtype = drivetypes[type] + + print(LINE_FORMAT % ( + letter, + label or "(unnamed)", + strtype, + prettySize(total), + prettySize(diskfree), + "%d%%" % used if used is not None else "-", + )) + + if isMapped: + print("%-5s ==> %s" % ("", target)) + else: + pathnames.remove(letter) + for path in pathnames: + print("%-5s <-- %s" % ("", path)) for root in Volumes.keys(): - if root in Printed: - continue - - pathnames = Volumes[root]["pathnames"][:] - isReady = Volumes[root]["ready"] - - if isReady: - type = GetDriveType(root) - info = GetVolumeInformation(root) - label, filesystem = info[0], info[4] - else: - type, label, filesystem = -2, "", None - - if isReady and type != DRIVE_REMOTE: - free, total, diskfree = Api.GetDiskFreeSpaceEx(root) - used = 100 - (100*diskfree/total) - else: - free, total, diskfree, used = None, None, None, None - - if filesystem: - strtype = "%s (%s)" % (drivetypes[type], filesystem) - else: - strtype = drivetypes[type] - - print(LINE_FORMAT % ( - "*", - label or "(unnamed)", - strtype, - prettySize(total), - prettySize(diskfree), - "%d%%" % used if used is not None else "-", - )) - for path in pathnames: - print("%-5s <-- %s" % ("", path)) + if root in Printed: + continue + + pathnames = Volumes[root]["pathnames"][:] + isReady = Volumes[root]["ready"] + + if isReady: + type = GetDriveType(root) + info = GetVolumeInformation(root) + label, filesystem = info[0], info[4] + else: + type, label, filesystem = -2, "", None + + if isReady and type != DRIVE_REMOTE: + free, total, diskfree = Api.GetDiskFreeSpaceEx(root) + used = 100 - (100*diskfree/total) + else: + free, total, diskfree, used = None, None, None, None + + if filesystem: + strtype = "%s (%s)" % (drivetypes[type], filesystem) + else: + strtype = drivetypes[type] + + print(LINE_FORMAT % ( + "*", + label or "(unnamed)", + strtype, + prettySize(total), + prettySize(diskfree), + "%d%%" % used if used is not None else "-", + )) + for path in pathnames: + print("%-5s <-- %s" % ("", path)) diff --git a/win32/fingerd.py b/win32/fingerd.py index 421497323..b87fe4416 100644 --- a/win32/fingerd.py +++ b/win32/fingerd.py @@ -1,77 +1,79 @@ -#!/usr/bin/env python2 +#!python +# a Finger daemon for Windows NT + import socket as so import win32api as api #import win32con as con import win32ts as ts def reListSessions(outFh): - protocols = { - ts.WTS_PROTOCOL_TYPE_CONSOLE: "console", - ts.WTS_PROTOCOL_TYPE_ICA: "citrix", - ts.WTS_PROTOCOL_TYPE_RDP: "rdp", - } - - #hostname = api.GetComputerName() - hserver = ts.WTS_CURRENT_SERVER_HANDLE - - currentSessId = ts.WTSGetActiveConsoleSessionId() - - format = "%(user)-16s %(active)1s%(session)-7s %(id)-7s %(protocol)-8s" - print >> outFh, format % dict( - user = "USER", - active = "", - session = "SESSION", - id = "ID", - protocol = "PROTOCOL", - ) - - for session in ts.WTSEnumerateSessions(hserver): - sessionId = session["SessionId"] - session["User"] = ts.WTSQuerySessionInformation(hserver, sessionId, ts.WTSUserName) - #session["Address"] = ts.WTSQuerySessionInformation(hserver, sessionId, ts.WTSClientAddress) - session["Protocol"] = ts.WTSQuerySessionInformation(hserver, sessionId, ts.WTSClientProtocolType) - print >> outFh, format % dict( - user = session["User"] or "(none)", - session = session["WinStationName"], - id = "(%d)" % session["SessionId"], - active = "*" if sessionId == currentSessId else "", - protocol = protocols[session["Protocol"]], - ) + protocols = { + ts.WTS_PROTOCOL_TYPE_CONSOLE: "console", + ts.WTS_PROTOCOL_TYPE_ICA: "citrix", + ts.WTS_PROTOCOL_TYPE_RDP: "rdp", + } + + #hostname = api.GetComputerName() + hserver = ts.WTS_CURRENT_SERVER_HANDLE + + currentSessId = ts.WTSGetActiveConsoleSessionId() + + format = "%(user)-16s %(active)1s%(session)-7s %(id)-7s %(protocol)-8s" + print >> outFh, format % dict( + user = "USER", + active = "", + session = "SESSION", + id = "ID", + protocol = "PROTOCOL", + ) + + for session in ts.WTSEnumerateSessions(hserver): + sessionId = session["SessionId"] + session["User"] = ts.WTSQuerySessionInformation(hserver, sessionId, ts.WTSUserName) + #session["Address"] = ts.WTSQuerySessionInformation(hserver, sessionId, ts.WTSClientAddress) + session["Protocol"] = ts.WTSQuerySessionInformation(hserver, sessionId, ts.WTSClientProtocolType) + print >> outFh, format % dict( + user = session["User"] or "(none)", + session = session["WinStationName"], + id = "(%d)" % session["SessionId"], + active = "*" if sessionId == currentSessId else "", + protocol = protocols[session["Protocol"]], + ) import sys reListSessions(sys.stdout) sys.exit() def reSingleUser(query): - pass + pass def handler(peerSh, addr): - peerFh = peerSh.makefile() - - query = peerFh.readline().strip("\r\n") - detailed = False - if query[:3] == "/W ": - query = query[3:] - detailed = True - - reListSessions(peerFh) - - peerFh.close() - peerSh.close() + peerFh = peerSh.makefile() + + query = peerFh.readline().strip("\r\n") + detailed = False + if query[:3] == "/W ": + query = query[3:] + detailed = True + + reListSessions(peerFh) + + peerFh.close() + peerSh.close() opt_force_inet4 = True port = so.getservbyname("finger") or 79 if so.has_ipv6 and not opt_force_inet4: - family, interface = so.AF_INET6, "::" + family, interface = so.AF_INET6, "::" else: - family, interface = so.AF_INET, "0.0.0.0" + family, interface = so.AF_INET, "0.0.0.0" serverSh = so.socket(family, so.SOCK_STREAM) serverSh.bind((interface, port)) serverSh.listen(1) print "Listening on %(interface)s/%(port)d" % locals() while True: - (peerSh, peerAddr) = serverSh.accept() - print "Connected from %s:%d" % peerAddr - handler(peerSh, peerAddr) + (peerSh, peerAddr) = serverSh.accept() + print "Connected from %s:%d" % peerAddr + handler(peerSh, peerAddr) serverSh.close() diff --git a/win32/fw.py b/win32/fw.py index ba1570ca4..093436dec 100644 --- a/win32/fw.py +++ b/win32/fw.py @@ -1,5 +1,6 @@ -# -*- mode: python -*- -# Simple command line interface to Windows XP Firewall. +#!python +# simple command line interface to Windows XP Firewall + from __future__ import print_function import cmd import sys @@ -8,85 +9,85 @@ from subprocess import list2cmdline def usage(): - print("Usage:") - print("\tfw [\\\\machine] ls") - print("\tfw [\\\\machine] enable|disable / ...") - print("\tfw [\\\\machine] add / []") - print("\tfw [\\\\machine] del /") + print("Usage:") + print("\tfw [\\\\machine] ls") + print("\tfw [\\\\machine] enable|disable / ...") + print("\tfw [\\\\machine] add / []") + print("\tfw [\\\\machine] del /") def parse_portspec(val): - a, b = val.lower().split("/") - try: - a = int(a) - except ValueError: - try: - b = int(b) - except ValueError: - raise ValueError("Port must be an integer") - else: - port, proto = b, a - else: - port, proto = a, b - - if not 1 < port < 65535: - raise ValueError("Port must be in range 1-65535") - if proto not in ("tcp", "udp"): - raise ValueError("Protocol must be TCP or UDP") - - return port, proto + a, b = val.lower().split("/") + try: + a = int(a) + except ValueError: + try: + b = int(b) + except ValueError: + raise ValueError("Port must be an integer") + else: + port, proto = b, a + else: + port, proto = a, b + + if not 1 < port < 65535: + raise ValueError("Port must be in range 1-65535") + if proto not in ("tcp", "udp"): + raise ValueError("Protocol must be TCP or UDP") + + return port, proto class Interactive(cmd.Cmd): - def __init__(self, machine): - cmd.Cmd.__init__(self) - self.prompt = "fw> " - self.fw = Firewall(machine) - - def emptyline(self): - pass - - def default(self, line): - print("Unknown command %r" % line, file=sys.stderr) - - def do_EOF(self, arg): - return True - - def do_ls(self, arg): - entries = list(self.fw.ports.values()) - entries.sort(key=lambda e: e[self.fw.ports.POS_PORTSPEC][0]) - entries.sort(key=lambda e: e[self.fw.ports.POS_PORTSPEC][1]) - for (port, proto), scope, enabled, name in entries: - name = load_string_resource(name) - print(" %1s %-4s %5d %s" % ("*" if enabled else "", proto, port, name)) - - def do_enable(self, arg): - specs = [parse_portspec(a) for a in arg.split()] - for portspec in specs: - self.fw.ports.set_rule_status(portspec, True) - - def do_disable(self, arg): - specs = [parse_portspec(a) for a in arg.split()] - for portspec in specs: - self.fw.ports.set_rule_status(portspec, False) - - def do_rename(self, arg): - spec, newname = arg.split(None, 1) - spec = parse_portspec(spec) - oldname = self.fw.ports[spec] - if oldname.startswith("@"): - print("Warning: Renaming built-in rule", oldname) - self.fw.ports[spec] + def __init__(self, machine): + cmd.Cmd.__init__(self) + self.prompt = "fw> " + self.fw = Firewall(machine) + + def emptyline(self): + pass + + def default(self, line): + print("Unknown command %r" % line, file=sys.stderr) + + def do_EOF(self, arg): + return True + + def do_ls(self, arg): + entries = list(self.fw.ports.values()) + entries.sort(key=lambda e: e[self.fw.ports.POS_PORTSPEC][0]) + entries.sort(key=lambda e: e[self.fw.ports.POS_PORTSPEC][1]) + for (port, proto), scope, enabled, name in entries: + name = load_string_resource(name) + print(" %1s %-4s %5d %s" % ("*" if enabled else "", proto, port, name)) + + def do_enable(self, arg): + specs = [parse_portspec(a) for a in arg.split()] + for portspec in specs: + self.fw.ports.set_rule_status(portspec, True) + + def do_disable(self, arg): + specs = [parse_portspec(a) for a in arg.split()] + for portspec in specs: + self.fw.ports.set_rule_status(portspec, False) + + def do_rename(self, arg): + spec, newname = arg.split(None, 1) + spec = parse_portspec(spec) + oldname = self.fw.ports[spec] + if oldname.startswith("@"): + print("Warning: Renaming built-in rule", oldname) + self.fw.ports[spec] try: - if sys.argv[1].startswith("\\\\"): - machine = sys.argv.pop(1) - else: - machine = None + if sys.argv[1].startswith("\\\\"): + machine = sys.argv.pop(1) + else: + machine = None except IndexError: - machine = None + machine = None interp = Interactive(machine) if len(sys.argv) > 1: - interp.onecmd(list2cmdline(sys.argv[1:])) + interp.onecmd(list2cmdline(sys.argv[1:])) else: - interp.cmdloop() + interp.cmdloop() diff --git a/win32/identd/win32-identd.py b/win32/identd/win32-identd.py index 218aea854..48996a5ad 100644 --- a/win32/identd/win32-identd.py +++ b/win32/identd/win32-identd.py @@ -10,399 +10,399 @@ import struct try: - import servicemanager - import win32api - import win32con - import win32security - import win32service - import win32serviceutil + import servicemanager + import win32api + import win32con + import win32security + import win32service + import win32serviceutil except ImportError: - print("Error: This program requires pywin32.", file=sys.stderr) - raise + print("Error: This program requires pywin32.", file=sys.stderr) + raise -NULL = None -UCHAR = ctypes.c_ubyte +NULL = None +UCHAR = ctypes.c_ubyte -ANY_SIZE = 1 +ANY_SIZE = 1 -NO_ERROR = 0 +NO_ERROR = 0 -TCP_CONNECTION_OFFLOAD_STATE = DWORD +TCP_CONNECTION_OFFLOAD_STATE = DWORD -TCP_TABLE_BASIC_LISTENER = 0 -TCP_TABLE_BASIC_CONNECTIONS = 1 -TCP_TABLE_BASIC_ALL = 2 -TCP_TABLE_OWNER_PID_LISTENER = 3 -TCP_TABLE_OWNER_PID_CONNECTIONS = 4 -TCP_TABLE_OWNER_PID_ALL = 5 -TCP_TABLE_OWNER_MODULE_LISTENER = 6 -TCP_TABLE_OWNER_MODULE_CONNECTIONS = 7 -TCP_TABLE_OWNER_MODULE_ALL = 8 +TCP_TABLE_BASIC_LISTENER = 0 +TCP_TABLE_BASIC_CONNECTIONS = 1 +TCP_TABLE_BASIC_ALL = 2 +TCP_TABLE_OWNER_PID_LISTENER = 3 +TCP_TABLE_OWNER_PID_CONNECTIONS = 4 +TCP_TABLE_OWNER_PID_ALL = 5 +TCP_TABLE_OWNER_MODULE_LISTENER = 6 +TCP_TABLE_OWNER_MODULE_CONNECTIONS = 7 +TCP_TABLE_OWNER_MODULE_ALL = 8 class MIB_TCPROW_OWNER_PID(ctypes.Structure): - _fields_ = [ - ("dwState", DWORD), - ("dwLocalAddr", DWORD), - ("dwLocalPort", DWORD), - ("dwRemoteAddr", DWORD), - ("dwRemotePort", DWORD), - ("dwOwningPid", DWORD), - ] + _fields_ = [ + ("dwState", DWORD), + ("dwLocalAddr", DWORD), + ("dwLocalPort", DWORD), + ("dwRemoteAddr", DWORD), + ("dwRemotePort", DWORD), + ("dwOwningPid", DWORD), + ] class MIB_TCPTABLE_OWNER_PID(ctypes.Structure): - _fields_ = [ - ("dwNumEntries", DWORD), - ("table", MIB_TCPROW_OWNER_PID * ANY_SIZE), - ] + _fields_ = [ + ("dwNumEntries", DWORD), + ("table", MIB_TCPROW_OWNER_PID * ANY_SIZE), + ] class MIB_TCP6ROW_OWNER_PID(ctypes.Structure): - _fields_ = [ - ("dwLocalAddr", UCHAR * 16), - ("dwLocalScopeId", DWORD), - ("dwLocalPort", DWORD), - ("dwRemoteAddr", UCHAR * 16), - ("dwRemoteScopeId", DWORD), - ("dwRemotePort", DWORD), - ("dwState", DWORD), - ("dwOwningPid", DWORD), - ] + _fields_ = [ + ("dwLocalAddr", UCHAR * 16), + ("dwLocalScopeId", DWORD), + ("dwLocalPort", DWORD), + ("dwRemoteAddr", UCHAR * 16), + ("dwRemoteScopeId", DWORD), + ("dwRemotePort", DWORD), + ("dwState", DWORD), + ("dwOwningPid", DWORD), + ] class MIB_TCP6TABLE_OWNER_PID(ctypes.Structure): - _fields_ = [ - ("dwNumEntries", DWORD), - ("table", MIB_TCP6ROW_OWNER_PID * ANY_SIZE), - ] + _fields_ = [ + ("dwNumEntries", DWORD), + ("table", MIB_TCP6ROW_OWNER_PID * ANY_SIZE), + ] def get_tcp_table(af): - _GetExtendedTcpTable = ctypes.windll.iphlpapi.GetExtendedTcpTable - tableClass = TCP_TABLE_OWNER_PID_CONNECTIONS - if af == socket.AF_INET: - _row = MIB_TCPROW_OWNER_PID - elif af == socket.AF_INET6: - _row = MIB_TCP6ROW_OWNER_PID - - ANY_SIZE = 65535 - class _table(ctypes.Structure): - _fields_ = [ - ("dwNumEntries", DWORD), - ("table", _row * ANY_SIZE), - ] - - dwSize = DWORD(0) - _GetExtendedTcpTable("", byref(dwSize), False, af, tableClass, 0) - #print "Expecting %d bytes" % dwSize.value - table = _table() - if _GetExtendedTcpTable(byref(table), byref(dwSize), False, af, tableClass, 0) == NO_ERROR: - for i in range(table.dwNumEntries): - entry = table.table[i] - - if af == socket.AF_INET: - local = (entry.dwLocalAddr, entry.dwLocalPort) - remote = (entry.dwRemoteAddr, entry.dwRemotePort) - elif af == socket.AF_INET6: - local = (entry.dwLocalAddr, entry.dwLocalPort, 0, entry.dwLocalScopeId) - remote = (entry.dwRemoteAddr, entry.dwRemotePort, 0, - entry.dwRemoteScopeId) - - yield { - "local": unpack_addr(af, local), - "remote": unpack_addr(af, remote), - "pid": entry.dwOwningPid, - } + _GetExtendedTcpTable = ctypes.windll.iphlpapi.GetExtendedTcpTable + tableClass = TCP_TABLE_OWNER_PID_CONNECTIONS + if af == socket.AF_INET: + _row = MIB_TCPROW_OWNER_PID + elif af == socket.AF_INET6: + _row = MIB_TCP6ROW_OWNER_PID + + ANY_SIZE = 65535 + class _table(ctypes.Structure): + _fields_ = [ + ("dwNumEntries", DWORD), + ("table", _row * ANY_SIZE), + ] + + dwSize = DWORD(0) + _GetExtendedTcpTable("", byref(dwSize), False, af, tableClass, 0) + #print "Expecting %d bytes" % dwSize.value + table = _table() + if _GetExtendedTcpTable(byref(table), byref(dwSize), False, af, tableClass, 0) == NO_ERROR: + for i in range(table.dwNumEntries): + entry = table.table[i] + + if af == socket.AF_INET: + local = (entry.dwLocalAddr, entry.dwLocalPort) + remote = (entry.dwRemoteAddr, entry.dwRemotePort) + elif af == socket.AF_INET6: + local = (entry.dwLocalAddr, entry.dwLocalPort, 0, entry.dwLocalScopeId) + remote = (entry.dwRemoteAddr, entry.dwRemotePort, 0, + entry.dwRemoteScopeId) + + yield { + "local": unpack_addr(af, local), + "remote": unpack_addr(af, remote), + "pid": entry.dwOwningPid, + } def get_connection_pid(af, local_addr, local_port, remote_addr, remote_port): - for entry in get_tcp_table(af): - if (entry["local"][0] == local_addr - and entry["local"][1] == local_port - and entry["remote"][0] == remote_addr - and entry["remote"][1] == remote_port): - return entry["pid"] - return None + for entry in get_tcp_table(af): + if (entry["local"][0] == local_addr + and entry["local"][1] == local_port + and entry["remote"][0] == remote_addr + and entry["remote"][1] == remote_port): + return entry["pid"] + return None def unpack_addr(af, psockaddr): - if af == socket.AF_INET: - addr, port = psockaddr - addr = socket.inet_ntoa(struct.pack("!L", socket.ntohl(addr))) - port = socket.ntohs(port) - return addr, port - elif af == socket.AF_INET6: - if len(psockaddr) == 2: - addr, port = psockaddr - flow, scope = None, None - elif len(psockaddr) == 4: - addr, port, flow, scope = psockaddr - addr = ":".join("%04x" % x for x in struct.unpack("!8H", addr)) - port = socket.ntohs(port) - return addr, port, flow, scope + if af == socket.AF_INET: + addr, port = psockaddr + addr = socket.inet_ntoa(struct.pack("!L", socket.ntohl(addr))) + port = socket.ntohs(port) + return addr, port + elif af == socket.AF_INET6: + if len(psockaddr) == 2: + addr, port = psockaddr + flow, scope = None, None + elif len(psockaddr) == 4: + addr, port, flow, scope = psockaddr + addr = ":".join("%04x" % x for x in struct.unpack("!8H", addr)) + port = socket.ntohs(port) + return addr, port, flow, scope def expand_v6_addr(addr): - if "::" in addr: - left, right = addr.split("::", 1) - left = left.split(":") - right = right.split(":") - rest = ['0'] * (8 - len(left) - len(right)) - addr = left+rest+right - else: - addr = addr.split(":") - return ":".join("%04x" % (int(c, 16) if c != '' else 0) for c in addr) + if "::" in addr: + left, right = addr.split("::", 1) + left = left.split(":") + right = right.split(":") + rest = ['0'] * (8 - len(left) - len(right)) + addr = left+rest+right + else: + addr = addr.split(":") + return ":".join("%04x" % (int(c, 16) if c != '' else 0) for c in addr) def format_addr(host, port, *rest): - return ("[%s]:%s" if ":" in host else "%s:%s") % (host, port) + return ("[%s]:%s" if ":" in host else "%s:%s") % (host, port) class Identd(): - def __init__(self, service=None): - try: - self.port = socket.getservbyname("auth") - except socket.error: - self.port = 113 - - self.os_name = "WIN32" - # Connections waiting for acception, per interface - self.listen_backlog = 3 - - self.listeners = [] - self.clients = [] - self.buffers = {} - self.peers = {} - self.requests = {} - self._service = service - - def start(self): - """Listen on all IPv4 and IPv6 interfaces and start accepting connections.""" - - self.listen(socket.AF_INET, "0.0.0.0") - if socket.has_ipv6: - self.listen(socket.AF_INET6, "::") - self.accept() - - def log(self, level, msg, *args): - if self._service: - self._service.log(level, msg % args) - else: - print(msg % args) - - def logEx(self, level, msg, *data): - msg += "\n\n" + "\n".join("%s:\t%s" % item if item else "" for item in data) - return self.log(level, msg) - - def listen(self, af, addr): - """Listen on a given address.""" - - fd = socket.socket(af, socket.SOCK_STREAM) - self.log("info", "Listening on %s", format_addr(addr, self.port)) - fd.bind((addr, self.port)) - fd.listen(self.listen_backlog) - self.listeners.append(fd) - - def accept(self): - """Wait for incoming data or connections.""" - while True: - r, w, x = self.listeners + self.clients, [], [] - r, w, x = select.select(r, w, x) - for fd in r: - if fd in self.listeners: - self.handle_connection(fd) - elif fd in self.clients: - try: - self.handle_in_data(fd) - except Exception as e: - self.log("error", "Error in handle_in_data(): %s: %s", - e.__class__.__name__, e) - self.close(fd) - raise - - def handle_connection(self, fd): - """Accept incoming connection. Called by accept() for listener sockets on select()""" - - client, peer = fd.accept() - self.log("info", "Accepting %s (fd=%d)", format_addr(*peer), client.fileno()) - self.clients.append(client) - self.buffers[client] = b"" - - def handle_in_data(self, fd): - """Accept incoming data. Called by accept() for client sockets on select()""" - buf = fd.recv(512) - if not buf: - self.log("notice", "Lost connection from %s", format_addr(*fd.getpeername())) - return self.close(fd) - self.buffers[fd] += buf - if b"\n" in self.buffers[fd]: - try: - self.handle_req(fd) - except BaseException as e: - self.log("error", "Error in handle_req(): %s: %s", e.__class__.__name__, e) - self.reply(fd, "ERROR", "UNKNOWN-ERROR") - raise - else: - # TODO: This assumes that the first packet contains the entire request. - self.log_invalid_request(fd, self.buffers[fd]) - - def handle_req(self, fd): - local_addr = fd.getsockname()[0] - remote_addr = fd.getpeername()[0] - - raw_request = self.buffers[fd].splitlines()[0] - - try: - local_port, remote_port = raw_request.split(b",", 1) - local_port = int(local_port.strip()) - remote_port = int(remote_port.strip()) - except ValueError: - return self.log_invalid_request(fd, self.buffers[fd]) - - self.requests[fd] = (local_addr, local_port), (remote_addr, remote_port) - - if not (0 < local_port < 65536 and 0 < remote_port < 65536): - return self.reply(fd, "ERROR", "INVALID-PORT") - - if fd.family == socket.AF_INET6: - local_addr = expand_v6_addr(local_addr) - remote_addr = expand_v6_addr(remote_addr) - - pid = get_connection_pid(fd.family, - local_addr, local_port, - remote_addr, remote_port) - if pid is None: - return self.reply(fd, "ERROR", "NO-USER") - - owner = self.get_pid_owner(fd, pid) - if not owner: - # insufficient privileges? - return self.reply(fd, "ERROR", "HIDDEN-USER") - - return self.reply_userid(fd, pid, owner) - - def reply(self, fd, code, info): - """Send a reply to an ident request.""" - - try: - local, remote = self.requests[fd] - except KeyError: - local, remote = 0, 0 - - self.logEx("notice", - "Query from %s" % format_addr(*fd.getpeername()), - ("local", format_addr(*local)), - ("remote", format_addr(*remote)), - None, - ("reply", code), - ("data", info),) - - return self.send_reply(fd, local[1], remote[1], code, info) - - def reply_userid(self, fd, pid, owner): - """Send a success reply and log owner information.""" - - try: - local, remote = self.requests[fd] - except KeyError: - local, remote = 0, 0 - - sid, username, domain = owner - - username = username.replace(":", "_").replace("\r", "").replace("\n", " ") - - code = "USERID" - - info = "%s,%s:%s" % (self.os_name, "UTF-8", username) - - self.logEx("notice", - "Successful query from %s." % format_addr(*fd.getpeername()), - ("local", format_addr(*local)), - ("remote", format_addr(*remote)), - None, - ("pid", pid), - ("owner", win32security.ConvertSidToStringSid(sid)), - ("user", username), - ("domain", domain), - None, - ("reply", code), - ("info", info),) - - return self.send_reply(fd, local[1], remote[1], code, info) - - def log_invalid_request(self, fd, raw_req): - """Send a reply to an unparseable Ident request.""" - - self.log("error", "Invalid query from %s\n\n" - "raw data:\t%r\n" - "\t(%d bytes)\n", - format_addr(*fd.getpeername()), raw_req, len(raw_req)) - - req = raw_req.rstrip(b"\r\n") - - fd.send(req + ":ERROR:INVALID-PORT\r\n".encode("utf-8")) - self.close(fd) - - def send_reply(self, fd, lport, rport, code, info): - """Format and send a raw Ident reply with given parameters.""" + def __init__(self, service=None): + try: + self.port = socket.getservbyname("auth") + except socket.error: + self.port = 113 + + self.os_name = "WIN32" + # Connections waiting for acception, per interface + self.listen_backlog = 3 + + self.listeners = [] + self.clients = [] + self.buffers = {} + self.peers = {} + self.requests = {} + self._service = service + + def start(self): + """Listen on all IPv4 and IPv6 interfaces and start accepting connections.""" + + self.listen(socket.AF_INET, "0.0.0.0") + if socket.has_ipv6: + self.listen(socket.AF_INET6, "::") + self.accept() + + def log(self, level, msg, *args): + if self._service: + self._service.log(level, msg % args) + else: + print(msg % args) + + def logEx(self, level, msg, *data): + msg += "\n\n" + "\n".join("%s:\t%s" % item if item else "" for item in data) + return self.log(level, msg) + + def listen(self, af, addr): + """Listen on a given address.""" + + fd = socket.socket(af, socket.SOCK_STREAM) + self.log("info", "Listening on %s", format_addr(addr, self.port)) + fd.bind((addr, self.port)) + fd.listen(self.listen_backlog) + self.listeners.append(fd) + + def accept(self): + """Wait for incoming data or connections.""" + while True: + r, w, x = self.listeners + self.clients, [], [] + r, w, x = select.select(r, w, x) + for fd in r: + if fd in self.listeners: + self.handle_connection(fd) + elif fd in self.clients: + try: + self.handle_in_data(fd) + except Exception as e: + self.log("error", "Error in handle_in_data(): %s: %s", + e.__class__.__name__, e) + self.close(fd) + raise + + def handle_connection(self, fd): + """Accept incoming connection. Called by accept() for listener sockets on select()""" + + client, peer = fd.accept() + self.log("info", "Accepting %s (fd=%d)", format_addr(*peer), client.fileno()) + self.clients.append(client) + self.buffers[client] = b"" + + def handle_in_data(self, fd): + """Accept incoming data. Called by accept() for client sockets on select()""" + buf = fd.recv(512) + if not buf: + self.log("notice", "Lost connection from %s", format_addr(*fd.getpeername())) + return self.close(fd) + self.buffers[fd] += buf + if b"\n" in self.buffers[fd]: + try: + self.handle_req(fd) + except BaseException as e: + self.log("error", "Error in handle_req(): %s: %s", e.__class__.__name__, e) + self.reply(fd, "ERROR", "UNKNOWN-ERROR") + raise + else: + # TODO: This assumes that the first packet contains the entire request. + self.log_invalid_request(fd, self.buffers[fd]) + + def handle_req(self, fd): + local_addr = fd.getsockname()[0] + remote_addr = fd.getpeername()[0] + + raw_request = self.buffers[fd].splitlines()[0] + + try: + local_port, remote_port = raw_request.split(b",", 1) + local_port = int(local_port.strip()) + remote_port = int(remote_port.strip()) + except ValueError: + return self.log_invalid_request(fd, self.buffers[fd]) + + self.requests[fd] = (local_addr, local_port), (remote_addr, remote_port) + + if not (0 < local_port < 65536 and 0 < remote_port < 65536): + return self.reply(fd, "ERROR", "INVALID-PORT") + + if fd.family == socket.AF_INET6: + local_addr = expand_v6_addr(local_addr) + remote_addr = expand_v6_addr(remote_addr) + + pid = get_connection_pid(fd.family, + local_addr, local_port, + remote_addr, remote_port) + if pid is None: + return self.reply(fd, "ERROR", "NO-USER") + + owner = self.get_pid_owner(fd, pid) + if not owner: + # insufficient privileges? + return self.reply(fd, "ERROR", "HIDDEN-USER") + + return self.reply_userid(fd, pid, owner) + + def reply(self, fd, code, info): + """Send a reply to an ident request.""" + + try: + local, remote = self.requests[fd] + except KeyError: + local, remote = 0, 0 + + self.logEx("notice", + "Query from %s" % format_addr(*fd.getpeername()), + ("local", format_addr(*local)), + ("remote", format_addr(*remote)), + None, + ("reply", code), + ("data", info),) + + return self.send_reply(fd, local[1], remote[1], code, info) + + def reply_userid(self, fd, pid, owner): + """Send a success reply and log owner information.""" + + try: + local, remote = self.requests[fd] + except KeyError: + local, remote = 0, 0 + + sid, username, domain = owner + + username = username.replace(":", "_").replace("\r", "").replace("\n", " ") + + code = "USERID" + + info = "%s,%s:%s" % (self.os_name, "UTF-8", username) + + self.logEx("notice", + "Successful query from %s." % format_addr(*fd.getpeername()), + ("local", format_addr(*local)), + ("remote", format_addr(*remote)), + None, + ("pid", pid), + ("owner", win32security.ConvertSidToStringSid(sid)), + ("user", username), + ("domain", domain), + None, + ("reply", code), + ("info", info),) + + return self.send_reply(fd, local[1], remote[1], code, info) + + def log_invalid_request(self, fd, raw_req): + """Send a reply to an unparseable Ident request.""" + + self.log("error", "Invalid query from %s\n\n" + "raw data:\t%r\n" + "\t(%d bytes)\n", + format_addr(*fd.getpeername()), raw_req, len(raw_req)) + + req = raw_req.rstrip(b"\r\n") + + fd.send(req + ":ERROR:INVALID-PORT\r\n".encode("utf-8")) + self.close(fd) + + def send_reply(self, fd, lport, rport, code, info): + """Format and send a raw Ident reply with given parameters.""" - data = "%d,%d:%s:%s\r\n" % (lport, rport, code, info) - fd.send(data.encode("utf-8")) - self.close(fd) + data = "%d,%d:%s:%s\r\n" % (lport, rport, code, info) + fd.send(data.encode("utf-8")) + self.close(fd) - def close(self, fd): - """Close TCP connection and remove data structures.""" - - self.log("debug", "Closing fd %d", fd.fileno()) - - try: - self.clients.remove(fd) - del self.buffers[fd] - except (KeyError, ValueError) as e: - pass + def close(self, fd): + """Close TCP connection and remove data structures.""" + + self.log("debug", "Closing fd %d", fd.fileno()) + + try: + self.clients.remove(fd) + del self.buffers[fd] + except (KeyError, ValueError) as e: + pass - fd.close() - - # helper functions - - def get_pid_owner(self, fd, pid): - try: - proc = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION, False, pid) - token = win32security.OpenProcessToken(proc, win32con.TOKEN_QUERY) - user_sid, user_attr = win32security.GetTokenInformation(token, - win32security.TokenUser) - user = win32security.LookupAccountSid(None, user_sid) - return user_sid, user[0], user[1] - except win32api.error as e: - self.logEx("error", - "%s failed" % funcname, - ("exception", e), - ("function", e.funcname), - ("error", "[%(winerror)d] %(strerror)s" % e), - None, - ("process", pid),) - raise + fd.close() + + # helper functions + + def get_pid_owner(self, fd, pid): + try: + proc = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION, False, pid) + token = win32security.OpenProcessToken(proc, win32con.TOKEN_QUERY) + user_sid, user_attr = win32security.GetTokenInformation(token, + win32security.TokenUser) + user = win32security.LookupAccountSid(None, user_sid) + return user_sid, user[0], user[1] + except win32api.error as e: + self.logEx("error", + "%s failed" % funcname, + ("exception", e), + ("function", e.funcname), + ("error", "[%(winerror)d] %(strerror)s" % e), + None, + ("process", pid),) + raise class IdentdService(win32serviceutil.ServiceFramework): - _svc_name_ = "identd" - _svc_display_name_ = "Ident service" - _svc_description_ = "Responds to Ident (RFC 1413) requests." - _svc_deps_ = ["Tcpip", "TermService"] - - def __init__(self, args): - win32serviceutil.ServiceFramework.__init__(self, args) - - def SvcDoRun(self): - d = Identd(service=self) - d.start() - - def SvcStop(self): - self.ReportServiceStatus(win32service.SERVICE_STOPPED) - - def log(self, level, msg): - if level == "error": - servicemanager.LogErrorMsg(msg) - elif level == "warn": - servicemanager.LogWarningMsg(msg) - elif level == "notice": - servicemanager.LogInfoMsg(msg) - else: - pass + _svc_name_ = "identd" + _svc_display_name_ = "Ident service" + _svc_description_ = "Responds to Ident (RFC 1413) requests." + _svc_deps_ = ["Tcpip", "TermService"] + + def __init__(self, args): + win32serviceutil.ServiceFramework.__init__(self, args) + + def SvcDoRun(self): + d = Identd(service=self) + d.start() + + def SvcStop(self): + self.ReportServiceStatus(win32service.SERVICE_STOPPED) + + def log(self, level, msg): + if level == "error": + servicemanager.LogErrorMsg(msg) + elif level == "warn": + servicemanager.LogWarningMsg(msg) + elif level == "notice": + servicemanager.LogInfoMsg(msg) + else: + pass if __name__ == "__main__": - if len(sys.argv) > 1: - win32serviceutil.HandleCommandLine(IdentdService) - else: - d = Identd() - d.start() + if len(sys.argv) > 1: + win32serviceutil.HandleCommandLine(IdentdService) + else: + d = Identd() + d.start() diff --git a/win32/pathed.py b/win32/pathed.py index aa1b77dd4..0c653ac18 100644 --- a/win32/pathed.py +++ b/win32/pathed.py @@ -9,19 +9,19 @@ from nullroute.windows.registry import RegistryKey def display_buf(): - for i, item in enumerate(buf): - print("%4d - %s" % (i, item)) + for i, item in enumerate(buf): + print("%4d - %s" % (i, item)) def display_stack(): - for i, item in enumerate(stack): - print("+ %s " % (item)) + for i, item in enumerate(stack): + print("+ %s " % (item)) def display(): - display_buf() - display_stack() + display_buf() + display_stack() def notify_os(): - Api.SendMessage(Con.HWND_BROADCAST, Con.WM_SETTINGCHANGE, None, "Environment") + Api.SendMessage(Con.HWND_BROADCAST, Con.WM_SETTINGCHANGE, None, "Environment") update = False @@ -31,8 +31,7 @@ def notify_os(): upath, _utype = ukey["PATH"] ubuf = upath.split(os.pathsep) -skey = RegistryKey(Con.HKEY_LOCAL_MACHINE, - "System\\CurrentControlSet\\Control\\Session Manager\\Environment") +skey = RegistryKey(Con.HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Control\\Session Manager\\Environment") spath, _stype = skey["PATH"] sbuf = spath.split(os.pathsep) @@ -59,143 +58,143 @@ def notify_os(): """ while True: - try: - tokens = input("pathed %s> " % bname).split(" ") - except EOFError: - update = True - break - except KeyboardInterrupt: - update = False - break - - token = lambda: tokens.pop(0) - rest = lambda: " ".join(tokens) - - try: - cmd = token() - except IndexError: - cmd = None - - if not cmd: - display() - - elif cmd == "q": - update = True - break - - elif cmd == "x": - update = False - break - - elif cmd == "h": - print(Commands) - - elif cmd == "i": - try: - pos = token() - except IndexError: - pos = 0 - else: - pos = int(pos) - - arg = rest() - - buf.insert(pos, arg) - - elif cmd == "a": - try: - pos = token() - except IndexError: - pos = None - else: - pos = int(pos) - - arg = rest() - - if pos is None: - buf.append(arg) - else: - buf.insert(pos+1, arg) - - elif cmd == "d": - pos, = tokens - pos = int(pos) - try: - val = buf.pop(pos) - except IndexError: - print("out of range") - else: - stack.append(val) - display_stack() - - elif cmd == "y": - pos, = tokens - pos = int(pos) - val = buf[pos] - stack.append(val) - display_stack() - - elif cmd == "P": - pos, = tokens - pos = int(pos) - - try: - val = stack.pop() - except IndexError: - print("stack empty") - else: - buf.insert(pos, val) - display_stack() - - elif cmd == "p": - pos, = tokens - pos = int(pos) - - try: - val = stack.pop() - except IndexError: - print("stack empty") - else: - buf.insert(pos+1, val) - display_stack() - - elif cmd == "sw": - a = int(token()) - b = int(token()) - buf[a], buf[b] = buf[b], buf[a] - display_buf() - - elif cmd == "r": - try: - val = stack.pop() - except IndexError: - print("stack empty") - else: - stack.insert(0, val) - display_stack() - - elif cmd == "pop": - try: - val = stack.pop() - except IndexError: - print("stack empty") - else: - display_stack() - - elif cmd == "/": - if buf is ubuf: - buf, bname = sbuf, "sys" - else: - buf, bname = ubuf, "usr" - display_buf() - - else: - print("unknown command") + try: + tokens = input("pathed %s> " % bname).split(" ") + except EOFError: + update = True + break + except KeyboardInterrupt: + update = False + break + + token = lambda: tokens.pop(0) + rest = lambda: " ".join(tokens) + + try: + cmd = token() + except IndexError: + cmd = None + + if not cmd: + display() + + elif cmd == "q": + update = True + break + + elif cmd == "x": + update = False + break + + elif cmd == "h": + print(Commands) + + elif cmd == "i": + try: + pos = token() + except IndexError: + pos = 0 + else: + pos = int(pos) + + arg = rest() + + buf.insert(pos, arg) + + elif cmd == "a": + try: + pos = token() + except IndexError: + pos = None + else: + pos = int(pos) + + arg = rest() + + if pos is None: + buf.append(arg) + else: + buf.insert(pos+1, arg) + + elif cmd == "d": + pos, = tokens + pos = int(pos) + try: + val = buf.pop(pos) + except IndexError: + print("out of range") + else: + stack.append(val) + display_stack() + + elif cmd == "y": + pos, = tokens + pos = int(pos) + val = buf[pos] + stack.append(val) + display_stack() + + elif cmd == "P": + pos, = tokens + pos = int(pos) + + try: + val = stack.pop() + except IndexError: + print("stack empty") + else: + buf.insert(pos, val) + display_stack() + + elif cmd == "p": + pos, = tokens + pos = int(pos) + + try: + val = stack.pop() + except IndexError: + print("stack empty") + else: + buf.insert(pos+1, val) + display_stack() + + elif cmd == "sw": + a = int(token()) + b = int(token()) + buf[a], buf[b] = buf[b], buf[a] + display_buf() + + elif cmd == "r": + try: + val = stack.pop() + except IndexError: + print("stack empty") + else: + stack.insert(0, val) + display_stack() + + elif cmd == "pop": + try: + val = stack.pop() + except IndexError: + print("stack empty") + else: + display_stack() + + elif cmd == "/": + if buf is ubuf: + buf, bname = sbuf, "sys" + else: + buf, bname = ubuf, "usr" + display_buf() + + else: + print("unknown command") if update: - print("Updating") - ukey["PATH"] = os.pathsep.join(ubuf), _utype - skey["PATH"] = os.pathsep.join(sbuf), _stype - notify_os() + print("Updating") + ukey["PATH"] = os.pathsep.join(ubuf), _utype + skey["PATH"] = os.pathsep.join(sbuf), _stype + notify_os() else: - print("Discarding changes") + print("Discarding changes") diff --git a/win32/powermonitor/actions.py.example b/win32/powermonitor/actions.py.example index fc853e09f..f7aaae56f 100644 --- a/win32/powermonitor/actions.py.example +++ b/win32/powermonitor/actions.py.example @@ -1,7 +1,7 @@ #!python def Suspend(): - pass + pass def Resume(): - pass + pass diff --git a/win32/powermonitor/powermonitor.py b/win32/powermonitor/powermonitor.py index 3a0067ff2..84b16347e 100644 --- a/win32/powermonitor/powermonitor.py +++ b/win32/powermonitor/powermonitor.py @@ -11,81 +11,81 @@ import servicemanager as smgr try: - import actions + import actions except ImportError: - print("You need to create an actions.py file first.") - sys.exit(1) + print("You need to create an actions.py file first.") + sys.exit(1) class PowerMonitor(): - def __init__(self, name="Power event monitor", classname="PowerMonitor"): - wc = WNDCLASS() - wc.hInstance = hInst = GetModuleHandle(None) - wc.lpszClassName = classname - wc.lpfnWndProc = self.WndProc - self.classAtom = RegisterClass(wc) - - style = 0 - self.hWnd = CreateWindow(self.classAtom, name, - style, 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, - 0, 0, hInst, None) - UpdateWindow(self.hWnd) - - def start(self): - self.log("info", "Starting main loop") - PumpMessages() - - def stop(self): - PostQuitMessage(0) - - def log(self, type, message): - print("[%s] %s" % (type, message)) - - def WndProc(self, hWnd, message, wParam, lParam): - if message == WM_POWERBROADCAST: - if wParam == PBT_APMSUSPEND: - self.OnSuspend(hWnd, message, wParam, lParam) - elif wParam == PBT_APMRESUMESUSPEND: - self.OnResume(hWnd, message, wParam, lParam) - elif message == WM_CLOSE: - DestroyWindow(hWnd) - elif message == WM_DESTROY: - PostQuitMessage(0) - elif message == WM_QUERYENDSESSION: - return True - - def OnSuspend(self, hWnd, message, wParam, lParam): - self.log("info", "APM suspend event received") - reload(actions).Suspend() - - def OnResume(self, hWnd, message, wParam, lParam): - self.log("info", "APM resume event received") - reload(actions).Resume() + def __init__(self, name="Power event monitor", classname="PowerMonitor"): + wc = WNDCLASS() + wc.hInstance = hInst = GetModuleHandle(None) + wc.lpszClassName = classname + wc.lpfnWndProc = self.WndProc + self.classAtom = RegisterClass(wc) + + style = 0 + self.hWnd = CreateWindow(self.classAtom, name, + style, 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, + 0, 0, hInst, None) + UpdateWindow(self.hWnd) + + def start(self): + self.log("info", "Starting main loop") + PumpMessages() + + def stop(self): + PostQuitMessage(0) + + def log(self, type, message): + print("[%s] %s" % (type, message)) + + def WndProc(self, hWnd, message, wParam, lParam): + if message == WM_POWERBROADCAST: + if wParam == PBT_APMSUSPEND: + self.OnSuspend(hWnd, message, wParam, lParam) + elif wParam == PBT_APMRESUMESUSPEND: + self.OnResume(hWnd, message, wParam, lParam) + elif message == WM_CLOSE: + DestroyWindow(hWnd) + elif message == WM_DESTROY: + PostQuitMessage(0) + elif message == WM_QUERYENDSESSION: + return True + + def OnSuspend(self, hWnd, message, wParam, lParam): + self.log("info", "APM suspend event received") + reload(actions).Suspend() + + def OnResume(self, hWnd, message, wParam, lParam): + self.log("info", "APM resume event received") + reload(actions).Resume() class PowerMonitorService(svcutil.ServiceFramework, PowerMonitor): - _svc_name_ = "PowerMonitor" - _svc_display_name_ = "Power event watcher" - - def __init__(self, args): - svcutil.ServiceFramework.__init__(self, args) - PowerMonitor.__init__(self, name=self._svc_display_name_, - classname=self._svc_name_) - - def log(self, priority, message): - if priority == "error": - logger = smgr.LogErrorMsg - elif priority == "warning": - logger = smgr.LogWarningMsg - else: - logger = smgr.LogInfoMsg - logger(message) - - def SvcDoRun(self): - self.start() - - def SvcStop(self): - self.ReportServiceStatus(svc.SERVICE_STOP_PENDING) - self.stop() - self.ReportServiceStatus(svc.SERVICE_STOPPED) + _svc_name_ = "PowerMonitor" + _svc_display_name_ = "Power event watcher" + + def __init__(self, args): + svcutil.ServiceFramework.__init__(self, args) + PowerMonitor.__init__(self, name=self._svc_display_name_, + classname=self._svc_name_) + + def log(self, priority, message): + if priority == "error": + logger = smgr.LogErrorMsg + elif priority == "warning": + logger = smgr.LogWarningMsg + else: + logger = smgr.LogInfoMsg + logger(message) + + def SvcDoRun(self): + self.start() + + def SvcStop(self): + self.ReportServiceStatus(svc.SERVICE_STOP_PENDING) + self.stop() + self.ReportServiceStatus(svc.SERVICE_STOPPED) if __name__ == '__main__': - svcutil.HandleCommandLine(PowerMonitorService) + svcutil.HandleCommandLine(PowerMonitorService) diff --git a/win32/ts.py b/win32/ts.py index dfea8c72a..a464663a6 100644 --- a/win32/ts.py +++ b/win32/ts.py @@ -1,5 +1,6 @@ -# -*- mode: python -*- -# A couple of Terminal Services commands +#!python +# a couple of Terminal Services commands + from __future__ import print_function import sys import socket @@ -9,222 +10,222 @@ from operator import itemgetter WTSSessionState = ( - "active", - "connected", - "queryconn", - "shadow", - "disconnect", - "idle", - "listen", - "reset", - "down", - "init", + "active", + "connected", + "queryconn", + "shadow", + "disconnect", + "idle", + "listen", + "reset", + "down", + "init", ) WTSProtocolType = { - Ts.WTS_PROTOCOL_TYPE_CONSOLE: "console", - Ts.WTS_PROTOCOL_TYPE_ICA: "citrix-ica", - Ts.WTS_PROTOCOL_TYPE_RDP: "ms-rdp", - None: "unknown", + Ts.WTS_PROTOCOL_TYPE_CONSOLE: "console", + Ts.WTS_PROTOCOL_TYPE_ICA: "citrix-ica", + Ts.WTS_PROTOCOL_TYPE_RDP: "ms-rdp", + None: "unknown", } def IsLocalhost(server): - return server.lower() == socket.gethostname().lower() + return server.lower() == socket.gethostname().lower() def OpenServer(server=None): - if (server is None) or IsLocalhost(server): - server_h = Ts.WTS_CURRENT_SERVER_HANDLE - else: - server_h = Ts.WTSOpenServer(server) - return server_h + if (server is None) or IsLocalhost(server): + server_h = Ts.WTS_CURRENT_SERVER_HANDLE + else: + server_h = Ts.WTSOpenServer(server) + return server_h def CloseServer(server_h): - return Ts.WTSCloseServer(server_h) + return Ts.WTSCloseServer(server_h) def EnumServers(): - for name in sorted(Ts.WTSEnumerateServers()): - yield name.upper(), OpenServer(name) + for name in sorted(Ts.WTSEnumerateServers()): + yield name.upper(), OpenServer(name) def EnumSessions(): - for server, server_h in EnumServers(): - for session in EnumServerSessions(server, server_h): - yield session + for server, server_h in EnumServers(): + for session in EnumServerSessions(server, server_h): + yield session def EnumServerSessions(server, server_h): - for session in Ts.WTSEnumerateSessions(server_h, 1): - session["Server"] = server - session["hServer"] = server_h - try: - session["User"] = Ts.WTSQuerySessionInformation(session["hServer"], - session["SessionId"], Ts.WTSUserName) - session["Protocol"] = Ts.WTSQuerySessionInformation(session["hServer"], - session["SessionId"], Ts.WTSClientProtocolType) - except Api.error: - session["User"] = "" - session["Protocol"] = None - yield session + for session in Ts.WTSEnumerateSessions(server_h, 1): + session["Server"] = server + session["hServer"] = server_h + try: + session["User"] = Ts.WTSQuerySessionInformation(session["hServer"], + session["SessionId"], Ts.WTSUserName) + session["Protocol"] = Ts.WTSQuerySessionInformation(session["hServer"], + session["SessionId"], Ts.WTSClientProtocolType) + except Api.error: + session["User"] = "" + session["Protocol"] = None + yield session def EnumProcesses(): - for server, server_h in EnumServers(): - for session, pid, image, sid in Ts.WTSEnumerateProcesses(server_h, 1): - yield {"Server": server, "hServer": server_h, - "SessionId": session, "Pid": pid, "Image": image, "Sid": sid} + for server, server_h in EnumServers(): + for session, pid, image, sid in Ts.WTSEnumerateProcesses(server_h, 1): + yield {"Server": server, "hServer": server_h, + "SessionId": session, "Pid": pid, "Image": image, "Sid": sid} class TermServUtil(): - def cmd_help(self, cmd, args): - print("Subcommands:") - print("\tKILL [\\\\server] pid[@server] ...") - print("\tLOGOUT [\\\\server] sid[@server] ...") - print("\tLS") - print("\tPS [\\\\server] [/o:] [/s:]") - print("\t\tsort: n=image name, h=hostname, s=session id") - print("\tW") - print("\tWHO") - def cmd_conn(self, cmd, args): - target = args[0] - subprocess.Popen(["mstsc", "/v:%s" % target]) - - def cmd_kill(self, cmd, args): - targets = {} - def_server = None - for arg in args: - arg = arg.upper() - if arg.startswith("\\\\"): - def_server = arg[2:] - continue - elif "@" in arg: - pids, server = arg.split("@", 1) - else: - pids, server = arg, def_server - - pids = map(int, pids.split(",")) - if server in targets: - targets[server] += pids - else: - targets[server] = pids - - for server in targets: - server_h = OpenServer(server) - for pid in targets[server]: - print("Killing %s on %s" % (pid, server)) - Ts.WTSTerminateProcess(server_h, pid, 1) - CloseServer(server_h) - - def cmd_logout(self, cmd, args): - targets = {} - def_server = None - for arg in args: - arg = arg.upper() - if arg.startswith("\\\\"): - def_server = arg[2:] - continue - elif "@" in arg: - sids, server = arg.split("@", 1) - else: - sids, server = arg, def_server - - sids = map(int, sids.split(",")) - if server in targets: - targets[server] += sids - else: - targets[server] = sids - - for server in targets: - server_h = OpenServer(server) - for sid in targets[server]: - print("Logoff %s on %s" % (sid, server)) - Ts.WTSLogoffSession(server_h, sid, False) - CloseServer(server_h) - - def cmd_ls(self, cmd, args): - print(" %-15s %-5s %-5s" % ("HOSTNAME", "USERS", "SESS")) - print(" "+("-"*78)) - for server, server_h in EnumServers(): - sessions = list(EnumServerSessions(server, server_h)) - nsessions = len(sessions) - nusers = len(list(filter(itemgetter("User"), sessions))) - print(" %-15s %-5s %-5s" % (server, nusers, nsessions)) - - def cmd_ps(self, cmd, args): - servers = [] - conditions = [] - sortmode = "nhs" - for arg in args: - if arg.startswith("\\\\"): - servers.append(arg[2:].upper()) - elif arg.startswith("/o:"): - sortmode = arg[3:] - elif arg.startswith("/s:"): - conditions.append((lambda proc, arg: str(proc["SessionId"]) == arg, arg[3:])) - elif "=" in arg: - k, v = arg.split("=", 1) - conditions.append((lambda proc, arg: str(proc[arg[0]]) == arg[1], (k, v))) - procs = EnumProcesses() - sessions = EnumSessions() - byserver = {} - for session in sessions: - server = session["Server"] - if server not in byserver: - byserver[server] = {} - byserver[server][session["SessionId"]] = session - for k in reversed(sortmode): - if k == "s": - procs = sorted(procs, key=itemgetter("SessionId")) - elif k == "h": - procs = sorted(procs, key=itemgetter("Server")) - elif k == "n": - procs = sorted(procs, key=lambda p: p["Image"].lower()) - else: - print("Unknown sort key '%s'" % k) - sys.exit() - for proc in procs: - proc["User"] = byserver[proc["Server"]][proc["SessionId"]]["User"] - if len(servers) and proc["Server"] not in servers: - continue - if len(conditions) and not all(map(lambda func: func[0](proc, func[1]), conditions)): - continue - print(" %(Server)-10s %(User)-14s %(SessionId)5d %(Pid)5d %(Image)s" % proc) - - def cmd_users(self, cmd, args): - return self.cmd_who("users", args) - - def cmd_w(self, cmd, args): - return self.cmd_who("users", args) - - def cmd_who(self, cmd, args): - sessions = EnumSessions() - - if cmd == "who": - cond = lambda session: True - sessions = sorted(sessions, key=lambda k: k["SessionId"]) - sessions = sorted(sessions, key=lambda k: k["Server"].lower()) - format = " %(Server)-15s %(WinStationName)-12s %(SessionId)5d %(User)-20s %(State)-6s %(Protocol)-7s" - print(" %-15s %-12s %5s %-20s %-6s %-7s" % ("SERVERNAME", "SESSIONNAME", "ID", "USERNAME", "STATE", "TYPE")) - else: - cond = lambda session: session["User"] - sessions = sorted(sessions, key=lambda k: k["Server"].lower()) - sessions = sorted(sessions, key=lambda k: k["User"].lower()) - format = " %(User)-20s %(Server)-15s %(WinStationName)-12s %(SessionId)5d %(State)-6s" - print(" %-20s %-15s %-12s %5s %-6s" % ("USERNAME", "SERVERNAME", "SESSIONNAME", "ID", "STATE")) - print(" "+("-"*78)) - for session in sessions: - if not cond(session): - continue - session["State"] = WTSSessionState[session["State"]][:6] - session["Protocol"] = WTSProtocolType[session["Protocol"]] - if not session["User"]: - session["User"] = "-" - print(format % session) - - def unknown(self, cmd, args): - print("Unknown command '%s'" % cmd, file=sys.stderr) + def cmd_help(self, cmd, args): + print("Subcommands:") + print("\tKILL [\\\\server] pid[@server] ...") + print("\tLOGOUT [\\\\server] sid[@server] ...") + print("\tLS") + print("\tPS [\\\\server] [/o:] [/s:]") + print("\t\tsort: n=image name, h=hostname, s=session id") + print("\tW") + print("\tWHO") + def cmd_conn(self, cmd, args): + target = args[0] + subprocess.Popen(["mstsc", "/v:%s" % target]) + + def cmd_kill(self, cmd, args): + targets = {} + def_server = None + for arg in args: + arg = arg.upper() + if arg.startswith("\\\\"): + def_server = arg[2:] + continue + elif "@" in arg: + pids, server = arg.split("@", 1) + else: + pids, server = arg, def_server + + pids = map(int, pids.split(",")) + if server in targets: + targets[server] += pids + else: + targets[server] = pids + + for server in targets: + server_h = OpenServer(server) + for pid in targets[server]: + print("Killing %s on %s" % (pid, server)) + Ts.WTSTerminateProcess(server_h, pid, 1) + CloseServer(server_h) + + def cmd_logout(self, cmd, args): + targets = {} + def_server = None + for arg in args: + arg = arg.upper() + if arg.startswith("\\\\"): + def_server = arg[2:] + continue + elif "@" in arg: + sids, server = arg.split("@", 1) + else: + sids, server = arg, def_server + + sids = map(int, sids.split(",")) + if server in targets: + targets[server] += sids + else: + targets[server] = sids + + for server in targets: + server_h = OpenServer(server) + for sid in targets[server]: + print("Logoff %s on %s" % (sid, server)) + Ts.WTSLogoffSession(server_h, sid, False) + CloseServer(server_h) + + def cmd_ls(self, cmd, args): + print(" %-15s %-5s %-5s" % ("HOSTNAME", "USERS", "SESS")) + print(" "+("-"*78)) + for server, server_h in EnumServers(): + sessions = list(EnumServerSessions(server, server_h)) + nsessions = len(sessions) + nusers = len(list(filter(itemgetter("User"), sessions))) + print(" %-15s %-5s %-5s" % (server, nusers, nsessions)) + + def cmd_ps(self, cmd, args): + servers = [] + conditions = [] + sortmode = "nhs" + for arg in args: + if arg.startswith("\\\\"): + servers.append(arg[2:].upper()) + elif arg.startswith("/o:"): + sortmode = arg[3:] + elif arg.startswith("/s:"): + conditions.append((lambda proc, arg: str(proc["SessionId"]) == arg, arg[3:])) + elif "=" in arg: + k, v = arg.split("=", 1) + conditions.append((lambda proc, arg: str(proc[arg[0]]) == arg[1], (k, v))) + procs = EnumProcesses() + sessions = EnumSessions() + byserver = {} + for session in sessions: + server = session["Server"] + if server not in byserver: + byserver[server] = {} + byserver[server][session["SessionId"]] = session + for k in reversed(sortmode): + if k == "s": + procs = sorted(procs, key=itemgetter("SessionId")) + elif k == "h": + procs = sorted(procs, key=itemgetter("Server")) + elif k == "n": + procs = sorted(procs, key=lambda p: p["Image"].lower()) + else: + print("Unknown sort key '%s'" % k) + sys.exit() + for proc in procs: + proc["User"] = byserver[proc["Server"]][proc["SessionId"]]["User"] + if len(servers) and proc["Server"] not in servers: + continue + if len(conditions) and not all(map(lambda func: func[0](proc, func[1]), conditions)): + continue + print(" %(Server)-10s %(User)-14s %(SessionId)5d %(Pid)5d %(Image)s" % proc) + + def cmd_users(self, cmd, args): + return self.cmd_who("users", args) + + def cmd_w(self, cmd, args): + return self.cmd_who("users", args) + + def cmd_who(self, cmd, args): + sessions = EnumSessions() + + if cmd == "who": + cond = lambda session: True + sessions = sorted(sessions, key=lambda k: k["SessionId"]) + sessions = sorted(sessions, key=lambda k: k["Server"].lower()) + format = " %(Server)-15s %(WinStationName)-12s %(SessionId)5d %(User)-20s %(State)-6s %(Protocol)-7s" + print(" %-15s %-12s %5s %-20s %-6s %-7s" % ("SERVERNAME", "SESSIONNAME", "ID", "USERNAME", "STATE", "TYPE")) + else: + cond = lambda session: session["User"] + sessions = sorted(sessions, key=lambda k: k["Server"].lower()) + sessions = sorted(sessions, key=lambda k: k["User"].lower()) + format = " %(User)-20s %(Server)-15s %(WinStationName)-12s %(SessionId)5d %(State)-6s" + print(" %-20s %-15s %-12s %5s %-6s" % ("USERNAME", "SERVERNAME", "SESSIONNAME", "ID", "STATE")) + print(" "+("-"*78)) + for session in sessions: + if not cond(session): + continue + session["State"] = WTSSessionState[session["State"]][:6] + session["Protocol"] = WTSProtocolType[session["Protocol"]] + if not session["User"]: + session["User"] = "-" + print(format % session) + + def unknown(self, cmd, args): + print("Unknown command '%s'" % cmd, file=sys.stderr) tsu = TermServUtil() try: - command = sys.argv[1].lower() + command = sys.argv[1].lower() except IndexError: - command = "users" + command = "users" args = sys.argv[2:] getattr(tsu, "cmd_%s" % command, tsu.unknown)(command, args)