| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- #!/usr/bin/env python
- """Grab URLs from the clipboard, interpret the queries as OpenID, and print.
- In addition to URLs, I also scan for queries as they appear in httpd log files,
- with a pattern like 'GET /foo?bar=baz HTTP'.
- Requires the 'xsel' program to get the contents of the clipboard.
- """
- from pprint import pformat
- from urlparse import urlsplit, urlunsplit
- import cgi, re, subprocess, sys
- from openid import message
- OPENID_SORT_ORDER = ['mode', 'identity', 'claimed_id']
- class NoQuery(Exception):
- def __init__(self, url):
- self.url = url
- def __str__(self):
- return "No query in url %s" % (self.url,)
- def getClipboard():
- xsel = subprocess.Popen(["xsel", "-o", "-b"], stdout=subprocess.PIPE)
- output = xsel.communicate()[0]
- return output
- def main():
- source = getClipboard()
- urls = find_urls(source)
- errors = []
- output = []
- queries = []
- queries.extend(queriesFromPostdata(source))
- for url in urls:
- try:
- queries.append(queryFromURL(url))
- except NoQuery, err:
- errors.append(err)
- queries.extend(queriesFromLogs(source))
- for where, query in queries:
- output.append('at %s:\n%s' % (where, openidFromQuery(query)))
- if output:
- print '\n\n'.join(output)
- elif errors:
- for err in errors:
- print err
- def queryFromURL(url):
- split_url = urlsplit(url)
- query = cgi.parse_qs(split_url[3])
- if not query:
- raise NoQuery(url)
- url_without_query = urlunsplit(split_url[:3] + (None, None))
- return (url_without_query, query)
- def openidFromQuery(query):
- try:
- msg = message.Message.fromPostArgs(unlistify(query))
- s = formatOpenIDMessage(msg)
- except Exception, err:
- # XXX - side effect.
- sys.stderr.write(str(err))
- s = pformat(query)
- return s
- def formatOpenIDMessage(msg):
- value_lists = {}
- for (ns_uri, ns_key), value in msg.args.items():
- l = value_lists.setdefault(ns_uri, {})
- l[ns_key] = value
- output = []
- for ns_uri, values in value_lists.items():
- ns_output = []
- alias = msg.namespaces.getAlias(ns_uri)
- if alias is message.NULL_NAMESPACE:
- alias = 'openid'
- ns_output.append(" %s <%s>" % (alias, ns_uri))
- for key in OPENID_SORT_ORDER:
- try:
- ns_output.append(" %s = %s" % (key, values.pop(key)))
- except KeyError:
- pass
- values = values.items()
- values.sort()
- for k, v in values:
- ns_output.append(" %s = %s" % (k, v))
- output.append('\n'.join(ns_output))
- return '\n\n'.join(output)
- def unlistify(d):
- return dict((i[0], i[1][0]) for i in d.items())
- def queriesFromLogs(s):
- qre = re.compile(r'GET (/.*)?\?(.+) HTTP')
- return [(match.group(1), cgi.parse_qs(match.group(2)))
- for match in qre.finditer(s)]
- def queriesFromPostdata(s):
- # This looks for query data in a line that starts POSTDATA=.
- # Tamperdata outputs such lines. If there's a 'Host=' in that block,
- # use that too, but don't require it.
- qre = re.compile(r'(?:^Host=(?P<host>.+?)$.*?)?^POSTDATA=(?P<query>.*)$',
- re.DOTALL | re.MULTILINE)
- return [(match.group('host') or 'POSTDATA',
- cgi.parse_qs(match.group('query'))) for match in qre.finditer(s)]
- def find_urls(s):
- # Regular expression borrowed from urlscan
- # by Daniel Burrows <dburrows@debian.org>, GPL.
- urlinternalpattern=r'[{}a-zA-Z/\-_0-9%?&.=:;+,#~]'
- urltrailingpattern=r'[{}a-zA-Z/\-_0-9%&=+#]'
- httpurlpattern = r'(?:https?://' + urlinternalpattern + r'*' + urltrailingpattern + r')'
- # Used to guess that blah.blah.blah.TLD is a URL.
- tlds=['biz', 'com', 'edu', 'info', 'org']
- guessedurlpattern=r'(?:[a-zA-Z0-9_\-%]+(?:\.[a-zA-Z0-9_\-%]+)*\.(?:' + '|'.join(tlds) + '))'
- urlre = re.compile(r'(?:<(?:URL:)?)?(' + httpurlpattern + '|' + guessedurlpattern + '|(?:mailto:[a-zA-Z0-9\-_]*@[0-9a-zA-Z_\-.]*[0-9a-zA-Z_\-]))>?')
- return [match.group(1) for match in urlre.finditer(s)]
- if __name__ == '__main__':
- main()
|