diff --git a/.gitignore b/.gitignore index 2da33f4..a70d2b6 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ website/lib/ website/src/ website/local website/build/ -website/private website/frontend/data.py website/static + +config.json diff --git a/config.json b/config.json deleted file mode 100644 index 0117eac..0000000 --- a/config.json +++ /dev/null @@ -1,37 +0,0 @@ -// Configuration file for application. -// See config.private.json for private values. -// vim: set ts=8 sw=8 et sts=8 ai ft=javascript: -{ - "config": { - "secret": "move me to config.private.json", - "localips": [ - "^192\\.168\\.", - "^172\\.(16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31)\\.", - "^10\\.", - "" - ], - "reload" : 600, - "": "" - }, - - "default": { - "channel": "#timvideos", - "irclog": "http://logs.timvideos.us/%23timvideos/preview.log.html", - "preview": "http://preview.timvideos.us/%23timvideos/latest.png", - "twitter_announce": "@timvideosus", - "twitter": "@timvideosus OR #timvideos OR #timvideosus", - "logo": "/static/logo/timvideos.png", - "link": "https://us.pycon.org/2012/schedule/", - "title": "Unknown stream", - "justintv_user": false, - "flumotion-mixer": false, - "flumotion-encoder": false, - "flumotion-collector": false, - "": "" - }, - - "example": { - }, - - "": {} -} diff --git a/tools/flumotion-config/pusher/Dockerfile_base b/tools/flumotion-config/pusher/Dockerfile_base new file mode 100644 index 0000000..fd52b89 --- /dev/null +++ b/tools/flumotion-config/pusher/Dockerfile_base @@ -0,0 +1,11 @@ +# DOCKER-VERSION 1.1.0 +FROM streamingsystem/flumotion +RUN apt-get update + +ADD sources /home/ +RUN cp /home/%s /usr/local/etc/flumotion/workers/default.xml +RUN cp /home/%s /usr/local/etc/flumotion/managers/default/planet.xml + +%s + +CMD service flumotion start && tail -f /usr/local/var/log/flumotion/* diff --git a/tools/flumotion-config/pusher/bin/activate b/tools/flumotion-config/pusher/bin/activate new file mode 100644 index 0000000..f9648a0 --- /dev/null +++ b/tools/flumotion-config/pusher/bin/activate @@ -0,0 +1,80 @@ +# This file must be used with "source bin/activate" *from bash* +# you cannot run it directly + +deactivate () { + unset pydoc + + # reset old environment variables + if [ -n "$_OLD_VIRTUAL_PATH" ] ; then + PATH="$_OLD_VIRTUAL_PATH" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if [ -n "$_OLD_VIRTUAL_PYTHONHOME" ] ; then + PYTHONHOME="$_OLD_VIRTUAL_PYTHONHOME" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # This should detect bash and zsh, which have a hash command that must + # be called to get it to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then + hash -r 2>/dev/null + fi + + if [ -n "$_OLD_VIRTUAL_PS1" ] ; then + PS1="$_OLD_VIRTUAL_PS1" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + if [ ! "$1" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +VIRTUAL_ENV="/Users/mstritzinger/Projects/GSoC2014/streaming-system-git/tools/flumotion-config/pusher" +export VIRTUAL_ENV + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/bin:$PATH" +export PATH + +# unset PYTHONHOME if set +# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) +# could use `if (set -u; : $PYTHONHOME) ;` in bash +if [ -n "$PYTHONHOME" ] ; then + _OLD_VIRTUAL_PYTHONHOME="$PYTHONHOME" + unset PYTHONHOME +fi + +if [ -z "$VIRTUAL_ENV_DISABLE_PROMPT" ] ; then + _OLD_VIRTUAL_PS1="$PS1" + if [ "x" != x ] ; then + PS1="$PS1" + else + if [ "`basename \"$VIRTUAL_ENV\"`" = "__" ] ; then + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + PS1="[`basename \`dirname \"$VIRTUAL_ENV\"\``] $PS1" + else + PS1="(`basename \"$VIRTUAL_ENV\"`)$PS1" + fi + fi + export PS1 +fi + +alias pydoc="python -m pydoc" + +# This should detect bash and zsh, which have a hash command that must +# be called to get it to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then + hash -r 2>/dev/null +fi diff --git a/tools/flumotion-config/pusher/bin/activate.csh b/tools/flumotion-config/pusher/bin/activate.csh new file mode 100644 index 0000000..fe3d650 --- /dev/null +++ b/tools/flumotion-config/pusher/bin/activate.csh @@ -0,0 +1,42 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. +# Created by Davide Di Blasi . + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate && unalias pydoc' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV "/Users/mstritzinger/Projects/GSoC2014/streaming-system-git/tools/flumotion-config/pusher" + +set _OLD_VIRTUAL_PATH="$PATH" +setenv PATH "$VIRTUAL_ENV/bin:$PATH" + + + +if ("" != "") then + set env_name = "" +else + if (`basename "$VIRTUAL_ENV"` == "__") then + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + set env_name = `basename \`dirname "$VIRTUAL_ENV"\`` + else + set env_name = `basename "$VIRTUAL_ENV"` + endif +endif + +# Could be in a non-interactive environment, +# in which case, $prompt is undefined and we wouldn't +# care about the prompt anyway. +if ( $?prompt ) then + set _OLD_VIRTUAL_PROMPT="$prompt" + set prompt = "[$env_name] $prompt" +endif + +unset env_name + +alias pydoc python -m pydoc + +rehash + diff --git a/tools/flumotion-config/pusher/bin/activate.fish b/tools/flumotion-config/pusher/bin/activate.fish new file mode 100644 index 0000000..5c7ba07 --- /dev/null +++ b/tools/flumotion-config/pusher/bin/activate.fish @@ -0,0 +1,74 @@ +# This file must be used with "source bin/activate.fish" *from fish* (http://fishshell.com) +# you cannot run it directly + +function deactivate -d "Exit virtualenv and return to normal shell environment" + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + set -gx PATH $_OLD_VIRTUAL_PATH + set -e _OLD_VIRTUAL_PATH + end + if test -n "$_OLD_VIRTUAL_PYTHONHOME" + set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME + set -e _OLD_VIRTUAL_PYTHONHOME + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + # set an empty local fish_function_path, so fish_prompt doesn't automatically reload + set -l fish_function_path + # erase the virtualenv's fish_prompt function, and restore the original + functions -e fish_prompt + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + set -e _OLD_FISH_PROMPT_OVERRIDE + end + + set -e VIRTUAL_ENV + if test "$argv[1]" != "nondestructive" + # Self destruct! + functions -e deactivate + end +end + +# unset irrelevant variables +deactivate nondestructive + +set -gx VIRTUAL_ENV "/Users/mstritzinger/Projects/GSoC2014/streaming-system-git/tools/flumotion-config/pusher" + +set -gx _OLD_VIRTUAL_PATH $PATH +set -gx PATH "$VIRTUAL_ENV/bin" $PATH + +# unset PYTHONHOME if set +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # fish uses a function instead of an env var to generate the prompt. + + # copy the current fish_prompt function as the function _old_fish_prompt + functions -c fish_prompt _old_fish_prompt + + # with the original prompt function copied, we can override with our own. + function fish_prompt + # Prompt override? + if test -n "" + printf "%s%s" "" (set_color normal) + _old_fish_prompt + return + end + # ...Otherwise, prepend env + set -l _checkbase (basename "$VIRTUAL_ENV") + if test $_checkbase = "__" + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + printf "%s[%s]%s " (set_color -b blue white) (basename (dirname "$VIRTUAL_ENV")) (set_color normal) + _old_fish_prompt + else + printf "%s(%s)%s" (set_color -b blue white) (basename "$VIRTUAL_ENV") (set_color normal) + _old_fish_prompt + end + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" +end diff --git a/tools/flumotion-config/pusher/bin/activate_this.py b/tools/flumotion-config/pusher/bin/activate_this.py new file mode 100644 index 0000000..ea12c28 --- /dev/null +++ b/tools/flumotion-config/pusher/bin/activate_this.py @@ -0,0 +1,34 @@ +"""By using execfile(this_file, dict(__file__=this_file)) you will +activate this virtualenv environment. + +This can be used when you must use an existing Python interpreter, not +the virtualenv bin/python +""" + +try: + __file__ +except NameError: + raise AssertionError( + "You must run this like execfile('path/to/activate_this.py', dict(__file__='path/to/activate_this.py'))") +import sys +import os + +old_os_path = os.environ['PATH'] +os.environ['PATH'] = os.path.dirname(os.path.abspath(__file__)) + os.pathsep + old_os_path +base = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +if sys.platform == 'win32': + site_packages = os.path.join(base, 'Lib', 'site-packages') +else: + site_packages = os.path.join(base, 'lib', 'python%s' % sys.version[:3], 'site-packages') +prev_sys_path = list(sys.path) +import site +site.addsitedir(site_packages) +sys.real_prefix = sys.prefix +sys.prefix = base +# Move the added items to the front of the path: +new_sys_path = [] +for item in list(sys.path): + if item not in prev_sys_path: + new_sys_path.append(item) + sys.path.remove(item) +sys.path[:0] = new_sys_path diff --git a/tools/flumotion-config/pusher/bin/easy_install b/tools/flumotion-config/pusher/bin/easy_install new file mode 100755 index 0000000..c097838 --- /dev/null +++ b/tools/flumotion-config/pusher/bin/easy_install @@ -0,0 +1,11 @@ +#!/Users/mstritzinger/Projects/GSoC2014/streaming-system-git/tools/flumotion-config/pusher/bin/python + +# -*- coding: utf-8 -*- +import re +import sys + +from setuptools.command.easy_install import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/tools/flumotion-config/pusher/bin/easy_install-2.7 b/tools/flumotion-config/pusher/bin/easy_install-2.7 new file mode 100755 index 0000000..c097838 --- /dev/null +++ b/tools/flumotion-config/pusher/bin/easy_install-2.7 @@ -0,0 +1,11 @@ +#!/Users/mstritzinger/Projects/GSoC2014/streaming-system-git/tools/flumotion-config/pusher/bin/python + +# -*- coding: utf-8 -*- +import re +import sys + +from setuptools.command.easy_install import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/tools/flumotion-config/pusher/bin/fab b/tools/flumotion-config/pusher/bin/fab new file mode 100755 index 0000000..c04bbf8 --- /dev/null +++ b/tools/flumotion-config/pusher/bin/fab @@ -0,0 +1,10 @@ +#!/Users/mstritzinger/Projects/GSoC2014/streaming-system-git/tools/flumotion-config/pusher/bin/python +# EASY-INSTALL-ENTRY-SCRIPT: 'Fabric==1.9.0','console_scripts','fab' +__requires__ = 'Fabric==1.9.0' +import sys +from pkg_resources import load_entry_point + +if __name__ == '__main__': + sys.exit( + load_entry_point('Fabric==1.9.0', 'console_scripts', 'fab')() + ) diff --git a/tools/flumotion-config/pusher/bin/pip b/tools/flumotion-config/pusher/bin/pip new file mode 100755 index 0000000..d7ce437 --- /dev/null +++ b/tools/flumotion-config/pusher/bin/pip @@ -0,0 +1,11 @@ +#!/Users/mstritzinger/Projects/GSoC2014/streaming-system-git/tools/flumotion-config/pusher/bin/python + +# -*- coding: utf-8 -*- +import re +import sys + +from pip import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/tools/flumotion-config/pusher/bin/pip2 b/tools/flumotion-config/pusher/bin/pip2 new file mode 100755 index 0000000..d7ce437 --- /dev/null +++ b/tools/flumotion-config/pusher/bin/pip2 @@ -0,0 +1,11 @@ +#!/Users/mstritzinger/Projects/GSoC2014/streaming-system-git/tools/flumotion-config/pusher/bin/python + +# -*- coding: utf-8 -*- +import re +import sys + +from pip import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/tools/flumotion-config/pusher/bin/pip2.7 b/tools/flumotion-config/pusher/bin/pip2.7 new file mode 100755 index 0000000..d7ce437 --- /dev/null +++ b/tools/flumotion-config/pusher/bin/pip2.7 @@ -0,0 +1,11 @@ +#!/Users/mstritzinger/Projects/GSoC2014/streaming-system-git/tools/flumotion-config/pusher/bin/python + +# -*- coding: utf-8 -*- +import re +import sys + +from pip import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/tools/flumotion-config/pusher/bin/python b/tools/flumotion-config/pusher/bin/python new file mode 100755 index 0000000..bbd0088 Binary files /dev/null and b/tools/flumotion-config/pusher/bin/python differ diff --git a/tools/flumotion-config/pusher/bin/python2 b/tools/flumotion-config/pusher/bin/python2 new file mode 120000 index 0000000..d8654aa --- /dev/null +++ b/tools/flumotion-config/pusher/bin/python2 @@ -0,0 +1 @@ +python \ No newline at end of file diff --git a/tools/flumotion-config/pusher/bin/python2.7 b/tools/flumotion-config/pusher/bin/python2.7 new file mode 120000 index 0000000..d8654aa --- /dev/null +++ b/tools/flumotion-config/pusher/bin/python2.7 @@ -0,0 +1 @@ +python \ No newline at end of file diff --git a/tools/flumotion-config/pusher/collector.xml b/tools/flumotion-config/pusher/collector.xml index ecc48da..4eada09 100644 --- a/tools/flumotion-config/pusher/collector.xml +++ b/tools/flumotion-config/pusher/collector.xml @@ -131,7 +131,7 @@ %(flumotion-user)s %(flumotion-encoder)s %(flumotion-password)s - 15000 + %(flumotion-encoder-port)s false diff --git a/tools/flumotion-config/pusher/fabfile.py b/tools/flumotion-config/pusher/fabfile.py new file mode 100644 index 0000000..6d87540 --- /dev/null +++ b/tools/flumotion-config/pusher/fabfile.py @@ -0,0 +1,157 @@ +from fabric.api import * +from fabric.context_managers import * +from fabric.contrib.console import confirm +import json +import sys +import time +import crypt +from os import getcwd, path, listdir + +env.hosts = filter(lambda x: x[0] != '.', listdir('hosts')) + +class bcolors: + HEADER = '\033[95m' + OKBLUE = '\033[94m' + OKGREEN = '\033[92m' + WARNING = '\033[93m' + FAIL = '\033[91m' + ENDC = '\033[0m' + + +def prepare_deploy(): + with settings(warn_only=True): + local('rm hosts/*') + local('./genconfig.py') + +def deploy(): + host = env.host + host_conf = 'hosts/' + host + host_load(host_conf) + +def up(): + host = env.host + host_conf = 'hosts/' + host + host_start(host_conf) + +def down(): + host = env.host + host_conf = 'hosts/' + host + host_stop(host_conf) + +def host_stop(host_file): + host = json.loads(file_read(host_file)) + connection = host['connection_details'] + with settings(user = connection['user'] or 'docker', password = connection['password'] or 'tcuser', port = connection['port'] or 22, shell = "/bin/sh -c"): + if (run('docker stop $(docker ps -q)').succeeded): + print bcolors.OKGREEN + "All services on %s stopped successfully" % env.host + bcolors.ENDC + else: + print bcolors.FAIL + "Services on %s failed to stop correctly" % env.host + bcolors.ENDC + + +def host_load(host_file): + host = json.loads(file_read(host_file)) + for service in host['services']: + build_image(service, host['connection_details']) + +def file_read(file_name): + with open(file_name) as f: + contents = f.read() + return contents + +def file_write(file_name, contents): + with open(file_name, 'w') as f: + f.write(contents) + return + +# Builds a Docker image on a remote host +def build_image(service_dict, connection): + service_type = service_dict['type'] + service_conf = service_dict['conf'] + success = False + # if we are handling a flumotion service (only service currently supported~) + if (service_type == 'flumotion-encoder' or service_type == 'flumotion-collector'): + # First build flumotion and Docker files locally + group = service_conf['group'] + timestamp = time.time() + sgh = (service_type, group, env.host) + sg = (service_type, group) + try: + docker_base = file_read('Dockerfile_base') + encoder_base = file_read('encoder.xml') + collector_base = file_read('collector.xml') + worker_base = file_read('worker.xml') + except Exception as e: + raise Exception("Base read error on %s service for %s error({0}): {1}".format(e.errno, e.strerror) % sg) + worker_file = '/tmp/worker-%s-%s.xml' % (timestamp, group) + worker_contents = worker_base % service_conf + docker_file = '/tmp/Dockerfile-%s-%s-%s' % (timestamp, service_type, group) + if (service_type == 'flumotion-encoder'): + planet_file = '/tmp/encoder-%s-%s.xml' % (timestamp, group) + planet_contents = encoder_base % service_conf + docker_contents = docker_base % (path.basename(worker_file), path.basename(planet_file), 'EXPOSE\t\t15000') + elif (service_type == 'flumotion-collector'): + planet_file = '/tmp/collector-%s-%s.xml' % (timestamp, group) + planet_contents = collector_base % service_conf + docker_contents = docker_base % (path.basename(worker_file), path.basename(planet_file), '') + file_write(planet_file, planet_contents) + file_write(worker_file, worker_contents) + file_write(docker_file, docker_contents) + + # All configurations should be built locally, let's push them out to our host and build them with Docker. + print "-" * 80 + with settings(user = connection['user'], password = connection['password'], port = connection['port'], shell = "/bin/sh -c", \ + key_filename = connection['keyfile']): + print bcolors.HEADER + "Now pushing to %s" % env.host + bcolors.ENDC + build_dir = '/home/%s/%s-%s-%s/' % (env.user, timestamp, service_type, group) + sources_dir = build_dir + 'sources/' + if (run("mkdir -p " + sources_dir).succeeded): + put(planet_file, sources_dir) + put(worker_file, sources_dir) + put(docker_file, build_dir + 'Dockerfile') + else: + raise Exception("Failed to upload to remote host") + with cd(build_dir): + build_command = 'docker build -q --rm=false -t %s-%s:latest .' % sg + with hide('stdout'): + print bcolors.HEADER + "Building on %s. If this is the first build on this host, this may take several minutes." % env.host + bcolors.ENDC + if (sudo(build_command).succeeded): + print bcolors.OKBLUE + "%s for group %s image build on %s successful!" % sgh + bcolors.ENDC + else: + print bcolors.FAIL + "%s for group %s image build on %s unsuccesful!" % sgh + bcolors.ENDC + print "-" * 80 + print "\n" * 2 + +def host_start(host_file): + try: + f = open(host_file) + host = json.loads(f.read()) + f.close() + except IOError as e: + raise Exception("File read error on " + host_file + " I/O error({0}): {1}".format(e.errno, e.strerror)) + except ValueError as e: + raise Exception("JSON parse error on " + host_file + " I/O error({0}): {1}".format(e.errno, e.strerror)) + for service in host['services']: + start_container(service, host['connection_details']) + +def start_container(service_dict, connection): + service_type = service_dict['type'] + service_conf = service_dict['conf'] + + # if we are handling a flumotion service (only service currently supported~) + if (service_type == 'flumotion-encoder' or service_type == 'flumotion-collector'): + group = service_conf['group'] + sgh = (service_type, group, env.host) + + if (service_type == 'flumotion-encoder'): + start_command = 'docker run -d -p %i:15000 %s-%s:latest' % (service_conf['flumotion-encoder-port'], service_type, group) + else: + start_command = 'docker run -d %s-%s:latest' % (service_type, group) + with settings(user = connection['user'], password = connection['password'], port = connection['port'], shell = "/bin/sh -c", \ + key_filename = connection['keyfile']): + + if (sudo(start_command).succeeded or run(start_command).succeeded): + print (bcolors.OKGREEN + "%s service for %s group started successfully on %s" % sgh + bcolors.ENDC) + else: + print (bcolors.FAIL + "%s service for %s group failed to start on %s with command" % sgh + bcolors.ENDC) + + diff --git a/tools/flumotion-config/pusher/genconfig.py b/tools/flumotion-config/pusher/genconfig.py new file mode 100755 index 0000000..3a8f7e3 --- /dev/null +++ b/tools/flumotion-config/pusher/genconfig.py @@ -0,0 +1,116 @@ +#!/usr/bin/python +# +# -*- coding: utf-8 -*- +# vim: set ts=4 sw=4 et sts=4 ai: + +"""Generate configuration files to be used on a bunch of flumotion boxes.""" + +import crypt +import optparse +import os +import sys +import json + +mypath = os.path.dirname(__file__) +if not mypath: + mypath = "." + +config_path = os.path.realpath(mypath+"/../../..") +if config_path not in sys.path: + sys.path.append(config_path) +import config as common_config +CONFIG = common_config.config_load() + + +OPTIONS = optparse.OptionParser() +OPTIONS.add_option("-g", "--group", + action="store", dest="groups", default="", + help="Groups to write config for.") +OPTIONS.add_option("-e", "--encoders", + action="store_true", dest="encoders", default=True, + help="Write configs for encoders.") +OPTIONS.add_option("--no-encoders", + action="store_false", dest="encoders", + help="Don't write configs for encoders.") +OPTIONS.add_option("-c", "--collectors", + action="store_true", dest="collectors", default=True, + help="Write configs for collectors.") +OPTIONS.add_option("--no-collectors", + action="store_false", dest="collectors", + help="Don't write configs for collectors.") +def get_hosts(): + hosts = set() + (options, args) = OPTIONS.parse_args() + + # Groups to write for + active_groups = [x.strip() for x in options.groups.split(',') if x] + if not active_groups: + active_groups = CONFIG.groups() + for group in CONFIG.groups(): + if group not in active_groups: + print "Skipping", group, "for host generation" + continue + config = CONFIG.config(group) + hosts.add(config['flumotion-encoder']) + hosts.add(config['flumotion-collector']) + return list(hosts) + + +def main(args): + (options, args) = OPTIONS.parse_args() + + # Groups to write for + active_groups = [x.strip() for x in options.groups.split(',') if x] + if not active_groups: + active_groups = CONFIG.groups() + + print "Writing for groups:", active_groups + + # First we generate our host files. Hosts need to know what collectors/encoders they are running and + # what ports they are expected to be on + hosts = {} + default_connection = { "user":"docker", "password":"tcuser", "port":"22", "keyfile":"" } + for group in CONFIG.groups(): + if group not in active_groups: + print "Skipping", group, "for host generation" + continue + + config = CONFIG.config(group) + encoder_host = config['flumotion-encoder'] + collector_host = config['flumotion-collector'] + config['flumotion-password-crypt'] = crypt.crypt( + config['flumotion-password'], + config['flumotion-salt']) + if encoder_host not in hosts: + hosts[encoder_host] = { 'host':encoder_host, 'connection_details':default_connection, 'services':[] } + if collector_host not in hosts: + hosts[collector_host] = { 'host':collector_host, 'connection_details':{}, 'services':[] } + hosts[encoder_host]['services'].append({ 'type': 'flumotion-encoder', 'conf':config }) + hosts[collector_host]['services'].append({ 'type': 'flumotion-collector', 'conf':config }) + + # Hosts have been generated, now time to connect the collectors to the encoders and write our host config file + for host in hosts: + port = 49152 + encoders = filter(lambda x: x["type"] == "flumotion-encoder", hosts[host]['services']) + # For each encoder on the host + for encoder in encoders: + # Set the encoder to listen on an incremented port + group = encoder['conf']['group'] + encoder['conf']['flumotion-encoder-port'] = port + # Grab the host the collector(to that encoder) is running on, then look through that host for the collector we want and then set the encoder port + filter(lambda x: x['conf']['group'] == group and x['type'] == 'flumotion-collector', hosts[CONFIG.config(group)['flumotion-collector']]['services'])[0]['conf']['flumotion-encoder-port'] = port + # Increment the port for this host so we don't use it again + port += 1 + + # Output each host to a configuration file that will get shipped along with the necessary configurations to each host + # (this has to be done in a separate loop because we can't output our host files until after they've all been linked up.) + for host in hosts: + host_file = 'hosts/' + host + f = file(host_file, 'w') + f.write(json.dumps(hosts[host], indent=4, separators=(',', ': '), sort_keys=True)) + f.close() + + +if __name__ == "__main__": + import sys + main(sys.argv) diff --git a/tools/flumotion-config/pusher/include/python2.7 b/tools/flumotion-config/pusher/include/python2.7 new file mode 120000 index 0000000..3fe034f --- /dev/null +++ b/tools/flumotion-config/pusher/include/python2.7 @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 \ No newline at end of file diff --git a/tools/flumotion-config/pusher/lib/python2.7/UserDict.py b/tools/flumotion-config/pusher/lib/python2.7/UserDict.py new file mode 120000 index 0000000..b735f02 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/UserDict.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/UserDict.py \ No newline at end of file diff --git a/tools/flumotion-config/pusher/lib/python2.7/_abcoll.py b/tools/flumotion-config/pusher/lib/python2.7/_abcoll.py new file mode 120000 index 0000000..4a595bc --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/_abcoll.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/_abcoll.py \ No newline at end of file diff --git a/tools/flumotion-config/pusher/lib/python2.7/_weakrefset.py b/tools/flumotion-config/pusher/lib/python2.7/_weakrefset.py new file mode 120000 index 0000000..b8b09b7 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/_weakrefset.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/_weakrefset.py \ No newline at end of file diff --git a/tools/flumotion-config/pusher/lib/python2.7/abc.py b/tools/flumotion-config/pusher/lib/python2.7/abc.py new file mode 120000 index 0000000..87956e5 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/abc.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/abc.py \ No newline at end of file diff --git a/tools/flumotion-config/pusher/lib/python2.7/codecs.py b/tools/flumotion-config/pusher/lib/python2.7/codecs.py new file mode 120000 index 0000000..b18c8d6 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/codecs.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/codecs.py \ No newline at end of file diff --git a/tools/flumotion-config/pusher/lib/python2.7/config b/tools/flumotion-config/pusher/lib/python2.7/config new file mode 120000 index 0000000..88ddfa1 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/config @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/config \ No newline at end of file diff --git a/tools/flumotion-config/pusher/lib/python2.7/copy_reg.py b/tools/flumotion-config/pusher/lib/python2.7/copy_reg.py new file mode 120000 index 0000000..8d0265c --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/copy_reg.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/copy_reg.py \ No newline at end of file diff --git a/tools/flumotion-config/pusher/lib/python2.7/distutils/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/distutils/__init__.py new file mode 100644 index 0000000..29fc1da --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/distutils/__init__.py @@ -0,0 +1,101 @@ +import os +import sys +import warnings +import imp +import opcode # opcode is not a virtualenv module, so we can use it to find the stdlib + # Important! To work on pypy, this must be a module that resides in the + # lib-python/modified-x.y.z directory + +dirname = os.path.dirname + +distutils_path = os.path.join(os.path.dirname(opcode.__file__), 'distutils') +if os.path.normpath(distutils_path) == os.path.dirname(os.path.normpath(__file__)): + warnings.warn( + "The virtualenv distutils package at %s appears to be in the same location as the system distutils?") +else: + __path__.insert(0, distutils_path) + real_distutils = imp.load_module("_virtualenv_distutils", None, distutils_path, ('', '', imp.PKG_DIRECTORY)) + # Copy the relevant attributes + try: + __revision__ = real_distutils.__revision__ + except AttributeError: + pass + __version__ = real_distutils.__version__ + +from distutils import dist, sysconfig + +try: + basestring +except NameError: + basestring = str + +## patch build_ext (distutils doesn't know how to get the libs directory +## path on windows - it hardcodes the paths around the patched sys.prefix) + +if sys.platform == 'win32': + from distutils.command.build_ext import build_ext as old_build_ext + class build_ext(old_build_ext): + def finalize_options (self): + if self.library_dirs is None: + self.library_dirs = [] + elif isinstance(self.library_dirs, basestring): + self.library_dirs = self.library_dirs.split(os.pathsep) + + self.library_dirs.insert(0, os.path.join(sys.real_prefix, "Libs")) + old_build_ext.finalize_options(self) + + from distutils.command import build_ext as build_ext_module + build_ext_module.build_ext = build_ext + +## distutils.dist patches: + +old_find_config_files = dist.Distribution.find_config_files +def find_config_files(self): + found = old_find_config_files(self) + system_distutils = os.path.join(distutils_path, 'distutils.cfg') + #if os.path.exists(system_distutils): + # found.insert(0, system_distutils) + # What to call the per-user config file + if os.name == 'posix': + user_filename = ".pydistutils.cfg" + else: + user_filename = "pydistutils.cfg" + user_filename = os.path.join(sys.prefix, user_filename) + if os.path.isfile(user_filename): + for item in list(found): + if item.endswith('pydistutils.cfg'): + found.remove(item) + found.append(user_filename) + return found +dist.Distribution.find_config_files = find_config_files + +## distutils.sysconfig patches: + +old_get_python_inc = sysconfig.get_python_inc +def sysconfig_get_python_inc(plat_specific=0, prefix=None): + if prefix is None: + prefix = sys.real_prefix + return old_get_python_inc(plat_specific, prefix) +sysconfig_get_python_inc.__doc__ = old_get_python_inc.__doc__ +sysconfig.get_python_inc = sysconfig_get_python_inc + +old_get_python_lib = sysconfig.get_python_lib +def sysconfig_get_python_lib(plat_specific=0, standard_lib=0, prefix=None): + if standard_lib and prefix is None: + prefix = sys.real_prefix + return old_get_python_lib(plat_specific, standard_lib, prefix) +sysconfig_get_python_lib.__doc__ = old_get_python_lib.__doc__ +sysconfig.get_python_lib = sysconfig_get_python_lib + +old_get_config_vars = sysconfig.get_config_vars +def sysconfig_get_config_vars(*args): + real_vars = old_get_config_vars(*args) + if sys.platform == 'win32': + lib_dir = os.path.join(sys.real_prefix, "libs") + if isinstance(real_vars, dict) and 'LIBDIR' not in real_vars: + real_vars['LIBDIR'] = lib_dir # asked for all + elif isinstance(real_vars, list) and 'LIBDIR' in args: + real_vars = real_vars + [lib_dir] # asked for list + return real_vars +sysconfig_get_config_vars.__doc__ = old_get_config_vars.__doc__ +sysconfig.get_config_vars = sysconfig_get_config_vars diff --git a/tools/flumotion-config/pusher/lib/python2.7/distutils/distutils.cfg b/tools/flumotion-config/pusher/lib/python2.7/distutils/distutils.cfg new file mode 100644 index 0000000..1af230e --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/distutils/distutils.cfg @@ -0,0 +1,6 @@ +# This is a config file local to this virtualenv installation +# You may include options that will be used by all distutils commands, +# and by easy_install. For instance: +# +# [easy_install] +# find_links = http://mylocalsite diff --git a/tools/flumotion-config/pusher/lib/python2.7/encodings b/tools/flumotion-config/pusher/lib/python2.7/encodings new file mode 120000 index 0000000..8732f85 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/encodings @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/encodings \ No newline at end of file diff --git a/tools/flumotion-config/pusher/lib/python2.7/fnmatch.py b/tools/flumotion-config/pusher/lib/python2.7/fnmatch.py new file mode 120000 index 0000000..49b6bc0 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/fnmatch.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/fnmatch.py \ No newline at end of file diff --git a/tools/flumotion-config/pusher/lib/python2.7/genericpath.py b/tools/flumotion-config/pusher/lib/python2.7/genericpath.py new file mode 120000 index 0000000..7843bce --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/genericpath.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/genericpath.py \ No newline at end of file diff --git a/tools/flumotion-config/pusher/lib/python2.7/lib-dynload b/tools/flumotion-config/pusher/lib/python2.7/lib-dynload new file mode 120000 index 0000000..24c555e --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/lib-dynload @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload \ No newline at end of file diff --git a/tools/flumotion-config/pusher/lib/python2.7/linecache.py b/tools/flumotion-config/pusher/lib/python2.7/linecache.py new file mode 120000 index 0000000..1f79a61 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/linecache.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/linecache.py \ No newline at end of file diff --git a/tools/flumotion-config/pusher/lib/python2.7/locale.py b/tools/flumotion-config/pusher/lib/python2.7/locale.py new file mode 120000 index 0000000..cc8a5a7 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/locale.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/locale.py \ No newline at end of file diff --git a/tools/flumotion-config/pusher/lib/python2.7/no-global-site-packages.txt b/tools/flumotion-config/pusher/lib/python2.7/no-global-site-packages.txt new file mode 100644 index 0000000..e69de29 diff --git a/tools/flumotion-config/pusher/lib/python2.7/ntpath.py b/tools/flumotion-config/pusher/lib/python2.7/ntpath.py new file mode 120000 index 0000000..af0bbe7 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/ntpath.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/ntpath.py \ No newline at end of file diff --git a/tools/flumotion-config/pusher/lib/python2.7/orig-prefix.txt b/tools/flumotion-config/pusher/lib/python2.7/orig-prefix.txt new file mode 100644 index 0000000..2a45120 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/orig-prefix.txt @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.7 \ No newline at end of file diff --git a/tools/flumotion-config/pusher/lib/python2.7/os.py b/tools/flumotion-config/pusher/lib/python2.7/os.py new file mode 120000 index 0000000..04db928 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/os.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.py \ No newline at end of file diff --git a/tools/flumotion-config/pusher/lib/python2.7/posixpath.py b/tools/flumotion-config/pusher/lib/python2.7/posixpath.py new file mode 120000 index 0000000..cc89aa2 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/posixpath.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.py \ No newline at end of file diff --git a/tools/flumotion-config/pusher/lib/python2.7/re.py b/tools/flumotion-config/pusher/lib/python2.7/re.py new file mode 120000 index 0000000..b1a8e65 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/re.py @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/re.py \ No newline at end of file diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/AES.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/AES.py new file mode 100644 index 0000000..14f68d8 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/AES.py @@ -0,0 +1,115 @@ +# -*- coding: utf-8 -*- +# +# Cipher/AES.py : AES +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== +"""AES symmetric cipher + +AES `(Advanced Encryption Standard)`__ is a symmetric block cipher standardized +by NIST_ . It has a fixed data block size of 16 bytes. +Its keys can be 128, 192, or 256 bits long. + +AES is very fast and secure, and it is the de facto standard for symmetric +encryption. + +As an example, encryption can be done as follows: + + >>> from Crypto.Cipher import AES + >>> from Crypto import Random + >>> + >>> key = b'Sixteen byte key' + >>> iv = Random.new().read(AES.block_size) + >>> cipher = AES.new(key, AES.MODE_CFB, iv) + >>> msg = iv + cipher.encrypt(b'Attack at dawn') + +.. __: http://en.wikipedia.org/wiki/Advanced_Encryption_Standard +.. _NIST: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf + +:undocumented: __revision__, __package__ +""" + +__revision__ = "$Id$" + +from Crypto.Cipher import blockalgo +from Crypto.Cipher import _AES + +class AESCipher (blockalgo.BlockAlgo): + """AES cipher object""" + + def __init__(self, key, *args, **kwargs): + """Initialize an AES cipher object + + See also `new()` at the module level.""" + blockalgo.BlockAlgo.__init__(self, _AES, key, *args, **kwargs) + +def new(key, *args, **kwargs): + """Create a new AES cipher + + :Parameters: + key : byte string + The secret key to use in the symmetric cipher. + It must be 16 (*AES-128*), 24 (*AES-192*), or 32 (*AES-256*) bytes long. + :Keywords: + mode : a *MODE_** constant + The chaining mode to use for encryption or decryption. + Default is `MODE_ECB`. + IV : byte string + The initialization vector to use for encryption or decryption. + + It is ignored for `MODE_ECB` and `MODE_CTR`. + + For `MODE_OPENPGP`, IV must be `block_size` bytes long for encryption + and `block_size` +2 bytes for decryption (in the latter case, it is + actually the *encrypted* IV which was prefixed to the ciphertext). + It is mandatory. + + For all other modes, it must be `block_size` bytes longs. It is optional and + when not present it will be given a default value of all zeroes. + counter : callable + (*Only* `MODE_CTR`). A stateful function that returns the next + *counter block*, which is a byte string of `block_size` bytes. + For better performance, use `Crypto.Util.Counter`. + segment_size : integer + (*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext + are segmented in. + It must be a multiple of 8. If 0 or not specified, it will be assumed to be 8. + + :Return: an `AESCipher` object + """ + return AESCipher(key, *args, **kwargs) + +#: Electronic Code Book (ECB). See `blockalgo.MODE_ECB`. +MODE_ECB = 1 +#: Cipher-Block Chaining (CBC). See `blockalgo.MODE_CBC`. +MODE_CBC = 2 +#: Cipher FeedBack (CFB). See `blockalgo.MODE_CFB`. +MODE_CFB = 3 +#: This mode should not be used. +MODE_PGP = 4 +#: Output FeedBack (OFB). See `blockalgo.MODE_OFB`. +MODE_OFB = 5 +#: CounTer Mode (CTR). See `blockalgo.MODE_CTR`. +MODE_CTR = 6 +#: OpenPGP Mode. See `blockalgo.MODE_OPENPGP`. +MODE_OPENPGP = 7 +#: Size of a data block (in bytes) +block_size = 16 +#: Size of a key (in bytes) +key_size = ( 16, 24, 32 ) + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/ARC2.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/ARC2.py new file mode 100644 index 0000000..7b5f43a --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/ARC2.py @@ -0,0 +1,130 @@ +# -*- coding: utf-8 -*- +# +# Cipher/ARC2.py : ARC2.py +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== +"""RC2 symmetric cipher + +RC2_ (Rivest's Cipher version 2) is a symmetric block cipher designed +by Ron Rivest in 1987. The cipher started as a proprietary design, +that was reverse engineered and anonymously posted on Usenet in 1996. +For this reason, the algorithm was first called *Alleged* RC2 (ARC2), +since the company that owned RC2 (RSA Data Inc.) did not confirm whether +the details leaked into public domain were really correct. + +The company eventually published its full specification in RFC2268_. + +RC2 has a fixed data block size of 8 bytes. Length of its keys can vary from +8 to 128 bits. One particular property of RC2 is that the actual +cryptographic strength of the key (*effective key length*) can be reduced +via a parameter. + +Even though RC2 is not cryptographically broken, it has not been analyzed as +thoroughly as AES, which is also faster than RC2. + +New designs should not use RC2. + +As an example, encryption can be done as follows: + + >>> from Crypto.Cipher import ARC2 + >>> from Crypto import Random + >>> + >>> key = b'Sixteen byte key' + >>> iv = Random.new().read(ARC2.block_size) + >>> cipher = ARC2.new(key, ARC2.MODE_CFB, iv) + >>> msg = iv + cipher.encrypt(b'Attack at dawn') + +.. _RC2: http://en.wikipedia.org/wiki/RC2 +.. _RFC2268: http://tools.ietf.org/html/rfc2268 + +:undocumented: __revision__, __package__ +""" + +__revision__ = "$Id$" + +from Crypto.Cipher import blockalgo +from Crypto.Cipher import _ARC2 + +class RC2Cipher (blockalgo.BlockAlgo): + """RC2 cipher object""" + + def __init__(self, key, *args, **kwargs): + """Initialize an ARC2 cipher object + + See also `new()` at the module level.""" + blockalgo.BlockAlgo.__init__(self, _ARC2, key, *args, **kwargs) + +def new(key, *args, **kwargs): + """Create a new RC2 cipher + + :Parameters: + key : byte string + The secret key to use in the symmetric cipher. + Its length can vary from 1 to 128 bytes. + :Keywords: + mode : a *MODE_** constant + The chaining mode to use for encryption or decryption. + Default is `MODE_ECB`. + IV : byte string + The initialization vector to use for encryption or decryption. + + It is ignored for `MODE_ECB` and `MODE_CTR`. + + For `MODE_OPENPGP`, IV must be `block_size` bytes long for encryption + and `block_size` +2 bytes for decryption (in the latter case, it is + actually the *encrypted* IV which was prefixed to the ciphertext). + It is mandatory. + + For all other modes, it must be `block_size` bytes longs. It is optional and + when not present it will be given a default value of all zeroes. + counter : callable + (*Only* `MODE_CTR`). A stateful function that returns the next + *counter block*, which is a byte string of `block_size` bytes. + For better performance, use `Crypto.Util.Counter`. + segment_size : integer + (*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext + are segmented in. + It must be a multiple of 8. If 0 or not specified, it will be assumed to be 8. + effective_keylen : integer + Maximum cryptographic strength of the key, in bits. + It can vary from 0 to 1024. The default value is 1024. + + :Return: an `RC2Cipher` object + """ + return RC2Cipher(key, *args, **kwargs) + +#: Electronic Code Book (ECB). See `blockalgo.MODE_ECB`. +MODE_ECB = 1 +#: Cipher-Block Chaining (CBC). See `blockalgo.MODE_CBC`. +MODE_CBC = 2 +#: Cipher FeedBack (CFB). See `blockalgo.MODE_CFB`. +MODE_CFB = 3 +#: This mode should not be used. +MODE_PGP = 4 +#: Output FeedBack (OFB). See `blockalgo.MODE_OFB`. +MODE_OFB = 5 +#: CounTer Mode (CTR). See `blockalgo.MODE_CTR`. +MODE_CTR = 6 +#: OpenPGP Mode. See `blockalgo.MODE_OPENPGP`. +MODE_OPENPGP = 7 +#: Size of a data block (in bytes) +block_size = 8 +#: Size of a key (in bytes) +key_size = xrange(1,16+1) + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/ARC4.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/ARC4.py new file mode 100644 index 0000000..b745e7c --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/ARC4.py @@ -0,0 +1,120 @@ +# -*- coding: utf-8 -*- +# +# Cipher/ARC4.py : ARC4 +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== +"""ARC4 symmetric cipher + +ARC4_ (Alleged RC4) is an implementation of RC4 (Rivest's Cipher version 4), +a symmetric stream cipher designed by Ron Rivest in 1987. + +The cipher started as a proprietary design, that was reverse engineered and +anonymously posted on Usenet in 1994. The company that owns RC4 (RSA Data +Inc.) never confirmed the correctness of the leaked algorithm. + +Unlike RC2, the company has never published the full specification of RC4, +of whom it still holds the trademark. + +ARC4 keys can vary in length from 40 to 2048 bits. + +One problem of ARC4 is that it does not take a nonce or an IV. If it is required +to encrypt multiple messages with the same long-term key, a distinct +independent nonce must be created for each message, and a short-term key must +be derived from the combination of the long-term key and the nonce. +Due to the weak key scheduling algorithm of RC2, the combination must be carried +out with a complex function (e.g. a cryptographic hash) and not by simply +concatenating key and nonce. + +New designs should not use ARC4. A good alternative is AES +(`Crypto.Cipher.AES`) in any of the modes that turn it into a stream cipher (OFB, CFB, or CTR). + +As an example, encryption can be done as follows: + + >>> from Crypto.Cipher import ARC4 + >>> from Crypto.Hash import SHA + >>> from Crypto import Random + >>> + >>> key = b'Very long and confidential key' + >>> nonce = Random.new().read(16) + >>> tempkey = SHA.new(key+nonce).digest() + >>> cipher = ARC4.new(tempkey) + >>> msg = nonce + cipher.encrypt(b'Open the pod bay doors, HAL') + +.. _ARC4: http://en.wikipedia.org/wiki/RC4 + +:undocumented: __revision__, __package__ +""" + +__revision__ = "$Id$" + +from Crypto.Cipher import _ARC4 + +class ARC4Cipher: + """ARC4 cipher object""" + + + def __init__(self, key, *args, **kwargs): + """Initialize an ARC4 cipher object + + See also `new()` at the module level.""" + + self._cipher = _ARC4.new(key, *args, **kwargs) + self.block_size = self._cipher.block_size + self.key_size = self._cipher.key_size + + def encrypt(self, plaintext): + """Encrypt a piece of data. + + :Parameters: + plaintext : byte string + The piece of data to encrypt. It can be of any size. + :Return: the encrypted data (byte string, as long as the + plaintext). + """ + return self._cipher.encrypt(plaintext) + + def decrypt(self, ciphertext): + """Decrypt a piece of data. + + :Parameters: + ciphertext : byte string + The piece of data to decrypt. It can be of any size. + :Return: the decrypted data (byte string, as long as the + ciphertext). + """ + return self._cipher.decrypt(ciphertext) + +def new(key, *args, **kwargs): + """Create a new ARC4 cipher + + :Parameters: + key : byte string + The secret key to use in the symmetric cipher. + It can have any length, with a minimum of 40 bytes. + Its cryptograpic strength is always capped to 2048 bits (256 bytes). + + :Return: an `ARC4Cipher` object + """ + return ARC4Cipher(key, *args, **kwargs) + +#: Size of a data block (in bytes) +block_size = 1 +#: Size of a key (in bytes) +key_size = xrange(1,256+1) + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/Blowfish.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/Blowfish.py new file mode 100644 index 0000000..3f12bea --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/Blowfish.py @@ -0,0 +1,121 @@ +# -*- coding: utf-8 -*- +# +# Cipher/Blowfish.py : Blowfish +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== +"""Blowfish symmetric cipher + +Blowfish_ is a symmetric block cipher designed by Bruce Schneier. + +It has a fixed data block size of 8 bytes and its keys can vary in length +from 32 to 448 bits (4 to 56 bytes). + +Blowfish is deemed secure and it is fast. However, its keys should be chosen +to be big enough to withstand a brute force attack (e.g. at least 16 bytes). + +As an example, encryption can be done as follows: + + >>> from Crypto.Cipher import Blowfish + >>> from Crypto import Random + >>> from struct import pack + >>> + >>> bs = Blowfish.block_size + >>> key = b'An arbitrarily long key' + >>> iv = Random.new().read(bs) + >>> cipher = Blowfish.new(key, Blowfish.MODE_CBC, iv) + >>> plaintext = b'docendo discimus ' + >>> plen = bs - divmod(len(plaintext),bs)[1] + >>> padding = [plen]*plen + >>> padding = pack('b'*plen, *padding) + >>> msg = iv + cipher.encrypt(plaintext + padding) + +.. _Blowfish: http://www.schneier.com/blowfish.html + +:undocumented: __revision__, __package__ +""" + +__revision__ = "$Id$" + +from Crypto.Cipher import blockalgo +from Crypto.Cipher import _Blowfish + +class BlowfishCipher (blockalgo.BlockAlgo): + """Blowfish cipher object""" + + def __init__(self, key, *args, **kwargs): + """Initialize a Blowfish cipher object + + See also `new()` at the module level.""" + blockalgo.BlockAlgo.__init__(self, _Blowfish, key, *args, **kwargs) + +def new(key, *args, **kwargs): + """Create a new Blowfish cipher + + :Parameters: + key : byte string + The secret key to use in the symmetric cipher. + Its length can vary from 4 to 56 bytes. + :Keywords: + mode : a *MODE_** constant + The chaining mode to use for encryption or decryption. + Default is `MODE_ECB`. + IV : byte string + The initialization vector to use for encryption or decryption. + + It is ignored for `MODE_ECB` and `MODE_CTR`. + + For `MODE_OPENPGP`, IV must be `block_size` bytes long for encryption + and `block_size` +2 bytes for decryption (in the latter case, it is + actually the *encrypted* IV which was prefixed to the ciphertext). + It is mandatory. + + For all other modes, it must be `block_size` bytes longs. It is optional and + when not present it will be given a default value of all zeroes. + counter : callable + (*Only* `MODE_CTR`). A stateful function that returns the next + *counter block*, which is a byte string of `block_size` bytes. + For better performance, use `Crypto.Util.Counter`. + segment_size : integer + (*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext + are segmented in. + It must be a multiple of 8. If 0 or not specified, it will be assumed to be 8. + + :Return: a `BlowfishCipher` object + """ + return BlowfishCipher(key, *args, **kwargs) + +#: Electronic Code Book (ECB). See `blockalgo.MODE_ECB`. +MODE_ECB = 1 +#: Cipher-Block Chaining (CBC). See `blockalgo.MODE_CBC`. +MODE_CBC = 2 +#: Cipher FeedBack (CFB). See `blockalgo.MODE_CFB`. +MODE_CFB = 3 +#: This mode should not be used. +MODE_PGP = 4 +#: Output FeedBack (OFB). See `blockalgo.MODE_OFB`. +MODE_OFB = 5 +#: CounTer Mode (CTR). See `blockalgo.MODE_CTR`. +MODE_CTR = 6 +#: OpenPGP Mode. See `blockalgo.MODE_OPENPGP`. +MODE_OPENPGP = 7 +#: Size of a data block (in bytes) +block_size = 8 +#: Size of a key (in bytes) +key_size = xrange(4,56+1) + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/CAST.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/CAST.py new file mode 100644 index 0000000..f08dab3 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/CAST.py @@ -0,0 +1,123 @@ +# -*- coding: utf-8 -*- +# +# Cipher/CAST.py : CAST +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== +"""CAST-128 symmetric cipher + +CAST-128_ (or CAST5) is a symmetric block cipher specified in RFC2144_. + +It has a fixed data block size of 8 bytes. Its key can vary in length +from 40 to 128 bits. + +CAST is deemed to be cryptographically secure, but its usage is not widespread. +Keys of sufficient length should be used to prevent brute force attacks +(128 bits are recommended). + +As an example, encryption can be done as follows: + + >>> from Crypto.Cipher import CAST + >>> from Crypto import Random + >>> + >>> key = b'Sixteen byte key' + >>> iv = Random.new().read(CAST.block_size) + >>> cipher = CAST.new(key, CAST.MODE_OPENPGP, iv) + >>> plaintext = b'sona si latine loqueris ' + >>> msg = cipher.encrypt(plaintext) + >>> + ... + >>> eiv = msg[:CAST.block_size+2] + >>> ciphertext = msg[CAST.block_size+2:] + >>> cipher = CAST.new(key, CAST.MODE_OPENPGP, eiv) + >>> print cipher.decrypt(ciphertext) + +.. _CAST-128: http://en.wikipedia.org/wiki/CAST-128 +.. _RFC2144: http://tools.ietf.org/html/rfc2144 + +:undocumented: __revision__, __package__ +""" + +__revision__ = "$Id$" + +from Crypto.Cipher import blockalgo +from Crypto.Cipher import _CAST + +class CAST128Cipher(blockalgo.BlockAlgo): + """CAST-128 cipher object""" + + def __init__(self, key, *args, **kwargs): + """Initialize a CAST-128 cipher object + + See also `new()` at the module level.""" + blockalgo.BlockAlgo.__init__(self, _CAST, key, *args, **kwargs) + +def new(key, *args, **kwargs): + """Create a new CAST-128 cipher + + :Parameters: + key : byte string + The secret key to use in the symmetric cipher. + Its length may vary from 5 to 16 bytes. + :Keywords: + mode : a *MODE_** constant + The chaining mode to use for encryption or decryption. + Default is `MODE_ECB`. + IV : byte string + The initialization vector to use for encryption or decryption. + + It is ignored for `MODE_ECB` and `MODE_CTR`. + + For `MODE_OPENPGP`, IV must be `block_size` bytes long for encryption + and `block_size` +2 bytes for decryption (in the latter case, it is + actually the *encrypted* IV which was prefixed to the ciphertext). + It is mandatory. + + For all other modes, it must be `block_size` bytes longs. It is optional and + when not present it will be given a default value of all zeroes. + counter : callable + (*Only* `MODE_CTR`). A stateful function that returns the next + *counter block*, which is a byte string of `block_size` bytes. + For better performance, use `Crypto.Util.Counter`. + segment_size : integer + (*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext + are segmented in. + It must be a multiple of 8. If 0 or not specified, it will be assumed to be 8. + + :Return: an `CAST128Cipher` object + """ + return CAST128Cipher(key, *args, **kwargs) + +#: Electronic Code Book (ECB). See `blockalgo.MODE_ECB`. +MODE_ECB = 1 +#: Cipher-Block Chaining (CBC). See `blockalgo.MODE_CBC`. +MODE_CBC = 2 +#: Cipher FeedBack (CFB). See `blockalgo.MODE_CFB`. +MODE_CFB = 3 +#: This mode should not be used. +MODE_PGP = 4 +#: Output FeedBack (OFB). See `blockalgo.MODE_OFB`. +MODE_OFB = 5 +#: CounTer Mode (CTR). See `blockalgo.MODE_CTR`. +MODE_CTR = 6 +#: OpenPGP Mode. See `blockalgo.MODE_OPENPGP`. +MODE_OPENPGP = 7 +#: Size of a data block (in bytes) +block_size = 8 +#: Size of a key (in bytes) +key_size = xrange(5,16+1) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/DES.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/DES.py new file mode 100644 index 0000000..2fae42f --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/DES.py @@ -0,0 +1,118 @@ +# -*- coding: utf-8 -*- +# +# Cipher/DES.py : DES +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== +"""DES symmetric cipher + +DES `(Data Encryption Standard)`__ is a symmetric block cipher standardized +by NIST_ . It has a fixed data block size of 8 bytes. +Its keys are 64 bits long, even though 8 bits were used for integrity (now they +are ignored) and do not contribute to securty. + +DES is cryptographically secure, but its key length is too short by nowadays +standards and it could be brute forced with some effort. + +DES should not be used for new designs. Use `AES`. + +As an example, encryption can be done as follows: + + >>> from Crypto.Cipher import DES3 + >>> from Crypto import Random + >>> + >>> key = b'Sixteen byte key' + >>> iv = Random.new().read(DES3.block_size) + >>> cipher = DES3.new(key, DES3.MODE_OFB, iv) + >>> plaintext = b'sona si latine loqueris ' + >>> msg = iv + cipher.encrypt(plaintext) + +.. __: http://en.wikipedia.org/wiki/Data_Encryption_Standard +.. _NIST: http://csrc.nist.gov/publications/fips/fips46-3/fips46-3.pdf + +:undocumented: __revision__, __package__ +""" + +__revision__ = "$Id$" + +from Crypto.Cipher import blockalgo +from Crypto.Cipher import _DES + +class DESCipher(blockalgo.BlockAlgo): + """DES cipher object""" + + def __init__(self, key, *args, **kwargs): + """Initialize a DES cipher object + + See also `new()` at the module level.""" + blockalgo.BlockAlgo.__init__(self, _DES, key, *args, **kwargs) + +def new(key, *args, **kwargs): + """Create a new DES cipher + + :Parameters: + key : byte string + The secret key to use in the symmetric cipher. + It must be 8 byte long. The parity bits will be ignored. + :Keywords: + mode : a *MODE_** constant + The chaining mode to use for encryption or decryption. + Default is `MODE_ECB`. + IV : byte string + The initialization vector to use for encryption or decryption. + + It is ignored for `MODE_ECB` and `MODE_CTR`. + + For `MODE_OPENPGP`, IV must be `block_size` bytes long for encryption + and `block_size` +2 bytes for decryption (in the latter case, it is + actually the *encrypted* IV which was prefixed to the ciphertext). + It is mandatory. + + For all other modes, it must be `block_size` bytes longs. It is optional and + when not present it will be given a default value of all zeroes. + counter : callable + (*Only* `MODE_CTR`). A stateful function that returns the next + *counter block*, which is a byte string of `block_size` bytes. + For better performance, use `Crypto.Util.Counter`. + segment_size : integer + (*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext + are segmented in. + It must be a multiple of 8. If 0 or not specified, it will be assumed to be 8. + + :Return: an `DESCipher` object + """ + return DESCipher(key, *args, **kwargs) + +#: Electronic Code Book (ECB). See `blockalgo.MODE_ECB`. +MODE_ECB = 1 +#: Cipher-Block Chaining (CBC). See `blockalgo.MODE_CBC`. +MODE_CBC = 2 +#: Cipher FeedBack (CFB). See `blockalgo.MODE_CFB`. +MODE_CFB = 3 +#: This mode should not be used. +MODE_PGP = 4 +#: Output FeedBack (OFB). See `blockalgo.MODE_OFB`. +MODE_OFB = 5 +#: CounTer Mode (CTR). See `blockalgo.MODE_CTR`. +MODE_CTR = 6 +#: OpenPGP Mode. See `blockalgo.MODE_OPENPGP`. +MODE_OPENPGP = 7 +#: Size of a data block (in bytes) +block_size = 8 +#: Size of a key (in bytes) +key_size = 8 diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/DES3.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/DES3.py new file mode 100644 index 0000000..7fedac8 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/DES3.py @@ -0,0 +1,133 @@ +# -*- coding: utf-8 -*- +# +# Cipher/DES3.py : DES3 +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== +"""Triple DES symmetric cipher + +`Triple DES`__ (or TDES or TDEA or 3DES) is a symmetric block cipher standardized by NIST_. +It has a fixed data block size of 8 bytes. Its keys are 128 (*Option 1*) or 192 +bits (*Option 2*) long. +However, 1 out of 8 bits is used for redundancy and do not contribute to +security. The effective key length is respectively 112 or 168 bits. + +TDES consists of the concatenation of 3 simple `DES` ciphers. + +The plaintext is first DES encrypted with *K1*, then decrypted with *K2*, +and finally encrypted again with *K3*. The ciphertext is decrypted in the reverse manner. + +The 192 bit key is a bundle of three 64 bit independent subkeys: *K1*, *K2*, and *K3*. + +The 128 bit key is split into *K1* and *K2*, whereas *K1=K3*. + +It is important that all subkeys are different, otherwise TDES would degrade to +single `DES`. + +TDES is cryptographically secure, even though it is neither as secure nor as fast +as `AES`. + +As an example, encryption can be done as follows: + + >>> from Crypto.Cipher import DES + >>> from Crypto import Random + >>> from Crypto.Util import Counter + >>> + >>> key = b'-8B key-' + >>> nonce = Random.new().read(DES.block_size/2) + >>> ctr = Counter.new(DES.block_size*8/2, prefix=nonce) + >>> cipher = DES.new(key, DES.MODE_CTR, counter=ctr) + >>> plaintext = b'We are no longer the knights who say ni!' + >>> msg = nonce + cipher.encrypt(plaintext) + +.. __: http://en.wikipedia.org/wiki/Triple_DES +.. _NIST: http://csrc.nist.gov/publications/nistpubs/800-67/SP800-67.pdf + +:undocumented: __revision__, __package__ +""" + +__revision__ = "$Id$" + +from Crypto.Cipher import blockalgo +from Crypto.Cipher import _DES3 + +class DES3Cipher(blockalgo.BlockAlgo): + """TDES cipher object""" + + def __init__(self, key, *args, **kwargs): + """Initialize a TDES cipher object + + See also `new()` at the module level.""" + blockalgo.BlockAlgo.__init__(self, _DES3, key, *args, **kwargs) + +def new(key, *args, **kwargs): + """Create a new TDES cipher + + :Parameters: + key : byte string + The secret key to use in the symmetric cipher. + It must be 16 or 24 bytes long. The parity bits will be ignored. + :Keywords: + mode : a *MODE_** constant + The chaining mode to use for encryption or decryption. + Default is `MODE_ECB`. + IV : byte string + The initialization vector to use for encryption or decryption. + + It is ignored for `MODE_ECB` and `MODE_CTR`. + + For `MODE_OPENPGP`, IV must be `block_size` bytes long for encryption + and `block_size` +2 bytes for decryption (in the latter case, it is + actually the *encrypted* IV which was prefixed to the ciphertext). + It is mandatory. + + For all other modes, it must be `block_size` bytes longs. It is optional and + when not present it will be given a default value of all zeroes. + counter : callable + (*Only* `MODE_CTR`). A stateful function that returns the next + *counter block*, which is a byte string of `block_size` bytes. + For better performance, use `Crypto.Util.Counter`. + segment_size : integer + (*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext + are segmented in. + It must be a multiple of 8. If 0 or not specified, it will be assumed to be 8. + + :Attention: it is important that all 8 byte subkeys are different, + otherwise TDES would degrade to single `DES`. + :Return: an `DES3Cipher` object + """ + return DES3Cipher(key, *args, **kwargs) + +#: Electronic Code Book (ECB). See `blockalgo.MODE_ECB`. +MODE_ECB = 1 +#: Cipher-Block Chaining (CBC). See `blockalgo.MODE_CBC`. +MODE_CBC = 2 +#: Cipher FeedBack (CFB). See `blockalgo.MODE_CFB`. +MODE_CFB = 3 +#: This mode should not be used. +MODE_PGP = 4 +#: Output FeedBack (OFB). See `blockalgo.MODE_OFB`. +MODE_OFB = 5 +#: CounTer Mode (CTR). See `blockalgo.MODE_CTR`. +MODE_CTR = 6 +#: OpenPGP Mode. See `blockalgo.MODE_OPENPGP`. +MODE_OPENPGP = 7 +#: Size of a data block (in bytes) +block_size = 8 +#: Size of a key (in bytes) +key_size = ( 16, 24 ) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/PKCS1_OAEP.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/PKCS1_OAEP.py new file mode 100644 index 0000000..9afe176 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/PKCS1_OAEP.py @@ -0,0 +1,255 @@ +# -*- coding: utf-8 -*- +# +# Cipher/PKCS1_OAEP.py : PKCS#1 OAEP +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""RSA encryption protocol according to PKCS#1 OAEP + +See RFC3447__ or the `original RSA Labs specification`__ . + +This scheme is more properly called ``RSAES-OAEP``. + +As an example, a sender may encrypt a message in this way: + + >>> from Crypto.Cipher import PKCS1_OAEP + >>> from Crypto.PublicKey import RSA + >>> + >>> message = 'To be encrypted' + >>> key = RSA.importKey(open('pubkey.der').read()) + >>> cipher = PKCS1_OAEP.new(key) + >>> ciphertext = cipher.encrypt(message) + +At the receiver side, decryption can be done using the private part of +the RSA key: + + >>> key = RSA.importKey(open('privkey.der').read()) + >>> cipher = PKCS1_OAP.new(key) + >>> message = cipher.decrypt(ciphertext) + +:undocumented: __revision__, __package__ + +.. __: http://www.ietf.org/rfc/rfc3447.txt +.. __: http://www.rsa.com/rsalabs/node.asp?id=2125. +""" + +from __future__ import nested_scopes + +__revision__ = "$Id$" +__all__ = [ 'new', 'PKCS1OAEP_Cipher' ] + +import Crypto.Signature.PKCS1_PSS +import Crypto.Hash.SHA + +from Crypto.Util.py3compat import * +import Crypto.Util.number +from Crypto.Util.number import ceil_div +from Crypto.Util.strxor import strxor + +class PKCS1OAEP_Cipher: + """This cipher can perform PKCS#1 v1.5 OAEP encryption or decryption.""" + + def __init__(self, key, hashAlgo, mgfunc, label): + """Initialize this PKCS#1 OAEP cipher object. + + :Parameters: + key : an RSA key object + If a private half is given, both encryption and decryption are possible. + If a public half is given, only encryption is possible. + hashAlgo : hash object + The hash function to use. This can be a module under `Crypto.Hash` + or an existing hash object created from any of such modules. If not specified, + `Crypto.Hash.SHA` (that is, SHA-1) is used. + mgfunc : callable + A mask generation function that accepts two parameters: a string to + use as seed, and the lenth of the mask to generate, in bytes. + If not specified, the standard MGF1 is used (a safe choice). + label : string + A label to apply to this particular encryption. If not specified, + an empty string is used. Specifying a label does not improve + security. + + :attention: Modify the mask generation function only if you know what you are doing. + Sender and receiver must use the same one. + """ + self._key = key + + if hashAlgo: + self._hashObj = hashAlgo + else: + self._hashObj = Crypto.Hash.SHA + + if mgfunc: + self._mgf = mgfunc + else: + self._mgf = lambda x,y: Crypto.Signature.PKCS1_PSS.MGF1(x,y,self._hashObj) + + self._label = label + + def can_encrypt(self): + """Return True/1 if this cipher object can be used for encryption.""" + return self._key.can_encrypt() + + def can_decrypt(self): + """Return True/1 if this cipher object can be used for decryption.""" + return self._key.can_decrypt() + + def encrypt(self, message): + """Produce the PKCS#1 OAEP encryption of a message. + + This function is named ``RSAES-OAEP-ENCRYPT``, and is specified in + section 7.1.1 of RFC3447. + + :Parameters: + message : string + The message to encrypt, also known as plaintext. It can be of + variable length, but not longer than the RSA modulus (in bytes) + minus 2, minus twice the hash output size. + + :Return: A string, the ciphertext in which the message is encrypted. + It is as long as the RSA modulus (in bytes). + :Raise ValueError: + If the RSA key length is not sufficiently long to deal with the given + message. + """ + # TODO: Verify the key is RSA + + randFunc = self._key._randfunc + + # See 7.1.1 in RFC3447 + modBits = Crypto.Util.number.size(self._key.n) + k = ceil_div(modBits,8) # Convert from bits to bytes + hLen = self._hashObj.digest_size + mLen = len(message) + + # Step 1b + ps_len = k-mLen-2*hLen-2 + if ps_len<0: + raise ValueError("Plaintext is too long.") + # Step 2a + lHash = self._hashObj.new(self._label).digest() + # Step 2b + ps = bchr(0x00)*ps_len + # Step 2c + db = lHash + ps + bchr(0x01) + message + # Step 2d + ros = randFunc(hLen) + # Step 2e + dbMask = self._mgf(ros, k-hLen-1) + # Step 2f + maskedDB = strxor(db, dbMask) + # Step 2g + seedMask = self._mgf(maskedDB, hLen) + # Step 2h + maskedSeed = strxor(ros, seedMask) + # Step 2i + em = bchr(0x00) + maskedSeed + maskedDB + # Step 3a (OS2IP), step 3b (RSAEP), part of step 3c (I2OSP) + m = self._key.encrypt(em, 0)[0] + # Complete step 3c (I2OSP) + c = bchr(0x00)*(k-len(m)) + m + return c + + def decrypt(self, ct): + """Decrypt a PKCS#1 OAEP ciphertext. + + This function is named ``RSAES-OAEP-DECRYPT``, and is specified in + section 7.1.2 of RFC3447. + + :Parameters: + ct : string + The ciphertext that contains the message to recover. + + :Return: A string, the original message. + :Raise ValueError: + If the ciphertext length is incorrect, or if the decryption does not + succeed. + :Raise TypeError: + If the RSA key has no private half. + """ + # TODO: Verify the key is RSA + + # See 7.1.2 in RFC3447 + modBits = Crypto.Util.number.size(self._key.n) + k = ceil_div(modBits,8) # Convert from bits to bytes + hLen = self._hashObj.digest_size + + # Step 1b and 1c + if len(ct) != k or k>> from Crypto.Cipher import PKCS1_v1_5 + >>> from Crypto.PublicKey import RSA + >>> from Crypto.Hash import SHA + >>> + >>> message = 'To be encrypted' + >>> h = SHA.new(message) + >>> + >>> key = RSA.importKey(open('pubkey.der').read()) + >>> cipher = PKCS1_v1_5.new(key) + >>> ciphertext = cipher.encrypt(message+h.digest()) + +At the receiver side, decryption can be done using the private part of +the RSA key: + + >>> From Crypto.Hash import SHA + >>> from Crypto import Random + >>> + >>> key = RSA.importKey(open('privkey.der').read()) + >>> + >>> dsize = SHA.digest_size + >>> sentinel = Random.new().read(15+dsize) # Let's assume that average data length is 15 + >>> + >>> cipher = PKCS1_v1_5.new(key) + >>> message = cipher.decrypt(ciphertext, sentinel) + >>> + >>> digest = SHA.new(message[:-dsize]).digest() + >>> if digest==message[-dsize:]: # Note how we DO NOT look for the sentinel + >>> print "Encryption was correct." + >>> else: + >>> print "Encryption was not correct." + +:undocumented: __revision__, __package__ + +.. __: http://www.ietf.org/rfc/rfc3447.txt +.. __: http://www.rsa.com/rsalabs/node.asp?id=2125. +""" + +__revision__ = "$Id$" +__all__ = [ 'new', 'PKCS115_Cipher' ] + +from Crypto.Util.number import ceil_div +from Crypto.Util.py3compat import * +import Crypto.Util.number + +class PKCS115_Cipher: + """This cipher can perform PKCS#1 v1.5 RSA encryption or decryption.""" + + def __init__(self, key): + """Initialize this PKCS#1 v1.5 cipher object. + + :Parameters: + key : an RSA key object + If a private half is given, both encryption and decryption are possible. + If a public half is given, only encryption is possible. + """ + self._key = key + + def can_encrypt(self): + """Return True if this cipher object can be used for encryption.""" + return self._key.can_encrypt() + + def can_decrypt(self): + """Return True if this cipher object can be used for decryption.""" + return self._key.can_decrypt() + + def encrypt(self, message): + """Produce the PKCS#1 v1.5 encryption of a message. + + This function is named ``RSAES-PKCS1-V1_5-ENCRYPT``, and is specified in + section 7.2.1 of RFC3447. + For a complete example see `Crypto.Cipher.PKCS1_v1_5`. + + :Parameters: + message : byte string + The message to encrypt, also known as plaintext. It can be of + variable length, but not longer than the RSA modulus (in bytes) minus 11. + + :Return: A byte string, the ciphertext in which the message is encrypted. + It is as long as the RSA modulus (in bytes). + :Raise ValueError: + If the RSA key length is not sufficiently long to deal with the given + message. + + """ + # TODO: Verify the key is RSA + + randFunc = self._key._randfunc + + # See 7.2.1 in RFC3447 + modBits = Crypto.Util.number.size(self._key.n) + k = ceil_div(modBits,8) # Convert from bits to bytes + mLen = len(message) + + # Step 1 + if mLen > k-11: + raise ValueError("Plaintext is too long.") + # Step 2a + class nonZeroRandByte: + def __init__(self, rf): self.rf=rf + def __call__(self, c): + while bord(c)==0x00: c=self.rf(1)[0] + return c + ps = tobytes(map(nonZeroRandByte(randFunc), randFunc(k-mLen-3))) + # Step 2b + em = b('\x00\x02') + ps + bchr(0x00) + message + # Step 3a (OS2IP), step 3b (RSAEP), part of step 3c (I2OSP) + m = self._key.encrypt(em, 0)[0] + # Complete step 3c (I2OSP) + c = bchr(0x00)*(k-len(m)) + m + return c + + def decrypt(self, ct, sentinel): + """Decrypt a PKCS#1 v1.5 ciphertext. + + This function is named ``RSAES-PKCS1-V1_5-DECRYPT``, and is specified in + section 7.2.2 of RFC3447. + For a complete example see `Crypto.Cipher.PKCS1_v1_5`. + + :Parameters: + ct : byte string + The ciphertext that contains the message to recover. + sentinel : any type + The object to return to indicate that an error was detected during decryption. + + :Return: A byte string. It is either the original message or the ``sentinel`` (in case of an error). + :Raise ValueError: + If the ciphertext length is incorrect + :Raise TypeError: + If the RSA key has no private half. + + :attention: + You should **never** let the party who submitted the ciphertext know that + this function returned the ``sentinel`` value. + Armed with such knowledge (for a fair amount of carefully crafted but invalid ciphertexts), + an attacker is able to recontruct the plaintext of any other encryption that were carried out + with the same RSA public key (see `Bleichenbacher's`__ attack). + + In general, it should not be possible for the other party to distinguish + whether processing at the server side failed because the value returned + was a ``sentinel`` as opposed to a random, invalid message. + + In fact, the second option is not that unlikely: encryption done according to PKCS#1 v1.5 + embeds no good integrity check. There is roughly one chance + in 2^16 for a random ciphertext to be returned as a valid message + (although random looking). + + It is therefore advisabled to: + + 1. Select as ``sentinel`` a value that resembles a plausable random, invalid message. + 2. Not report back an error as soon as you detect a ``sentinel`` value. + Put differently, you should not explicitly check if the returned value is the ``sentinel`` or not. + 3. Cover all possible errors with a single, generic error indicator. + 4. Embed into the definition of ``message`` (at the protocol level) a digest (e.g. ``SHA-1``). + It is recommended for it to be the rightmost part ``message``. + 5. Where possible, monitor the number of errors due to ciphertexts originating from the same party, + and slow down the rate of the requests from such party (or even blacklist it altogether). + + **If you are designing a new protocol, consider using the more robust PKCS#1 OAEP.** + + .. __: http://www.bell-labs.com/user/bleichen/papers/pkcs.ps + + """ + + # TODO: Verify the key is RSA + + # See 7.2.1 in RFC3447 + modBits = Crypto.Util.number.size(self._key.n) + k = ceil_div(modBits,8) # Convert from bits to bytes + + # Step 1 + if len(ct) != k: + raise ValueError("Ciphertext with incorrect length.") + # Step 2a (O2SIP), 2b (RSADP), and part of 2c (I2OSP) + m = self._key.decrypt(ct) + # Complete step 2c (I2OSP) + em = bchr(0x00)*(k-len(m)) + m + # Step 3 + sep = em.find(bchr(0x00),2) + if not em.startswith(b('\x00\x02')) or sep<10: + return sentinel + # Step 4 + return em[sep+1:] + +def new(key): + """Return a cipher object `PKCS115_Cipher` that can be used to perform PKCS#1 v1.5 encryption or decryption. + + :Parameters: + key : RSA key object + The key to use to encrypt or decrypt the message. This is a `Crypto.PublicKey.RSA` object. + Decryption is only possible if *key* is a private RSA key. + + """ + return PKCS115_Cipher(key) + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/XOR.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/XOR.py new file mode 100644 index 0000000..26ec1b1 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/XOR.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +# +# Cipher/XOR.py : XOR +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== +"""XOR toy cipher + +XOR is one the simplest stream ciphers. Encryption and decryption are +performed by XOR-ing data with a keystream made by contatenating +the key. + +Do not use it for real applications! + +:undocumented: __revision__, __package__ +""" + +__revision__ = "$Id$" + +from Crypto.Cipher import _XOR + +class XORCipher: + """XOR cipher object""" + + def __init__(self, key, *args, **kwargs): + """Initialize a XOR cipher object + + See also `new()` at the module level.""" + self._cipher = _XOR.new(key, *args, **kwargs) + self.block_size = self._cipher.block_size + self.key_size = self._cipher.key_size + + def encrypt(self, plaintext): + """Encrypt a piece of data. + + :Parameters: + plaintext : byte string + The piece of data to encrypt. It can be of any size. + :Return: the encrypted data (byte string, as long as the + plaintext). + """ + return self._cipher.encrypt(plaintext) + + def decrypt(self, ciphertext): + """Decrypt a piece of data. + + :Parameters: + ciphertext : byte string + The piece of data to decrypt. It can be of any size. + :Return: the decrypted data (byte string, as long as the + ciphertext). + """ + return self._cipher.decrypt(ciphertext) + +def new(key, *args, **kwargs): + """Create a new XOR cipher + + :Parameters: + key : byte string + The secret key to use in the symmetric cipher. + Its length may vary from 1 to 32 bytes. + + :Return: an `XORCipher` object + """ + return XORCipher(key, *args, **kwargs) + +#: Size of a data block (in bytes) +block_size = 1 +#: Size of a key (in bytes) +key_size = xrange(1,32+1) + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/_AES.so b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/_AES.so new file mode 100755 index 0000000..846e556 Binary files /dev/null and b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/_AES.so differ diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/_ARC2.so b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/_ARC2.so new file mode 100755 index 0000000..b46d97f Binary files /dev/null and b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/_ARC2.so differ diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/_ARC4.so b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/_ARC4.so new file mode 100755 index 0000000..c13bdc4 Binary files /dev/null and b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/_ARC4.so differ diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/_Blowfish.so b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/_Blowfish.so new file mode 100755 index 0000000..ff8d9f9 Binary files /dev/null and b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/_Blowfish.so differ diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/_CAST.so b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/_CAST.so new file mode 100755 index 0000000..f4ff77d Binary files /dev/null and b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/_CAST.so differ diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/_DES.so b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/_DES.so new file mode 100755 index 0000000..914eccd Binary files /dev/null and b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/_DES.so differ diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/_DES3.so b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/_DES3.so new file mode 100755 index 0000000..7cba0fa Binary files /dev/null and b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/_DES3.so differ diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/_XOR.so b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/_XOR.so new file mode 100755 index 0000000..1c04f63 Binary files /dev/null and b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/_XOR.so differ diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/__init__.py new file mode 100644 index 0000000..7afed2d --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/__init__.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Symmetric- and asymmetric-key encryption algorithms. + +Encryption algorithms transform plaintext in some way that +is dependent on a key or key pair, producing ciphertext. + +Symmetric algorithms +-------------------- + +Encryption can easily be reversed, if (and, hopefully, only if) +one knows the same key. +In other words, sender and receiver share the same key. + +The symmetric encryption modules here all support the interface described in PEP +272, "API for Block Encryption Algorithms". + +If you don't know which algorithm to choose, use AES because it's +standard and has undergone a fair bit of examination. + +======================== ======= ======================== +Module name Type Description +======================== ======= ======================== +`Crypto.Cipher.AES` Block Advanced Encryption Standard +`Crypto.Cipher.ARC2` Block Alleged RC2 +`Crypto.Cipher.ARC4` Stream Alleged RC4 +`Crypto.Cipher.Blowfish` Block Blowfish +`Crypto.Cipher.CAST` Block CAST +`Crypto.Cipher.DES` Block The Data Encryption Standard. + Very commonly used in the past, + but today its 56-bit keys are too small. +`Crypto.Cipher.DES3` Block Triple DES. +`Crypto.Cipher.XOR` Stream The simple XOR cipher. +======================== ======= ======================== + + +Asymmetric algorithms +--------------------- + +For asymmetric algorithms, the key to be used for decryption is totally +different and cannot be derived in a feasible way from the key used +for encryption. Put differently, sender and receiver each own one half +of a key pair. The encryption key is often called ``public`` whereas +the decryption key is called ``private``. + +========================== ======================= +Module name Description +========================== ======================= +`Crypto.Cipher.PKCS1_v1_5` PKCS#1 v1.5 encryption, based on RSA key pairs +`Crypto.Cipher.PKCS1_OAEP` PKCS#1 OAEP encryption, based on RSA key pairs +========================== ======================= + +:undocumented: __revision__, __package__, _AES, _ARC2, _ARC4, _Blowfish + _CAST, _DES, _DES3, _XOR +""" + +__all__ = ['AES', 'ARC2', 'ARC4', + 'Blowfish', 'CAST', 'DES', 'DES3', + 'XOR', + 'PKCS1_v1_5', 'PKCS1_OAEP' + ] + +__revision__ = "$Id$" + + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/blockalgo.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/blockalgo.py new file mode 100644 index 0000000..dd183dc --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Cipher/blockalgo.py @@ -0,0 +1,296 @@ +# -*- coding: utf-8 -*- +# +# Cipher/blockalgo.py +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== +"""Module with definitions common to all block ciphers.""" + +import sys +if sys.version_info[0] == 2 and sys.version_info[1] == 1: + from Crypto.Util.py21compat import * +from Crypto.Util.py3compat import * + +#: *Electronic Code Book (ECB)*. +#: This is the simplest encryption mode. Each of the plaintext blocks +#: is directly encrypted into a ciphertext block, independently of +#: any other block. This mode exposes frequency of symbols +#: in your plaintext. Other modes (e.g. *CBC*) should be used instead. +#: +#: See `NIST SP800-38A`_ , Section 6.1 . +#: +#: .. _`NIST SP800-38A` : http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf +MODE_ECB = 1 + +#: *Cipher-Block Chaining (CBC)*. Each of the ciphertext blocks depends +#: on the current and all previous plaintext blocks. An Initialization Vector +#: (*IV*) is required. +#: +#: The *IV* is a data block to be transmitted to the receiver. +#: The *IV* can be made public, but it must be authenticated by the receiver and +#: it should be picked randomly. +#: +#: See `NIST SP800-38A`_ , Section 6.2 . +#: +#: .. _`NIST SP800-38A` : http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf +MODE_CBC = 2 + +#: *Cipher FeedBack (CFB)*. This mode is similar to CBC, but it transforms +#: the underlying block cipher into a stream cipher. Plaintext and ciphertext +#: are processed in *segments* of **s** bits. The mode is therefore sometimes +#: labelled **s**-bit CFB. An Initialization Vector (*IV*) is required. +#: +#: When encrypting, each ciphertext segment contributes to the encryption of +#: the next plaintext segment. +#: +#: This *IV* is a data block to be transmitted to the receiver. +#: The *IV* can be made public, but it should be picked randomly. +#: Reusing the same *IV* for encryptions done with the same key lead to +#: catastrophic cryptographic failures. +#: +#: See `NIST SP800-38A`_ , Section 6.3 . +#: +#: .. _`NIST SP800-38A` : http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf +MODE_CFB = 3 + +#: This mode should not be used. +MODE_PGP = 4 + +#: *Output FeedBack (OFB)*. This mode is very similar to CBC, but it +#: transforms the underlying block cipher into a stream cipher. +#: The keystream is the iterated block encryption of an Initialization Vector (*IV*). +#: +#: The *IV* is a data block to be transmitted to the receiver. +#: The *IV* can be made public, but it should be picked randomly. +#: +#: Reusing the same *IV* for encryptions done with the same key lead to +#: catastrophic cryptograhic failures. +#: +#: See `NIST SP800-38A`_ , Section 6.4 . +#: +#: .. _`NIST SP800-38A` : http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf +MODE_OFB = 5 + +#: *CounTeR (CTR)*. This mode is very similar to ECB, in that +#: encryption of one block is done independently of all other blocks. +#: Unlike ECB, the block *position* contributes to the encryption and no +#: information leaks about symbol frequency. +#: +#: Each message block is associated to a *counter* which must be unique +#: across all messages that get encrypted with the same key (not just within +#: the same message). The counter is as big as the block size. +#: +#: Counters can be generated in several ways. The most straightword one is +#: to choose an *initial counter block* (which can be made public, similarly +#: to the *IV* for the other modes) and increment its lowest **m** bits by +#: one (modulo *2^m*) for each block. In most cases, **m** is chosen to be half +#: the block size. +#: +#: Reusing the same *initial counter block* for encryptions done with the same +#: key lead to catastrophic cryptograhic failures. +#: +#: See `NIST SP800-38A`_ , Section 6.5 (for the mode) and Appendix B (for how +#: to manage the *initial counter block*). +#: +#: .. _`NIST SP800-38A` : http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf +MODE_CTR = 6 + +#: OpenPGP. This mode is a variant of CFB, and it is only used in PGP and OpenPGP_ applications. +#: An Initialization Vector (*IV*) is required. +#: +#: Unlike CFB, the IV is not transmitted to the receiver. Instead, the *encrypted* IV is. +#: The IV is a random data block. Two of its bytes are duplicated to act as a checksum +#: for the correctness of the key. The encrypted IV is therefore 2 bytes longer than +#: the clean IV. +#: +#: .. _OpenPGP: http://tools.ietf.org/html/rfc4880 +MODE_OPENPGP = 7 + +def _getParameter(name, index, args, kwargs, default=None): + """Find a parameter in tuple and dictionary arguments a function receives""" + param = kwargs.get(name) + if len(args)>index: + if param: + raise ValueError("Parameter '%s' is specified twice" % name) + param = args[index] + return param or default + +class BlockAlgo: + """Class modelling an abstract block cipher.""" + + def __init__(self, factory, key, *args, **kwargs): + self.mode = _getParameter('mode', 0, args, kwargs, default=MODE_ECB) + self.block_size = factory.block_size + + if self.mode != MODE_OPENPGP: + self._cipher = factory.new(key, *args, **kwargs) + self.IV = self._cipher.IV + else: + # OPENPGP mode. For details, see 13.9 in RCC4880. + # + # A few members are specifically created for this mode: + # - _encrypted_iv, set in this constructor + # - _done_first_block, set to True after the first encryption + # - _done_last_block, set to True after a partial block is processed + + self._done_first_block = False + self._done_last_block = False + self.IV = _getParameter('iv', 1, args, kwargs) + if not self.IV: + raise ValueError("MODE_OPENPGP requires an IV") + + # Instantiate a temporary cipher to process the IV + IV_cipher = factory.new(key, MODE_CFB, + b('\x00')*self.block_size, # IV for CFB + segment_size=self.block_size*8) + + # The cipher will be used for... + if len(self.IV) == self.block_size: + # ... encryption + self._encrypted_IV = IV_cipher.encrypt( + self.IV + self.IV[-2:] + # Plaintext + b('\x00')*(self.block_size-2) # Padding + )[:self.block_size+2] + elif len(self.IV) == self.block_size+2: + # ... decryption + self._encrypted_IV = self.IV + self.IV = IV_cipher.decrypt(self.IV + # Ciphertext + b('\x00')*(self.block_size-2) # Padding + )[:self.block_size+2] + if self.IV[-2:] != self.IV[-4:-2]: + raise ValueError("Failed integrity check for OPENPGP IV") + self.IV = self.IV[:-2] + else: + raise ValueError("Length of IV must be %d or %d bytes for MODE_OPENPGP" + % (self.block_size, self.block_size+2)) + + # Instantiate the cipher for the real PGP data + self._cipher = factory.new(key, MODE_CFB, + self._encrypted_IV[-self.block_size:], + segment_size=self.block_size*8) + + def encrypt(self, plaintext): + """Encrypt data with the key and the parameters set at initialization. + + The cipher object is stateful; encryption of a long block + of data can be broken up in two or more calls to `encrypt()`. + That is, the statement: + + >>> c.encrypt(a) + c.encrypt(b) + + is always equivalent to: + + >>> c.encrypt(a+b) + + That also means that you cannot reuse an object for encrypting + or decrypting other data with the same key. + + This function does not perform any padding. + + - For `MODE_ECB`, `MODE_CBC`, and `MODE_OFB`, *plaintext* length + (in bytes) must be a multiple of *block_size*. + + - For `MODE_CFB`, *plaintext* length (in bytes) must be a multiple + of *segment_size*/8. + + - For `MODE_CTR`, *plaintext* can be of any length. + + - For `MODE_OPENPGP`, *plaintext* must be a multiple of *block_size*, + unless it is the last chunk of the message. + + :Parameters: + plaintext : byte string + The piece of data to encrypt. + :Return: + the encrypted data, as a byte string. It is as long as + *plaintext* with one exception: when encrypting the first message + chunk with `MODE_OPENPGP`, the encypted IV is prepended to the + returned ciphertext. + """ + + if self.mode == MODE_OPENPGP: + padding_length = (self.block_size - len(plaintext) % self.block_size) % self.block_size + if padding_length>0: + # CFB mode requires ciphertext to have length multiple of block size, + # but PGP mode allows the last block to be shorter + if self._done_last_block: + raise ValueError("Only the last chunk is allowed to have length not multiple of %d bytes", + self.block_size) + self._done_last_block = True + padded = plaintext + b('\x00')*padding_length + res = self._cipher.encrypt(padded)[:len(plaintext)] + else: + res = self._cipher.encrypt(plaintext) + if not self._done_first_block: + res = self._encrypted_IV + res + self._done_first_block = True + return res + + return self._cipher.encrypt(plaintext) + + def decrypt(self, ciphertext): + """Decrypt data with the key and the parameters set at initialization. + + The cipher object is stateful; decryption of a long block + of data can be broken up in two or more calls to `decrypt()`. + That is, the statement: + + >>> c.decrypt(a) + c.decrypt(b) + + is always equivalent to: + + >>> c.decrypt(a+b) + + That also means that you cannot reuse an object for encrypting + or decrypting other data with the same key. + + This function does not perform any padding. + + - For `MODE_ECB`, `MODE_CBC`, and `MODE_OFB`, *ciphertext* length + (in bytes) must be a multiple of *block_size*. + + - For `MODE_CFB`, *ciphertext* length (in bytes) must be a multiple + of *segment_size*/8. + + - For `MODE_CTR`, *ciphertext* can be of any length. + + - For `MODE_OPENPGP`, *plaintext* must be a multiple of *block_size*, + unless it is the last chunk of the message. + + :Parameters: + ciphertext : byte string + The piece of data to decrypt. + :Return: the decrypted data (byte string, as long as *ciphertext*). + """ + if self.mode == MODE_OPENPGP: + padding_length = (self.block_size - len(ciphertext) % self.block_size) % self.block_size + if padding_length>0: + # CFB mode requires ciphertext to have length multiple of block size, + # but PGP mode allows the last block to be shorter + if self._done_last_block: + raise ValueError("Only the last chunk is allowed to have length not multiple of %d bytes", + self.block_size) + self._done_last_block = True + padded = ciphertext + b('\x00')*padding_length + res = self._cipher.decrypt(padded)[:len(ciphertext)] + else: + res = self._cipher.decrypt(ciphertext) + return res + + return self._cipher.decrypt(ciphertext) + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/HMAC.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/HMAC.py new file mode 100644 index 0000000..6244db4 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/HMAC.py @@ -0,0 +1,212 @@ +# HMAC.py - Implements the HMAC algorithm as described by RFC 2104. +# +# =================================================================== +# Portions Copyright (c) 2001, 2002, 2003 Python Software Foundation; +# All Rights Reserved +# +# This file contains code from the Python 2.2 hmac.py module (the +# "Original Code"), with modifications made after it was incorporated +# into PyCrypto (the "Modifications"). +# +# To the best of our knowledge, the Python Software Foundation is the +# copyright holder of the Original Code, and has licensed it under the +# Python 2.2 license. See the file LEGAL/copy/LICENSE.python-2.2 for +# details. +# +# The Modifications to this file are dedicated to the public domain. +# To the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. No rights are +# reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + + +"""HMAC (Hash-based Message Authentication Code) algorithm + +HMAC is a MAC defined in RFC2104_ and FIPS-198_ and constructed using +a cryptograpic hash algorithm. +It is usually named *HMAC-X*, where *X* is the hash algorithm; for +instance *HMAC-SHA1* or *HMAC-MD5*. + +The strength of an HMAC depends on: + + - the strength of the hash algorithm + - the length and entropy of the secret key + +An example of possible usage is the following: + + >>> from Crypto.Hash import HMAC + >>> + >>> secret = b'Swordfish' + >>> h = HMAC.new(secret) + >>> h.update(b'Hello') + >>> print h.hexdigest() + +.. _RFC2104: http://www.ietf.org/rfc/rfc2104.txt +.. _FIPS-198: http://csrc.nist.gov/publications/fips/fips198/fips-198a.pdf +""" + +# This is just a copy of the Python 2.2 HMAC module, modified to work when +# used on versions of Python before 2.2. + +__revision__ = "$Id$" + +__all__ = ['new', 'digest_size', 'HMAC' ] + +from Crypto.Util.strxor import strxor_c +from Crypto.Util.py3compat import * + +#: The size of the authentication tag produced by the MAC. +#: It matches the digest size on the underlying +#: hashing module used. +digest_size = None + +class HMAC: + """Class that implements HMAC""" + + #: The size of the authentication tag produced by the MAC. + #: It matches the digest size on the underlying + #: hashing module used. + digest_size = None + + def __init__(self, key, msg = None, digestmod = None): + """Create a new HMAC object. + + :Parameters: + key : byte string + secret key for the MAC object. + It must be long enough to match the expected security level of the + MAC. However, there is no benefit in using keys longer than the + `digest_size` of the underlying hash algorithm. + msg : byte string + The very first chunk of the message to authenticate. + It is equivalent to an early call to `update()`. Optional. + :Parameter digestmod: + The hash algorithm the HMAC is based on. + Default is `Crypto.Hash.MD5`. + :Type digestmod: + A hash module or object instantiated from `Crypto.Hash` + """ + if digestmod is None: + import MD5 + digestmod = MD5 + + self.digestmod = digestmod + self.outer = digestmod.new() + self.inner = digestmod.new() + try: + self.digest_size = digestmod.digest_size + except AttributeError: + self.digest_size = len(self.outer.digest()) + + try: + # The block size is 128 bytes for SHA384 and SHA512 and 64 bytes + # for the others hash function + blocksize = digestmod.block_size + except AttributeError: + blocksize = 64 + + ipad = 0x36 + opad = 0x5C + + if len(key) > blocksize: + key = digestmod.new(key).digest() + + key = key + bchr(0) * (blocksize - len(key)) + self.outer.update(strxor_c(key, opad)) + self.inner.update(strxor_c(key, ipad)) + if (msg): + self.update(msg) + + def update(self, msg): + """Continue authentication of a message by consuming the next chunk of data. + + Repeated calls are equivalent to a single call with the concatenation + of all the arguments. In other words: + + >>> m.update(a); m.update(b) + + is equivalent to: + + >>> m.update(a+b) + + :Parameters: + msg : byte string + The next chunk of the message being authenticated + """ + + self.inner.update(msg) + + def copy(self): + """Return a copy ("clone") of the MAC object. + + The copy will have the same internal state as the original MAC + object. + This can be used to efficiently compute the MAC of strings that + share a common initial substring. + + :Returns: An `HMAC` object + """ + other = HMAC(b("")) + other.digestmod = self.digestmod + other.inner = self.inner.copy() + other.outer = self.outer.copy() + return other + + def digest(self): + """Return the **binary** (non-printable) MAC of the message that has + been authenticated so far. + + This method does not change the state of the MAC object. + You can continue updating the object after calling this function. + + :Return: A byte string of `digest_size` bytes. It may contain non-ASCII + characters, including null bytes. + """ + h = self.outer.copy() + h.update(self.inner.digest()) + return h.digest() + + def hexdigest(self): + """Return the **printable** MAC of the message that has been + authenticated so far. + + This method does not change the state of the MAC object. + + :Return: A string of 2* `digest_size` bytes. It contains only + hexadecimal ASCII digits. + """ + return "".join(["%02x" % bord(x) + for x in tuple(self.digest())]) + +def new(key, msg = None, digestmod = None): + """Create a new HMAC object. + + :Parameters: + key : byte string + key for the MAC object. + It must be long enough to match the expected security level of the + MAC. However, there is no benefit in using keys longer than the + `digest_size` of the underlying hash algorithm. + msg : byte string + The very first chunk of the message to authenticate. + It is equivalent to an early call to `HMAC.update()`. + Optional. + :Parameter digestmod: + The hash to use to implement the HMAC. Default is `Crypto.Hash.MD5`. + :Type digestmod: + A hash module or instantiated object from `Crypto.Hash` + :Returns: An `HMAC` object + """ + return HMAC(key, msg, digestmod) + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/MD2.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/MD2.py new file mode 100644 index 0000000..dac959e --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/MD2.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""MD2 cryptographic hash algorithm. + +MD2 is specified in RFC1319_ and it produces the 128 bit digest of a message. + + >>> from Crypto.Hash import MD2 + >>> + >>> h = MD2.new() + >>> h.update(b'Hello') + >>> print h.hexdigest() + +MD2 stand for Message Digest version 2, and it was invented by Rivest in 1989. + +This algorithm is both slow and insecure. Do not use it for new designs. + +.. _RFC1319: http://tools.ietf.org/html/rfc1319 +""" + +_revision__ = "$Id$" + +__all__ = ['new', 'digest_size', 'MD2Hash' ] + +from Crypto.Util.py3compat import * +from Crypto.Hash.hashalgo import HashAlgo + +import Crypto.Hash._MD2 as _MD2 +hashFactory = _MD2 + +class MD2Hash(HashAlgo): + """Class that implements an MD2 hash + + :undocumented: block_size + """ + + #: ASN.1 Object identifier (OID):: + #: + #: id-md2 OBJECT IDENTIFIER ::= { + #: iso(1) member-body(2) us(840) rsadsi(113549) + #: digestAlgorithm(2) 2 + #: } + #: + #: This value uniquely identifies the MD2 algorithm. + oid = b('\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x02') + + digest_size = 16 + block_size = 16 + + def __init__(self, data=None): + HashAlgo.__init__(self, hashFactory, data) + + def new(self, data=None): + return MD2Hash(data) + +def new(data=None): + """Return a fresh instance of the hash object. + + :Parameters: + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `MD2Hash.update()`. + Optional. + + :Return: An `MD2Hash` object + """ + return MD2Hash().new(data) + +#: The size of the resulting hash in bytes. +digest_size = MD2Hash.digest_size + +#: The internal block size of the hash algorithm in bytes. +block_size = MD2Hash.block_size + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/MD4.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/MD4.py new file mode 100644 index 0000000..e28a201 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/MD4.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""MD4 cryptographic hash algorithm. + +MD4 is specified in RFC1320_ and produces the 128 bit digest of a message. + + >>> from Crypto.Hash import MD4 + >>> + >>> h = MD4.new() + >>> h.update(b'Hello') + >>> print h.hexdigest() + +MD4 stand for Message Digest version 4, and it was invented by Rivest in 1990. + +This algorithm is insecure. Do not use it for new designs. + +.. _RFC1320: http://tools.ietf.org/html/rfc1320 +""" + +_revision__ = "$Id$" + +__all__ = ['new', 'digest_size', 'MD4Hash' ] + +from Crypto.Util.py3compat import * +from Crypto.Hash.hashalgo import HashAlgo + +import Crypto.Hash._MD4 as _MD4 +hashFactory = _MD4 + +class MD4Hash(HashAlgo): + """Class that implements an MD4 hash + + :undocumented: block_size + """ + + #: ASN.1 Object identifier (OID):: + #: + #: id-md2 OBJECT IDENTIFIER ::= { + #: iso(1) member-body(2) us(840) rsadsi(113549) + #: digestAlgorithm(2) 4 + #: } + #: + #: This value uniquely identifies the MD4 algorithm. + oid = b('\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x04') + + digest_size = 16 + block_size = 64 + + def __init__(self, data=None): + HashAlgo.__init__(self, hashFactory, data) + + def new(self, data=None): + return MD4Hash(data) + +def new(data=None): + """Return a fresh instance of the hash object. + + :Parameters: + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `MD4Hash.update()`. + Optional. + + :Return: A `MD4Hash` object + """ + return MD4Hash().new(data) + +#: The size of the resulting hash in bytes. +digest_size = MD4Hash.digest_size + +#: The internal block size of the hash algorithm in bytes. +block_size = MD4Hash.block_size + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/MD5.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/MD5.py new file mode 100644 index 0000000..18e9e7b --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/MD5.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""MD5 cryptographic hash algorithm. + +MD5 is specified in RFC1321_ and produces the 128 bit digest of a message. + + >>> from Crypto.Hash import MD5 + >>> + >>> h = MD5.new() + >>> h.update(b'Hello') + >>> print h.hexdigest() + +MD5 stand for Message Digest version 5, and it was invented by Rivest in 1991. + +This algorithm is insecure. Do not use it for new designs. + +.. _RFC1321: http://tools.ietf.org/html/rfc1321 +""" + +_revision__ = "$Id$" + +__all__ = ['new', 'digest_size', 'MD5Hash' ] + +from Crypto.Util.py3compat import * +from Crypto.Hash.hashalgo import HashAlgo + +try: + # The md5 module is deprecated in Python 2.6, so use hashlib when possible. + import hashlib + hashFactory = hashlib.md5 + +except ImportError: + import md5 + hashFactory = md5 + +class MD5Hash(HashAlgo): + """Class that implements an MD5 hash + + :undocumented: block_size + """ + + #: ASN.1 Object identifier (OID):: + #: + #: id-md5 OBJECT IDENTIFIER ::= { + #: iso(1) member-body(2) us(840) rsadsi(113549) + #: digestAlgorithm(2) 5 + #: } + #: + #: This value uniquely identifies the MD5 algorithm. + oid = b('\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05') + + digest_size = 16 + block_size = 64 + + def __init__(self, data=None): + HashAlgo.__init__(self, hashFactory, data) + + def new(self, data=None): + return MD5Hash(data) + +def new(data=None): + """Return a fresh instance of the hash object. + + :Parameters: + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `MD5Hash.update()`. + Optional. + + :Return: A `MD5Hash` object + """ + return MD5Hash().new(data) + +#: The size of the resulting hash in bytes. +digest_size = MD5Hash.digest_size + +#: The internal block size of the hash algorithm in bytes. +block_size = MD5Hash.block_size + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/RIPEMD.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/RIPEMD.py new file mode 100644 index 0000000..33099cb --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/RIPEMD.py @@ -0,0 +1,94 @@ +# -*- coding: utf-8 -*- +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""RIPEMD-160 cryptographic hash algorithm. + +RIPEMD-160_ produces the 160 bit digest of a message. + + >>> from Crypto.Hash import RIPEMD + >>> + >>> h = RIPEMD.new() + >>> h.update(b'Hello') + >>> print h.hexdigest() + +RIPEMD-160 stands for RACE Integrity Primitives Evaluation Message Digest +with a 160 bit digest. It was invented by Dobbertin, Bosselaers, and Preneel. + +This algorithm is considered secure, although it has not been scrutinized as +extensively as SHA-1. Moreover, it provides an informal security level of just +80bits. + +.. _RIPEMD-160: http://homes.esat.kuleuven.be/~bosselae/ripemd160.html +""" + +_revision__ = "$Id$" + +__all__ = ['new', 'digest_size', 'RIPEMD160Hash' ] + +from Crypto.Util.py3compat import * +from Crypto.Hash.hashalgo import HashAlgo + +import Crypto.Hash._RIPEMD160 as _RIPEMD160 +hashFactory = _RIPEMD160 + +class RIPEMD160Hash(HashAlgo): + """Class that implements a RIPMD-160 hash + + :undocumented: block_size + """ + + #: ASN.1 Object identifier (OID):: + #: + #: id-ripemd160 OBJECT IDENTIFIER ::= { + #: iso(1) identified-organization(3) teletrust(36) + #: algorithm(3) hashAlgorithm(2) ripemd160(1) + #: } + #: + #: This value uniquely identifies the RIPMD-160 algorithm. + oid = b("\x06\x05\x2b\x24\x03\x02\x01") + + digest_size = 20 + block_size = 64 + + def __init__(self, data=None): + HashAlgo.__init__(self, hashFactory, data) + + def new(self, data=None): + return RIPEMD160Hash(data) + +def new(data=None): + """Return a fresh instance of the hash object. + + :Parameters: + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `RIPEMD160Hash.update()`. + Optional. + + :Return: A `RIPEMD160Hash` object + """ + return RIPEMD160Hash().new(data) + +#: The size of the resulting hash in bytes. +digest_size = RIPEMD160Hash.digest_size + +#: The internal block size of the hash algorithm in bytes. +block_size = RIPEMD160Hash.block_size + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/SHA.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/SHA.py new file mode 100644 index 0000000..0bc5917 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/SHA.py @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""SHA-1 cryptographic hash algorithm. + +SHA-1_ produces the 160 bit digest of a message. + + >>> from Crypto.Hash import SHA + >>> + >>> h = SHA.new() + >>> h.update(b'Hello') + >>> print h.hexdigest() + +*SHA* stands for Secure Hash Algorithm. + +This algorithm is not considered secure. Do not use it for new designs. + +.. _SHA-1: http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf +""" + +_revision__ = "$Id$" + +__all__ = ['new', 'digest_size', 'SHA1Hash' ] + +from Crypto.Util.py3compat import * +from Crypto.Hash.hashalgo import HashAlgo + +try: + # The sha module is deprecated in Python 2.6, so use hashlib when possible. + import hashlib + hashFactory = hashlib.sha1 + +except ImportError: + import sha + hashFactory = sha + +class SHA1Hash(HashAlgo): + """Class that implements a SHA-1 hash + + :undocumented: block_size + """ + + #: ASN.1 Object identifier (OID):: + #: + #: id-sha1 OBJECT IDENTIFIER ::= { + #: iso(1) identified-organization(3) oiw(14) secsig(3) + #: algorithms(2) 26 + #: } + #: + #: This value uniquely identifies the SHA-1 algorithm. + oid = b('\x06\x05\x2b\x0e\x03\x02\x1a') + + digest_size = 20 + block_size = 64 + + def __init__(self, data=None): + HashAlgo.__init__(self, hashFactory, data) + + def new(self, data=None): + return SHA1Hash(data) + +def new(data=None): + """Return a fresh instance of the hash object. + + :Parameters: + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `SHA1Hash.update()`. + Optional. + + :Return: A `SHA1Hash` object + """ + return SHA1Hash().new(data) + +#: The size of the resulting hash in bytes. +digest_size = SHA1Hash.digest_size + +#: The internal block size of the hash algorithm in bytes. +block_size = SHA1Hash.block_size + + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/SHA224.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/SHA224.py new file mode 100644 index 0000000..959b56d --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/SHA224.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""SHA-224 cryptographic hash algorithm. + +SHA-224 belongs to the SHA-2_ family of cryptographic hashes. +It produces the 224 bit digest of a message. + + >>> from Crypto.Hash import SHA224 + >>> + >>> h = SHA224.new() + >>> h.update(b'Hello') + >>> print h.hexdigest() + +*SHA* stands for Secure Hash Algorithm. + +.. _SHA-2: http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf +""" + +_revision__ = "$Id$" + +__all__ = ['new', 'digest_size', 'SHA224Hash' ] + +from Crypto.Util.py3compat import * +from Crypto.Hash.hashalgo import HashAlgo + +try: + import hashlib + hashFactory = hashlib.sha224 + +except ImportError: + from Crypto.Hash import _SHA224 + hashFactory = _SHA224 + +class SHA224Hash(HashAlgo): + """Class that implements a SHA-224 hash + + :undocumented: block_size + """ + + #: ASN.1 Object identifier (OID):: + #: + #: id-sha224 OBJECT IDENTIFIER ::= { + #: joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) + #: nistalgorithm(4) hashalgs(2) 4 + #: } + #: + #: This value uniquely identifies the SHA-224 algorithm. + oid = b('\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04') + + digest_size = 28 + block_size = 64 + + def __init__(self, data=None): + HashAlgo.__init__(self, hashFactory, data) + + def new(self, data=None): + return SHA224Hash(data) + +def new(data=None): + """Return a fresh instance of the hash object. + + :Parameters: + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `SHA224Hash.update()`. + Optional. + + :Return: A `SHA224Hash` object + """ + return SHA224Hash().new(data) + +#: The size of the resulting hash in bytes. +digest_size = SHA224Hash.digest_size + +#: The internal block size of the hash algorithm in bytes. +block_size = SHA224Hash.block_size + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/SHA256.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/SHA256.py new file mode 100644 index 0000000..b0a99b3 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/SHA256.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""SHA-256 cryptographic hash algorithm. + +SHA-256 belongs to the SHA-2_ family of cryptographic hashes. +It produces the 256 bit digest of a message. + + >>> from Crypto.Hash import SHA256 + >>> + >>> h = SHA256.new() + >>> h.update(b'Hello') + >>> print h.hexdigest() + +*SHA* stands for Secure Hash Algorithm. + +.. _SHA-2: http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf +""" + +_revision__ = "$Id$" + +__all__ = ['new', 'digest_size', 'SHA256Hash' ] + +from Crypto.Util.py3compat import * +from Crypto.Hash.hashalgo import HashAlgo + +try: + import hashlib + hashFactory = hashlib.sha256 + +except ImportError: + from Crypto.Hash import _SHA256 + hashFactory = _SHA256 + +class SHA256Hash(HashAlgo): + """Class that implements a SHA-256 hash + + :undocumented: block_size + """ + + #: ASN.1 Object identifier (OID):: + #: + #: id-sha256 OBJECT IDENTIFIER ::= { + #: joint-iso-itu-t(2) country(16) us(840) organization(1) + #: gov(101) csor(3) nistalgorithm(4) hashalgs(2) 1 + #: } + #: + #: This value uniquely identifies the SHA-256 algorithm. + oid = b('\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01') + + digest_size = 32 + block_size = 64 + + def __init__(self, data=None): + HashAlgo.__init__(self, hashFactory, data) + + def new(self, data=None): + return SHA256Hash(data) + +def new(data=None): + """Return a fresh instance of the hash object. + + :Parameters: + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `SHA256Hash.update()`. + Optional. + + :Return: A `SHA256Hash` object + """ + return SHA256Hash().new(data) + +#: The size of the resulting hash in bytes. +digest_size = SHA256Hash.digest_size + +#: The internal block size of the hash algorithm in bytes. +block_size = SHA256Hash.block_size + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/SHA384.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/SHA384.py new file mode 100644 index 0000000..3490b02 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/SHA384.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""SHA-384 cryptographic hash algorithm. + +SHA-384 belongs to the SHA-2_ family of cryptographic hashes. +It produces the 384 bit digest of a message. + + >>> from Crypto.Hash import SHA384 + >>> + >>> h = SHA384.new() + >>> h.update(b'Hello') + >>> print h.hexdigest() + +*SHA* stands for Secure Hash Algorithm. + +.. _SHA-2: http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf +""" + +_revision__ = "$Id$" + +__all__ = ['new', 'digest_size', 'SHA384Hash' ] + +from Crypto.Util.py3compat import * +from Crypto.Hash.hashalgo import HashAlgo + +try: + import hashlib + hashFactory = hashlib.sha384 + +except ImportError: + from Crypto.Hash import _SHA384 + hashFactory = _SHA384 + +class SHA384Hash(HashAlgo): + """Class that implements a SHA-384 hash + + :undocumented: block_size + """ + + #: ASN.1 Object identifier (OID):: + #: + #: id-sha384 OBJECT IDENTIFIER ::= { + #: joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) + #: nistalgorithm(4) hashalgs(2) 2 + #: } + #: + #: This value uniquely identifies the SHA-384 algorithm. + oid = b('\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02') + + digest_size = 48 + block_size = 128 + + def __init__(self, data=None): + HashAlgo.__init__(self, hashFactory, data) + + def new(self, data=None): + return SHA384Hash(data) + +def new(data=None): + """Return a fresh instance of the hash object. + + :Parameters: + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `SHA384Hash.update()`. + Optional. + + :Return: A `SHA384Hash` object + """ + return SHA384Hash().new(data) + +#: The size of the resulting hash in bytes. +digest_size = SHA384Hash.digest_size + +#: The internal block size of the hash algorithm in bytes. +block_size = SHA384Hash.block_size + + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/SHA512.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/SHA512.py new file mode 100644 index 0000000..d57548d --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/SHA512.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""SHA-512 cryptographic hash algorithm. + +SHA-512 belongs to the SHA-2_ family of cryptographic hashes. +It produces the 512 bit digest of a message. + + >>> from Crypto.Hash import SHA512 + >>> + >>> h = SHA512.new() + >>> h.update(b'Hello') + >>> print h.hexdigest() + +*SHA* stands for Secure Hash Algorithm. + +.. _SHA-2: http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf +""" + +_revision__ = "$Id$" + +__all__ = ['new', 'digest_size', 'SHA512Hash' ] + +from Crypto.Util.py3compat import * +from Crypto.Hash.hashalgo import HashAlgo + +try: + import hashlib + hashFactory = hashlib.sha512 + +except ImportError: + from Crypto.Hash import _SHA512 + hashFactory = _SHA512 + +class SHA512Hash(HashAlgo): + """Class that implements a SHA-512 hash + + :undocumented: block_size + """ + + #: ASN.1 Object identifier (OID):: + #: + #: id-sha512 OBJECT IDENTIFIER ::= { + #: joint-iso-itu-t(2) + #: country(16) us(840) organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 3 + #: } + #: + #: This value uniquely identifies the SHA-512 algorithm. + oid = b('\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03') + + digest_size = 64 + block_size = 128 + + def __init__(self, data=None): + HashAlgo.__init__(self, hashFactory, data) + + def new(self, data=None): + return SHA512Hash(data) + +def new(data=None): + """Return a fresh instance of the hash object. + + :Parameters: + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `SHA512Hash.update()`. + Optional. + + :Return: A `SHA512Hash` object + """ + return SHA512Hash().new(data) + +#: The size of the resulting hash in bytes. +digest_size = SHA512Hash.digest_size + +#: The internal block size of the hash algorithm in bytes. +block_size = SHA512Hash.block_size + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/_MD2.so b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/_MD2.so new file mode 100755 index 0000000..78f7288 Binary files /dev/null and b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/_MD2.so differ diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/_MD4.so b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/_MD4.so new file mode 100755 index 0000000..12fd329 Binary files /dev/null and b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/_MD4.so differ diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/_RIPEMD160.so b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/_RIPEMD160.so new file mode 100755 index 0000000..ee864e6 Binary files /dev/null and b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/_RIPEMD160.so differ diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/_SHA224.so b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/_SHA224.so new file mode 100755 index 0000000..022fc9e Binary files /dev/null and b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/_SHA224.so differ diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/_SHA256.so b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/_SHA256.so new file mode 100755 index 0000000..21f27e5 Binary files /dev/null and b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/_SHA256.so differ diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/_SHA384.so b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/_SHA384.so new file mode 100755 index 0000000..ac9e34d Binary files /dev/null and b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/_SHA384.so differ diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/_SHA512.so b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/_SHA512.so new file mode 100755 index 0000000..d62645e Binary files /dev/null and b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/_SHA512.so differ diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/__init__.py new file mode 100644 index 0000000..4582c66 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/__init__.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Hashing algorithms + +Hash functions take arbitrary binary strings as input, and produce a random-like output +of fixed size that is dependent on the input; it should be practically infeasible +to derive the original input data given only the hash function's +output. In other words, the hash function is *one-way*. + +It should also not be practically feasible to find a second piece of data +(a *second pre-image*) whose hash is the same as the original message +(*weak collision resistance*). + +Finally, it should not be feasible to find two arbitrary messages with the +same hash (*strong collision resistance*). + +The output of the hash function is called the *digest* of the input message. +In general, the security of a hash function is related to the length of the +digest. If the digest is *n* bits long, its security level is roughly comparable +to the the one offered by an *n/2* bit encryption algorithm. + +Hash functions can be used simply as a integrity check, or, in +association with a public-key algorithm, can be used to implement +digital signatures. + +The hashing modules here all support the interface described in `PEP +247`_ , "API for Cryptographic Hash Functions". + +.. _`PEP 247` : http://www.python.org/dev/peps/pep-0247/ + +:undocumented: _MD2, _MD4, _RIPEMD160, _SHA224, _SHA256, _SHA384, _SHA512 +""" + +__all__ = ['HMAC', 'MD2', 'MD4', 'MD5', 'RIPEMD', 'SHA', + 'SHA224', 'SHA256', 'SHA384', 'SHA512'] +__revision__ = "$Id$" + + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/hashalgo.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/hashalgo.py new file mode 100644 index 0000000..b38b3a6 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Hash/hashalgo.py @@ -0,0 +1,116 @@ +# -*- coding: utf-8 -*- +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +from binascii import hexlify + +class HashAlgo: + """A generic class for an abstract cryptographic hash algorithm. + + :undocumented: block_size + """ + + #: The size of the resulting hash in bytes. + digest_size = None + #: The internal block size of the hash algorithm in bytes. + block_size = None + + def __init__(self, hashFactory, data=None): + """Initialize the hash object. + + :Parameters: + hashFactory : callable + An object that will generate the actual hash implementation. + *hashFactory* must have a *new()* method, or must be directly + callable. + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `update()`. + """ + if hasattr(hashFactory, 'new'): + self._hash = hashFactory.new() + else: + self._hash = hashFactory() + if data: + self.update(data) + + def update(self, data): + """Continue hashing of a message by consuming the next chunk of data. + + Repeated calls are equivalent to a single call with the concatenation + of all the arguments. In other words: + + >>> m.update(a); m.update(b) + + is equivalent to: + + >>> m.update(a+b) + + :Parameters: + data : byte string + The next chunk of the message being hashed. + """ + return self._hash.update(data) + + def digest(self): + """Return the **binary** (non-printable) digest of the message that has been hashed so far. + + This method does not change the state of the hash object. + You can continue updating the object after calling this function. + + :Return: A byte string of `digest_size` bytes. It may contain non-ASCII + characters, including null bytes. + """ + return self._hash.digest() + + def hexdigest(self): + """Return the **printable** digest of the message that has been hashed so far. + + This method does not change the state of the hash object. + + :Return: A string of 2* `digest_size` characters. It contains only + hexadecimal ASCII digits. + """ + return self._hash.hexdigest() + + def copy(self): + """Return a copy ("clone") of the hash object. + + The copy will have the same internal state as the original hash + object. + This can be used to efficiently compute the digests of strings that + share a common initial substring. + + :Return: A hash object of the same type + """ + return self._hash.copy() + + def new(self, data=None): + """Return a fresh instance of the hash object. + + Unlike the `copy` method, the internal state of the object is empty. + + :Parameters: + data : byte string + The next chunk of the message being hashed. + + :Return: A hash object of the same type + """ + pass + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Protocol/AllOrNothing.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Protocol/AllOrNothing.py new file mode 100644 index 0000000..acc92d5 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Protocol/AllOrNothing.py @@ -0,0 +1,319 @@ +# +# AllOrNothing.py : all-or-nothing package transformations +# +# Part of the Python Cryptography Toolkit +# +# Written by Andrew M. Kuchling and others +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""This file implements all-or-nothing package transformations. + +An all-or-nothing package transformation is one in which some text is +transformed into message blocks, such that all blocks must be obtained before +the reverse transformation can be applied. Thus, if any blocks are corrupted +or lost, the original message cannot be reproduced. + +An all-or-nothing package transformation is not encryption, although a block +cipher algorithm is used. The encryption key is randomly generated and is +extractable from the message blocks. + +This class implements the All-Or-Nothing package transformation algorithm +described in: + +Ronald L. Rivest. "All-Or-Nothing Encryption and The Package Transform" +http://theory.lcs.mit.edu/~rivest/fusion.pdf + +""" + +__revision__ = "$Id$" + +import operator +import sys +from Crypto.Util.number import bytes_to_long, long_to_bytes +from Crypto.Util.py3compat import * + +def isInt(x): + test = 0 + try: + test += x + except TypeError: + return 0 + return 1 + +class AllOrNothing: + """Class implementing the All-or-Nothing package transform. + + Methods for subclassing: + + _inventkey(key_size): + Returns a randomly generated key. Subclasses can use this to + implement better random key generating algorithms. The default + algorithm is probably not very cryptographically secure. + + """ + + def __init__(self, ciphermodule, mode=None, IV=None): + """AllOrNothing(ciphermodule, mode=None, IV=None) + + ciphermodule is a module implementing the cipher algorithm to + use. It must provide the PEP272 interface. + + Note that the encryption key is randomly generated + automatically when needed. Optional arguments mode and IV are + passed directly through to the ciphermodule.new() method; they + are the feedback mode and initialization vector to use. All + three arguments must be the same for the object used to create + the digest, and to undigest'ify the message blocks. + """ + + self.__ciphermodule = ciphermodule + self.__mode = mode + self.__IV = IV + self.__key_size = ciphermodule.key_size + if not isInt(self.__key_size) or self.__key_size==0: + self.__key_size = 16 + + __K0digit = bchr(0x69) + + def digest(self, text): + """digest(text:string) : [string] + + Perform the All-or-Nothing package transform on the given + string. Output is a list of message blocks describing the + transformed text, where each block is a string of bit length equal + to the ciphermodule's block_size. + """ + + # generate a random session key and K0, the key used to encrypt the + # hash blocks. Rivest calls this a fixed, publically-known encryption + # key, but says nothing about the security implications of this key or + # how to choose it. + key = self._inventkey(self.__key_size) + K0 = self.__K0digit * self.__key_size + + # we need two cipher objects here, one that is used to encrypt the + # message blocks and one that is used to encrypt the hashes. The + # former uses the randomly generated key, while the latter uses the + # well-known key. + mcipher = self.__newcipher(key) + hcipher = self.__newcipher(K0) + + # Pad the text so that its length is a multiple of the cipher's + # block_size. Pad with trailing spaces, which will be eliminated in + # the undigest() step. + block_size = self.__ciphermodule.block_size + padbytes = block_size - (len(text) % block_size) + text = text + b(' ') * padbytes + + # Run through the algorithm: + # s: number of message blocks (size of text / block_size) + # input sequence: m1, m2, ... ms + # random key K' (`key' in the code) + # Compute output sequence: m'1, m'2, ... m's' for s' = s + 1 + # Let m'i = mi ^ E(K', i) for i = 1, 2, 3, ..., s + # Let m's' = K' ^ h1 ^ h2 ^ ... hs + # where hi = E(K0, m'i ^ i) for i = 1, 2, ... s + # + # The one complication I add is that the last message block is hard + # coded to the number of padbytes added, so that these can be stripped + # during the undigest() step + s = divmod(len(text), block_size)[0] + blocks = [] + hashes = [] + for i in range(1, s+1): + start = (i-1) * block_size + end = start + block_size + mi = text[start:end] + assert len(mi) == block_size + cipherblock = mcipher.encrypt(long_to_bytes(i, block_size)) + mticki = bytes_to_long(mi) ^ bytes_to_long(cipherblock) + blocks.append(mticki) + # calculate the hash block for this block + hi = hcipher.encrypt(long_to_bytes(mticki ^ i, block_size)) + hashes.append(bytes_to_long(hi)) + + # Add the padbytes length as a message block + i = i + 1 + cipherblock = mcipher.encrypt(long_to_bytes(i, block_size)) + mticki = padbytes ^ bytes_to_long(cipherblock) + blocks.append(mticki) + + # calculate this block's hash + hi = hcipher.encrypt(long_to_bytes(mticki ^ i, block_size)) + hashes.append(bytes_to_long(hi)) + + # Now calculate the last message block of the sequence 1..s'. This + # will contain the random session key XOR'd with all the hash blocks, + # so that for undigest(), once all the hash blocks are calculated, the + # session key can be trivially extracted. Calculating all the hash + # blocks requires that all the message blocks be received, thus the + # All-or-Nothing algorithm succeeds. + mtick_stick = bytes_to_long(key) ^ reduce(operator.xor, hashes) + blocks.append(mtick_stick) + + # we convert the blocks to strings since in Python, byte sequences are + # always represented as strings. This is more consistent with the + # model that encryption and hash algorithms always operate on strings. + return [long_to_bytes(i,self.__ciphermodule.block_size) for i in blocks] + + + def undigest(self, blocks): + """undigest(blocks : [string]) : string + + Perform the reverse package transformation on a list of message + blocks. Note that the ciphermodule used for both transformations + must be the same. blocks is a list of strings of bit length + equal to the ciphermodule's block_size. + """ + + # better have at least 2 blocks, for the padbytes package and the hash + # block accumulator + if len(blocks) < 2: + raise ValueError, "List must be at least length 2." + + # blocks is a list of strings. We need to deal with them as long + # integers + blocks = map(bytes_to_long, blocks) + + # Calculate the well-known key, to which the hash blocks are + # encrypted, and create the hash cipher. + K0 = self.__K0digit * self.__key_size + hcipher = self.__newcipher(K0) + block_size = self.__ciphermodule.block_size + + # Since we have all the blocks (or this method would have been called + # prematurely), we can calculate all the hash blocks. + hashes = [] + for i in range(1, len(blocks)): + mticki = blocks[i-1] ^ i + hi = hcipher.encrypt(long_to_bytes(mticki, block_size)) + hashes.append(bytes_to_long(hi)) + + # now we can calculate K' (key). remember the last block contains + # m's' which we don't include here + key = blocks[-1] ^ reduce(operator.xor, hashes) + + # and now we can create the cipher object + mcipher = self.__newcipher(long_to_bytes(key, self.__key_size)) + + # And we can now decode the original message blocks + parts = [] + for i in range(1, len(blocks)): + cipherblock = mcipher.encrypt(long_to_bytes(i, block_size)) + mi = blocks[i-1] ^ bytes_to_long(cipherblock) + parts.append(mi) + + # The last message block contains the number of pad bytes appended to + # the original text string, such that its length was an even multiple + # of the cipher's block_size. This number should be small enough that + # the conversion from long integer to integer should never overflow + padbytes = int(parts[-1]) + text = b('').join(map(long_to_bytes, parts[:-1])) + return text[:-padbytes] + + def _inventkey(self, key_size): + # Return key_size random bytes + from Crypto import Random + return Random.new().read(key_size) + + def __newcipher(self, key): + if self.__mode is None and self.__IV is None: + return self.__ciphermodule.new(key) + elif self.__IV is None: + return self.__ciphermodule.new(key, self.__mode) + else: + return self.__ciphermodule.new(key, self.__mode, self.__IV) + + + +if __name__ == '__main__': + import sys + import getopt + import base64 + + usagemsg = '''\ +Test module usage: %(program)s [-c cipher] [-l] [-h] + +Where: + --cipher module + -c module + Cipher module to use. Default: %(ciphermodule)s + + --aslong + -l + Print the encoded message blocks as long integers instead of base64 + encoded strings + + --help + -h + Print this help message +''' + + ciphermodule = 'AES' + aslong = 0 + + def usage(code, msg=None): + if msg: + print msg + print usagemsg % {'program': sys.argv[0], + 'ciphermodule': ciphermodule} + sys.exit(code) + + try: + opts, args = getopt.getopt(sys.argv[1:], + 'c:l', ['cipher=', 'aslong']) + except getopt.error, msg: + usage(1, msg) + + if args: + usage(1, 'Too many arguments') + + for opt, arg in opts: + if opt in ('-h', '--help'): + usage(0) + elif opt in ('-c', '--cipher'): + ciphermodule = arg + elif opt in ('-l', '--aslong'): + aslong = 1 + + # ugly hack to force __import__ to give us the end-path module + module = __import__('Crypto.Cipher.'+ciphermodule, None, None, ['new']) + + x = AllOrNothing(module) + print 'Original text:\n==========' + print __doc__ + print '==========' + msgblocks = x.digest(b(__doc__)) + print 'message blocks:' + for i, blk in zip(range(len(msgblocks)), msgblocks): + # base64 adds a trailing newline + print ' %3d' % i, + if aslong: + print bytes_to_long(blk) + else: + print base64.encodestring(blk)[:-1] + # + # get a new undigest-only object so there's no leakage + y = AllOrNothing(module) + text = y.undigest(msgblocks) + if text == b(__doc__): + print 'They match!' + else: + print 'They differ!' diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Protocol/Chaffing.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Protocol/Chaffing.py new file mode 100644 index 0000000..c19e037 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Protocol/Chaffing.py @@ -0,0 +1,245 @@ +# +# Chaffing.py : chaffing & winnowing support +# +# Part of the Python Cryptography Toolkit +# +# Written by Andrew M. Kuchling, Barry A. Warsaw, and others +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== +# +"""This file implements the chaffing algorithm. + +Winnowing and chaffing is a technique for enhancing privacy without requiring +strong encryption. In short, the technique takes a set of authenticated +message blocks (the wheat) and adds a number of chaff blocks which have +randomly chosen data and MAC fields. This means that to an adversary, the +chaff blocks look as valid as the wheat blocks, and so the authentication +would have to be performed on every block. By tailoring the number of chaff +blocks added to the message, the sender can make breaking the message +computationally infeasible. There are many other interesting properties of +the winnow/chaff technique. + +For example, say Alice is sending a message to Bob. She packetizes the +message and performs an all-or-nothing transformation on the packets. Then +she authenticates each packet with a message authentication code (MAC). The +MAC is a hash of the data packet, and there is a secret key which she must +share with Bob (key distribution is an exercise left to the reader). She then +adds a serial number to each packet, and sends the packets to Bob. + +Bob receives the packets, and using the shared secret authentication key, +authenticates the MACs for each packet. Those packets that have bad MACs are +simply discarded. The remainder are sorted by serial number, and passed +through the reverse all-or-nothing transform. The transform means that an +eavesdropper (say Eve) must acquire all the packets before any of the data can +be read. If even one packet is missing, the data is useless. + +There's one twist: by adding chaff packets, Alice and Bob can make Eve's job +much harder, since Eve now has to break the shared secret key, or try every +combination of wheat and chaff packet to read any of the message. The cool +thing is that Bob doesn't need to add any additional code; the chaff packets +are already filtered out because their MACs don't match (in all likelihood -- +since the data and MACs for the chaff packets are randomly chosen it is +possible, but very unlikely that a chaff MAC will match the chaff data). And +Alice need not even be the party adding the chaff! She could be completely +unaware that a third party, say Charles, is adding chaff packets to her +messages as they are transmitted. + +For more information on winnowing and chaffing see this paper: + +Ronald L. Rivest, "Chaffing and Winnowing: Confidentiality without Encryption" +http://theory.lcs.mit.edu/~rivest/chaffing.txt + +""" + +__revision__ = "$Id$" + +from Crypto.Util.number import bytes_to_long + +class Chaff: + """Class implementing the chaff adding algorithm. + + Methods for subclasses: + + _randnum(size): + Returns a randomly generated number with a byte-length equal + to size. Subclasses can use this to implement better random + data and MAC generating algorithms. The default algorithm is + probably not very cryptographically secure. It is most + important that the chaff data does not contain any patterns + that can be used to discern it from wheat data without running + the MAC. + + """ + + def __init__(self, factor=1.0, blocksper=1): + """Chaff(factor:float, blocksper:int) + + factor is the number of message blocks to add chaff to, + expressed as a percentage between 0.0 and 1.0. blocksper is + the number of chaff blocks to include for each block being + chaffed. Thus the defaults add one chaff block to every + message block. By changing the defaults, you can adjust how + computationally difficult it could be for an adversary to + brute-force crack the message. The difficulty is expressed + as: + + pow(blocksper, int(factor * number-of-blocks)) + + For ease of implementation, when factor < 1.0, only the first + int(factor*number-of-blocks) message blocks are chaffed. + """ + + if not (0.0<=factor<=1.0): + raise ValueError, "'factor' must be between 0.0 and 1.0" + if blocksper < 0: + raise ValueError, "'blocksper' must be zero or more" + + self.__factor = factor + self.__blocksper = blocksper + + + def chaff(self, blocks): + """chaff( [(serial-number:int, data:string, MAC:string)] ) + : [(int, string, string)] + + Add chaff to message blocks. blocks is a list of 3-tuples of the + form (serial-number, data, MAC). + + Chaff is created by choosing a random number of the same + byte-length as data, and another random number of the same + byte-length as MAC. The message block's serial number is + placed on the chaff block and all the packet's chaff blocks + are randomly interspersed with the single wheat block. This + method then returns a list of 3-tuples of the same form. + Chaffed blocks will contain multiple instances of 3-tuples + with the same serial number, but the only way to figure out + which blocks are wheat and which are chaff is to perform the + MAC hash and compare values. + """ + + chaffedblocks = [] + + # count is the number of blocks to add chaff to. blocksper is the + # number of chaff blocks to add per message block that is being + # chaffed. + count = len(blocks) * self.__factor + blocksper = range(self.__blocksper) + for i, wheat in zip(range(len(blocks)), blocks): + # it shouldn't matter which of the n blocks we add chaff to, so for + # ease of implementation, we'll just add them to the first count + # blocks + if i < count: + serial, data, mac = wheat + datasize = len(data) + macsize = len(mac) + addwheat = 1 + # add chaff to this block + for j in blocksper: + import sys + chaffdata = self._randnum(datasize) + chaffmac = self._randnum(macsize) + chaff = (serial, chaffdata, chaffmac) + # mix up the order, if the 5th bit is on then put the + # wheat on the list + if addwheat and bytes_to_long(self._randnum(16)) & 0x40: + chaffedblocks.append(wheat) + addwheat = 0 + chaffedblocks.append(chaff) + if addwheat: + chaffedblocks.append(wheat) + else: + # just add the wheat + chaffedblocks.append(wheat) + return chaffedblocks + + def _randnum(self, size): + from Crypto import Random + return Random.new().read(size) + + +if __name__ == '__main__': + text = """\ +We hold these truths to be self-evident, that all men are created equal, that +they are endowed by their Creator with certain unalienable Rights, that among +these are Life, Liberty, and the pursuit of Happiness. That to secure these +rights, Governments are instituted among Men, deriving their just powers from +the consent of the governed. That whenever any Form of Government becomes +destructive of these ends, it is the Right of the People to alter or to +abolish it, and to institute new Government, laying its foundation on such +principles and organizing its powers in such form, as to them shall seem most +likely to effect their Safety and Happiness. +""" + print 'Original text:\n==========' + print text + print '==========' + + # first transform the text into packets + blocks = [] ; size = 40 + for i in range(0, len(text), size): + blocks.append( text[i:i+size] ) + + # now get MACs for all the text blocks. The key is obvious... + print 'Calculating MACs...' + from Crypto.Hash import HMAC, SHA + key = 'Jefferson' + macs = [HMAC.new(key, block, digestmod=SHA).digest() + for block in blocks] + + assert len(blocks) == len(macs) + + # put these into a form acceptable as input to the chaffing procedure + source = [] + m = zip(range(len(blocks)), blocks, macs) + print m + for i, data, mac in m: + source.append((i, data, mac)) + + # now chaff these + print 'Adding chaff...' + c = Chaff(factor=0.5, blocksper=2) + chaffed = c.chaff(source) + + from base64 import encodestring + + # print the chaffed message blocks. meanwhile, separate the wheat from + # the chaff + + wheat = [] + print 'chaffed message blocks:' + for i, data, mac in chaffed: + # do the authentication + h = HMAC.new(key, data, digestmod=SHA) + pmac = h.digest() + if pmac == mac: + tag = '-->' + wheat.append(data) + else: + tag = ' ' + # base64 adds a trailing newline + print tag, '%3d' % i, \ + repr(data), encodestring(mac)[:-1] + + # now decode the message packets and check it against the original text + print 'Undigesting wheat...' + # PY3K: This is meant to be text, do not change to bytes (data) + newtext = "".join(wheat) + if newtext == text: + print 'They match!' + else: + print 'They differ!' diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Protocol/KDF.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Protocol/KDF.py new file mode 100644 index 0000000..973b7af --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Protocol/KDF.py @@ -0,0 +1,123 @@ +# +# KDF.py : a collection of Key Derivation Functions +# +# Part of the Python Cryptography Toolkit +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""This file contains a collection of standard key derivation functions. + +A key derivation function derives one or more secondary secret keys from +one primary secret (a master key or a pass phrase). + +This is typically done to insulate the secondary keys from each other, +to avoid that leakage of a secondary key compromises the security of the +master key, or to thwart attacks on pass phrases (e.g. via rainbow tables). + +:undocumented: __revision__ +""" + +__revision__ = "$Id$" + +import math +import struct + +from Crypto.Util.py3compat import * +from Crypto.Hash import SHA as SHA1, HMAC +from Crypto.Util.strxor import strxor + +def PBKDF1(password, salt, dkLen, count=1000, hashAlgo=None): + """Derive one key from a password (or passphrase). + + This function performs key derivation according an old version of + the PKCS#5 standard (v1.5). + + This algorithm is called ``PBKDF1``. Even though it is still described + in the latest version of the PKCS#5 standard (version 2, or RFC2898), + newer applications should use the more secure and versatile `PBKDF2` instead. + + :Parameters: + password : string + The secret password or pass phrase to generate the key from. + salt : byte string + An 8 byte string to use for better protection from dictionary attacks. + This value does not need to be kept secret, but it should be randomly + chosen for each derivation. + dkLen : integer + The length of the desired key. Default is 16 bytes, suitable for instance for `Crypto.Cipher.AES`. + count : integer + The number of iterations to carry out. It's recommended to use at least 1000. + hashAlgo : module + The hash algorithm to use, as a module or an object from the `Crypto.Hash` package. + The digest length must be no shorter than ``dkLen``. + The default algorithm is `SHA1`. + + :Return: A byte string of length `dkLen` that can be used as key. + """ + if not hashAlgo: + hashAlgo = SHA1 + password = tobytes(password) + pHash = hashAlgo.new(password+salt) + digest = pHash.digest_size + if dkLen>digest: + raise ValueError("Selected hash algorithm has a too short digest (%d bytes)." % digest) + if len(salt)!=8: + raise ValueError("Salt is not 8 bytes long.") + for i in xrange(count-1): + pHash = pHash.new(pHash.digest()) + return pHash.digest()[:dkLen] + +def PBKDF2(password, salt, dkLen=16, count=1000, prf=None): + """Derive one or more keys from a password (or passphrase). + + This performs key derivation according to the PKCS#5 standard (v2.0), + by means of the ``PBKDF2`` algorithm. + + :Parameters: + password : string + The secret password or pass phrase to generate the key from. + salt : string + A string to use for better protection from dictionary attacks. + This value does not need to be kept secret, but it should be randomly + chosen for each derivation. It is recommended to be at least 8 bytes long. + dkLen : integer + The cumulative length of the desired keys. Default is 16 bytes, suitable for instance for `Crypto.Cipher.AES`. + count : integer + The number of iterations to carry out. It's recommended to use at least 1000. + prf : callable + A pseudorandom function. It must be a function that returns a pseudorandom string + from two parameters: a secret and a salt. If not specified, HMAC-SHA1 is used. + + :Return: A byte string of length `dkLen` that can be used as key material. + If you wanted multiple keys, just break up this string into segments of the desired length. +""" + password = tobytes(password) + if prf is None: + prf = lambda p,s: HMAC.new(p,s,SHA1).digest() + key = b('') + i = 1 + while len(key)I", i)) + for j in xrange(count-1): + previousU = t = prf(password,previousU) + U = strxor(U,t) + key += U + i = i + 1 + return key[:dkLen] + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Protocol/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Protocol/__init__.py new file mode 100644 index 0000000..cacc685 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Protocol/__init__.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Cryptographic protocols + +Implements various cryptographic protocols. (Don't expect to find +network protocols here.) + +Crypto.Protocol.AllOrNothing + Transforms a message into a set of message blocks, such that the blocks + can be recombined to get the message back. + +Crypto.Protocol.Chaffing + Takes a set of authenticated message blocks (the wheat) and adds a number + of randomly generated blocks (the chaff). + +Crypto.Protocol.KDF + A collection of standard key derivation functions. + +:undocumented: __revision__ +""" + +__all__ = ['AllOrNothing', 'Chaffing', 'KDF'] +__revision__ = "$Id$" diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/PublicKey/DSA.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/PublicKey/DSA.py new file mode 100644 index 0000000..d6bffd6 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/PublicKey/DSA.py @@ -0,0 +1,379 @@ +# -*- coding: utf-8 -*- +# +# PublicKey/DSA.py : DSA signature primitive +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""DSA public-key signature algorithm. + +DSA_ is a widespread public-key signature algorithm. Its security is +based on the discrete logarithm problem (DLP_). Given a cyclic +group, a generator *g*, and an element *h*, it is hard +to find an integer *x* such that *g^x = h*. The problem is believed +to be difficult, and it has been proved such (and therefore secure) for +more than 30 years. + +The group is actually a sub-group over the integers modulo *p*, with *p* prime. +The sub-group order is *q*, which is prime too; it always holds that *(p-1)* is a multiple of *q*. +The cryptographic strength is linked to the magnitude of *p* and *q*. +The signer holds a value *x* (*0>> from Crypto.Random import random + >>> from Crypto.PublicKey import DSA + >>> from Crypto.Hash import SHA + >>> + >>> message = "Hello" + >>> key = DSA.generate(1024) + >>> h = SHA.new(message).digest() + >>> k = random.StrongRandom().randint(1,key.q-1) + >>> sig = key.sign(h,k) + >>> ... + >>> if key.verify(h,sig): + >>> print "OK" + >>> else: + >>> print "Incorrect signature" + +.. _DSA: http://en.wikipedia.org/wiki/Digital_Signature_Algorithm +.. _DLP: http://www.cosic.esat.kuleuven.be/publications/talk-78.pdf +.. _ECRYPT: http://www.ecrypt.eu.org/documents/D.SPA.17.pdf +""" + +__revision__ = "$Id$" + +__all__ = ['generate', 'construct', 'error', 'DSAImplementation', '_DSAobj'] + +import sys +if sys.version_info[0] == 2 and sys.version_info[1] == 1: + from Crypto.Util.py21compat import * + +from Crypto.PublicKey import _DSA, _slowmath, pubkey +from Crypto import Random + +try: + from Crypto.PublicKey import _fastmath +except ImportError: + _fastmath = None + +class _DSAobj(pubkey.pubkey): + """Class defining an actual DSA key. + + :undocumented: __getstate__, __setstate__, __repr__, __getattr__ + """ + #: Dictionary of DSA parameters. + #: + #: A public key will only have the following entries: + #: + #: - **y**, the public key. + #: - **g**, the generator. + #: - **p**, the modulus. + #: - **q**, the order of the sub-group. + #: + #: A private key will also have: + #: + #: - **x**, the private key. + keydata = ['y', 'g', 'p', 'q', 'x'] + + def __init__(self, implementation, key): + self.implementation = implementation + self.key = key + + def __getattr__(self, attrname): + if attrname in self.keydata: + # For backward compatibility, allow the user to get (not set) the + # DSA key parameters directly from this object. + return getattr(self.key, attrname) + else: + raise AttributeError("%s object has no %r attribute" % (self.__class__.__name__, attrname,)) + + def sign(self, M, K): + """Sign a piece of data with DSA. + + :Parameter M: The piece of data to sign with DSA. It may + not be longer in bit size than the sub-group order (*q*). + :Type M: byte string or long + + :Parameter K: A secret number, chosen randomly in the closed + range *[1,q-1]*. + :Type K: long (recommended) or byte string (not recommended) + + :attention: selection of *K* is crucial for security. Generating a + random number larger than *q* and taking the modulus by *q* is + **not** secure, since smaller values will occur more frequently. + Generating a random number systematically smaller than *q-1* + (e.g. *floor((q-1)/8)* random bytes) is also **not** secure. In general, + it shall not be possible for an attacker to know the value of `any + bit of K`__. + + :attention: The number *K* shall not be reused for any other + operation and shall be discarded immediately. + + :attention: M must be a digest cryptographic hash, otherwise + an attacker may mount an existential forgery attack. + + :Return: A tuple with 2 longs. + + .. __: http://www.di.ens.fr/~pnguyen/pub_NgSh00.htm + """ + return pubkey.pubkey.sign(self, M, K) + + def verify(self, M, signature): + """Verify the validity of a DSA signature. + + :Parameter M: The expected message. + :Type M: byte string or long + + :Parameter signature: The DSA signature to verify. + :Type signature: A tuple with 2 longs as return by `sign` + + :Return: True if the signature is correct, False otherwise. + """ + return pubkey.pubkey.verify(self, M, signature) + + def _encrypt(self, c, K): + raise TypeError("DSA cannot encrypt") + + def _decrypt(self, c): + raise TypeError("DSA cannot decrypt") + + def _blind(self, m, r): + raise TypeError("DSA cannot blind") + + def _unblind(self, m, r): + raise TypeError("DSA cannot unblind") + + def _sign(self, m, k): + return self.key._sign(m, k) + + def _verify(self, m, sig): + (r, s) = sig + return self.key._verify(m, r, s) + + def has_private(self): + return self.key.has_private() + + def size(self): + return self.key.size() + + def can_blind(self): + return False + + def can_encrypt(self): + return False + + def can_sign(self): + return True + + def publickey(self): + return self.implementation.construct((self.key.y, self.key.g, self.key.p, self.key.q)) + + def __getstate__(self): + d = {} + for k in self.keydata: + try: + d[k] = getattr(self.key, k) + except AttributeError: + pass + return d + + def __setstate__(self, d): + if not hasattr(self, 'implementation'): + self.implementation = DSAImplementation() + t = [] + for k in self.keydata: + if not d.has_key(k): + break + t.append(d[k]) + self.key = self.implementation._math.dsa_construct(*tuple(t)) + + def __repr__(self): + attrs = [] + for k in self.keydata: + if k == 'p': + attrs.append("p(%d)" % (self.size()+1,)) + elif hasattr(self.key, k): + attrs.append(k) + if self.has_private(): + attrs.append("private") + # PY3K: This is meant to be text, do not change to bytes (data) + return "<%s @0x%x %s>" % (self.__class__.__name__, id(self), ",".join(attrs)) + +class DSAImplementation(object): + """ + A DSA key factory. + + This class is only internally used to implement the methods of the + `Crypto.PublicKey.DSA` module. + """ + + def __init__(self, **kwargs): + """Create a new DSA key factory. + + :Keywords: + use_fast_math : bool + Specify which mathematic library to use: + + - *None* (default). Use fastest math available. + - *True* . Use fast math. + - *False* . Use slow math. + default_randfunc : callable + Specify how to collect random data: + + - *None* (default). Use Random.new().read(). + - not *None* . Use the specified function directly. + :Raise RuntimeError: + When **use_fast_math** =True but fast math is not available. + """ + use_fast_math = kwargs.get('use_fast_math', None) + if use_fast_math is None: # Automatic + if _fastmath is not None: + self._math = _fastmath + else: + self._math = _slowmath + + elif use_fast_math: # Explicitly select fast math + if _fastmath is not None: + self._math = _fastmath + else: + raise RuntimeError("fast math module not available") + + else: # Explicitly select slow math + self._math = _slowmath + + self.error = self._math.error + + # 'default_randfunc' parameter: + # None (default) - use Random.new().read + # not None - use the specified function + self._default_randfunc = kwargs.get('default_randfunc', None) + self._current_randfunc = None + + def _get_randfunc(self, randfunc): + if randfunc is not None: + return randfunc + elif self._current_randfunc is None: + self._current_randfunc = Random.new().read + return self._current_randfunc + + def generate(self, bits, randfunc=None, progress_func=None): + """Randomly generate a fresh, new DSA key. + + :Parameters: + bits : int + Key length, or size (in bits) of the DSA modulus + *p*. + It must be a multiple of 64, in the closed + interval [512,1024]. + randfunc : callable + Random number generation function; it should accept + a single integer N and return a string of random data + N bytes long. + If not specified, a new one will be instantiated + from ``Crypto.Random``. + progress_func : callable + Optional function that will be called with a short string + containing the key parameter currently being generated; + it's useful for interactive applications where a user is + waiting for a key to be generated. + + :attention: You should always use a cryptographically secure random number generator, + such as the one defined in the ``Crypto.Random`` module; **don't** just use the + current time and the ``random`` module. + + :Return: A DSA key object (`_DSAobj`). + + :Raise ValueError: + When **bits** is too little, too big, or not a multiple of 64. + """ + + # Check against FIPS 186-2, which says that the size of the prime p + # must be a multiple of 64 bits between 512 and 1024 + for i in (0, 1, 2, 3, 4, 5, 6, 7, 8): + if bits == 512 + 64*i: + return self._generate(bits, randfunc, progress_func) + + # The March 2006 draft of FIPS 186-3 also allows 2048 and 3072-bit + # primes, but only with longer q values. Since the current DSA + # implementation only supports a 160-bit q, we don't support larger + # values. + raise ValueError("Number of bits in p must be a multiple of 64 between 512 and 1024, not %d bits" % (bits,)) + + def _generate(self, bits, randfunc=None, progress_func=None): + rf = self._get_randfunc(randfunc) + obj = _DSA.generate_py(bits, rf, progress_func) # TODO: Don't use legacy _DSA module + key = self._math.dsa_construct(obj.y, obj.g, obj.p, obj.q, obj.x) + return _DSAobj(self, key) + + def construct(self, tup): + """Construct a DSA key from a tuple of valid DSA components. + + The modulus *p* must be a prime. + + The following equations must apply: + + - p-1 = 0 mod q + - g^x = y mod p + - 0 < x < q + - 1 < g < p + + :Parameters: + tup : tuple + A tuple of long integers, with 4 or 5 items + in the following order: + + 1. Public key (*y*). + 2. Sub-group generator (*g*). + 3. Modulus, finite field order (*p*). + 4. Sub-group order (*q*). + 5. Private key (*x*). Optional. + + :Return: A DSA key object (`_DSAobj`). + """ + key = self._math.dsa_construct(*tup) + return _DSAobj(self, key) + +_impl = DSAImplementation() +generate = _impl.generate +construct = _impl.construct +error = _impl.error + +# vim:set ts=4 sw=4 sts=4 expandtab: + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/PublicKey/ElGamal.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/PublicKey/ElGamal.py new file mode 100644 index 0000000..99af71c --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/PublicKey/ElGamal.py @@ -0,0 +1,373 @@ +# +# ElGamal.py : ElGamal encryption/decryption and signatures +# +# Part of the Python Cryptography Toolkit +# +# Originally written by: A.M. Kuchling +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""ElGamal public-key algorithm (randomized encryption and signature). + +Signature algorithm +------------------- +The security of the ElGamal signature scheme is based (like DSA) on the discrete +logarithm problem (DLP_). Given a cyclic group, a generator *g*, +and an element *h*, it is hard to find an integer *x* such that *g^x = h*. + +The group is the largest multiplicative sub-group of the integers modulo *p*, +with *p* prime. +The signer holds a value *x* (*0>> from Crypto import Random + >>> from Crypto.Random import random + >>> from Crypto.PublicKey import ElGamal + >>> from Crypto.Util.number import GCD + >>> from Crypto.Hash import SHA + >>> + >>> message = "Hello" + >>> key = ElGamal.generate(1024, Random.new().read) + >>> h = SHA.new(message).digest() + >>> while 1: + >>> k = random.StrongRandom().randint(1,key.p-1) + >>> if GCD(k,key.p-1)==1: break + >>> sig = key.sign(h,k) + >>> ... + >>> if key.verify(h,sig): + >>> print "OK" + >>> else: + >>> print "Incorrect signature" + +.. _DLP: http://www.cosic.esat.kuleuven.be/publications/talk-78.pdf +.. _CDH: http://en.wikipedia.org/wiki/Computational_Diffie%E2%80%93Hellman_assumption +.. _ECRYPT: http://www.ecrypt.eu.org/documents/D.SPA.17.pdf +""" + +__revision__ = "$Id$" + +__all__ = ['generate', 'construct', 'error', 'ElGamalobj'] + +from Crypto.PublicKey.pubkey import * +from Crypto.Util import number + +class error (Exception): + pass + +# Generate an ElGamal key with N bits +def generate(bits, randfunc, progress_func=None): + """Randomly generate a fresh, new ElGamal key. + + The key will be safe for use for both encryption and signature + (although it should be used for **only one** purpose). + + :Parameters: + bits : int + Key length, or size (in bits) of the modulus *p*. + Recommended value is 2048. + randfunc : callable + Random number generation function; it should accept + a single integer N and return a string of random data + N bytes long. + progress_func : callable + Optional function that will be called with a short string + containing the key parameter currently being generated; + it's useful for interactive applications where a user is + waiting for a key to be generated. + + :attention: You should always use a cryptographically secure random number generator, + such as the one defined in the ``Crypto.Random`` module; **don't** just use the + current time and the ``random`` module. + + :Return: An ElGamal key object (`ElGamalobj`). + """ + obj=ElGamalobj() + # Generate a safe prime p + # See Algorithm 4.86 in Handbook of Applied Cryptography + if progress_func: + progress_func('p\n') + while 1: + q = bignum(getPrime(bits-1, randfunc)) + obj.p = 2*q+1 + if number.isPrime(obj.p, randfunc=randfunc): + break + # Generate generator g + # See Algorithm 4.80 in Handbook of Applied Cryptography + # Note that the order of the group is n=p-1=2q, where q is prime + if progress_func: + progress_func('g\n') + while 1: + # We must avoid g=2 because of Bleichenbacher's attack described + # in "Generating ElGamal signatures without knowning the secret key", + # 1996 + # + obj.g = number.getRandomRange(3, obj.p, randfunc) + safe = 1 + if pow(obj.g, 2, obj.p)==1: + safe=0 + if safe and pow(obj.g, q, obj.p)==1: + safe=0 + # Discard g if it divides p-1 because of the attack described + # in Note 11.67 (iii) in HAC + if safe and divmod(obj.p-1, obj.g)[1]==0: + safe=0 + # g^{-1} must not divide p-1 because of Khadir's attack + # described in "Conditions of the generator for forging ElGamal + # signature", 2011 + ginv = number.inverse(obj.g, obj.p) + if safe and divmod(obj.p-1, ginv)[1]==0: + safe=0 + if safe: + break + # Generate private key x + if progress_func: + progress_func('x\n') + obj.x=number.getRandomRange(2, obj.p-1, randfunc) + # Generate public key y + if progress_func: + progress_func('y\n') + obj.y = pow(obj.g, obj.x, obj.p) + return obj + +def construct(tup): + """Construct an ElGamal key from a tuple of valid ElGamal components. + + The modulus *p* must be a prime. + + The following conditions must apply: + + - 1 < g < p-1 + - g^{p-1} = 1 mod p + - 1 < x < p-1 + - g^x = y mod p + + :Parameters: + tup : tuple + A tuple of long integers, with 3 or 4 items + in the following order: + + 1. Modulus (*p*). + 2. Generator (*g*). + 3. Public key (*y*). + 4. Private key (*x*). Optional. + + :Return: An ElGamal key object (`ElGamalobj`). + """ + + obj=ElGamalobj() + if len(tup) not in [3,4]: + raise ValueError('argument for construct() wrong length') + for i in range(len(tup)): + field = obj.keydata[i] + setattr(obj, field, tup[i]) + return obj + +class ElGamalobj(pubkey): + """Class defining an ElGamal key. + + :undocumented: __getstate__, __setstate__, __repr__, __getattr__ + """ + + #: Dictionary of ElGamal parameters. + #: + #: A public key will only have the following entries: + #: + #: - **y**, the public key. + #: - **g**, the generator. + #: - **p**, the modulus. + #: + #: A private key will also have: + #: + #: - **x**, the private key. + keydata=['p', 'g', 'y', 'x'] + + def encrypt(self, plaintext, K): + """Encrypt a piece of data with ElGamal. + + :Parameter plaintext: The piece of data to encrypt with ElGamal. + It must be numerically smaller than the module (*p*). + :Type plaintext: byte string or long + + :Parameter K: A secret number, chosen randomly in the closed + range *[1,p-2]*. + :Type K: long (recommended) or byte string (not recommended) + + :Return: A tuple with two items. Each item is of the same type as the + plaintext (string or long). + + :attention: selection of *K* is crucial for security. Generating a + random number larger than *p-1* and taking the modulus by *p-1* is + **not** secure, since smaller values will occur more frequently. + Generating a random number systematically smaller than *p-1* + (e.g. *floor((p-1)/8)* random bytes) is also **not** secure. + In general, it shall not be possible for an attacker to know + the value of any bit of K. + + :attention: The number *K* shall not be reused for any other + operation and shall be discarded immediately. + """ + return pubkey.encrypt(self, plaintext, K) + + def decrypt(self, ciphertext): + """Decrypt a piece of data with ElGamal. + + :Parameter ciphertext: The piece of data to decrypt with ElGamal. + :Type ciphertext: byte string, long or a 2-item tuple as returned + by `encrypt` + + :Return: A byte string if ciphertext was a byte string or a tuple + of byte strings. A long otherwise. + """ + return pubkey.decrypt(self, ciphertext) + + def sign(self, M, K): + """Sign a piece of data with ElGamal. + + :Parameter M: The piece of data to sign with ElGamal. It may + not be longer in bit size than *p-1*. + :Type M: byte string or long + + :Parameter K: A secret number, chosen randomly in the closed + range *[1,p-2]* and such that *gcd(k,p-1)=1*. + :Type K: long (recommended) or byte string (not recommended) + + :attention: selection of *K* is crucial for security. Generating a + random number larger than *p-1* and taking the modulus by *p-1* is + **not** secure, since smaller values will occur more frequently. + Generating a random number systematically smaller than *p-1* + (e.g. *floor((p-1)/8)* random bytes) is also **not** secure. + In general, it shall not be possible for an attacker to know + the value of any bit of K. + + :attention: The number *K* shall not be reused for any other + operation and shall be discarded immediately. + + :attention: M must be be a cryptographic hash, otherwise an + attacker may mount an existential forgery attack. + + :Return: A tuple with 2 longs. + """ + return pubkey.sign(self, M, K) + + def verify(self, M, signature): + """Verify the validity of an ElGamal signature. + + :Parameter M: The expected message. + :Type M: byte string or long + + :Parameter signature: The ElGamal signature to verify. + :Type signature: A tuple with 2 longs as return by `sign` + + :Return: True if the signature is correct, False otherwise. + """ + return pubkey.verify(self, M, signature) + + def _encrypt(self, M, K): + a=pow(self.g, K, self.p) + b=( M*pow(self.y, K, self.p) ) % self.p + return ( a,b ) + + def _decrypt(self, M): + if (not hasattr(self, 'x')): + raise TypeError('Private key not available in this object') + ax=pow(M[0], self.x, self.p) + plaintext=(M[1] * inverse(ax, self.p ) ) % self.p + return plaintext + + def _sign(self, M, K): + if (not hasattr(self, 'x')): + raise TypeError('Private key not available in this object') + p1=self.p-1 + if (GCD(K, p1)!=1): + raise ValueError('Bad K value: GCD(K,p-1)!=1') + a=pow(self.g, K, self.p) + t=(M-self.x*a) % p1 + while t<0: t=t+p1 + b=(t*inverse(K, p1)) % p1 + return (a, b) + + def _verify(self, M, sig): + if sig[0]<1 or sig[0]>self.p-1: + return 0 + v1=pow(self.y, sig[0], self.p) + v1=(v1*pow(sig[0], sig[1], self.p)) % self.p + v2=pow(self.g, M, self.p) + if v1==v2: + return 1 + return 0 + + def size(self): + return number.size(self.p) - 1 + + def has_private(self): + if hasattr(self, 'x'): + return 1 + else: + return 0 + + def publickey(self): + return construct((self.p, self.g, self.y)) + + +object=ElGamalobj diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/PublicKey/RSA.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/PublicKey/RSA.py new file mode 100644 index 0000000..99d851d --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/PublicKey/RSA.py @@ -0,0 +1,719 @@ +# -*- coding: utf-8 -*- +# +# PublicKey/RSA.py : RSA public key primitive +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""RSA public-key cryptography algorithm (signature and encryption). + +RSA_ is the most widespread and used public key algorithm. Its security is +based on the difficulty of factoring large integers. The algorithm has +withstood attacks for 30 years, and it is therefore considered reasonably +secure for new designs. + +The algorithm can be used for both confidentiality (encryption) and +authentication (digital signature). It is worth noting that signing and +decryption are significantly slower than verification and encryption. +The cryptograhic strength is primarily linked to the length of the modulus *n*. +In 2012, a sufficient length is deemed to be 2048 bits. For more information, +see the most recent ECRYPT_ report. + +Both RSA ciphertext and RSA signature are as big as the modulus *n* (256 +bytes if *n* is 2048 bit long). + +This module provides facilities for generating fresh, new RSA keys, constructing +them from known components, exporting them, and importing them. + + >>> from Crypto.PublicKey import RSA + >>> + >>> key = RSA.generate(2048) + >>> f = open('mykey.pem','w') + >>> f.write(RSA.exportKey('PEM')) + >>> f.close() + ... + >>> f = open('mykey.pem','r') + >>> key = RSA.importKey(f.read()) + +Even though you may choose to directly use the methods of an RSA key object +to perform the primitive cryptographic operations (e.g. `_RSAobj.encrypt`), +it is recommended to use one of the standardized schemes instead (like +`Crypto.Cipher.PKCS1_v1_5` or `Crypto.Signature.PKCS1_v1_5`). + +.. _RSA: http://en.wikipedia.org/wiki/RSA_%28algorithm%29 +.. _ECRYPT: http://www.ecrypt.eu.org/documents/D.SPA.17.pdf + +:sort: generate,construct,importKey,error +""" + +__revision__ = "$Id$" + +__all__ = ['generate', 'construct', 'error', 'importKey', 'RSAImplementation', '_RSAobj'] + +import sys +if sys.version_info[0] == 2 and sys.version_info[1] == 1: + from Crypto.Util.py21compat import * +from Crypto.Util.py3compat import * +#from Crypto.Util.python_compat import * +from Crypto.Util.number import getRandomRange, bytes_to_long, long_to_bytes + +from Crypto.PublicKey import _RSA, _slowmath, pubkey +from Crypto import Random + +from Crypto.Util.asn1 import DerObject, DerSequence, DerNull +import binascii +import struct + +from Crypto.Util.number import inverse + +from Crypto.Util.number import inverse + +try: + from Crypto.PublicKey import _fastmath +except ImportError: + _fastmath = None + +class _RSAobj(pubkey.pubkey): + """Class defining an actual RSA key. + + :undocumented: __getstate__, __setstate__, __repr__, __getattr__ + """ + #: Dictionary of RSA parameters. + #: + #: A public key will only have the following entries: + #: + #: - **n**, the modulus. + #: - **e**, the public exponent. + #: + #: A private key will also have: + #: + #: - **d**, the private exponent. + #: - **p**, the first factor of n. + #: - **q**, the second factor of n. + #: - **u**, the CRT coefficient (1/p) mod q. + keydata = ['n', 'e', 'd', 'p', 'q', 'u'] + + def __init__(self, implementation, key, randfunc=None): + self.implementation = implementation + self.key = key + if randfunc is None: + randfunc = Random.new().read + self._randfunc = randfunc + + def __getattr__(self, attrname): + if attrname in self.keydata: + # For backward compatibility, allow the user to get (not set) the + # RSA key parameters directly from this object. + return getattr(self.key, attrname) + else: + raise AttributeError("%s object has no %r attribute" % (self.__class__.__name__, attrname,)) + + def encrypt(self, plaintext, K): + """Encrypt a piece of data with RSA. + + :Parameter plaintext: The piece of data to encrypt with RSA. It may not + be numerically larger than the RSA module (**n**). + :Type plaintext: byte string or long + + :Parameter K: A random parameter (*for compatibility only. This + value will be ignored*) + :Type K: byte string or long + + :attention: this function performs the plain, primitive RSA encryption + (*textbook*). In real applications, you always need to use proper + cryptographic padding, and you should not directly encrypt data with + this method. Failure to do so may lead to security vulnerabilities. + It is recommended to use modules + `Crypto.Cipher.PKCS1_OAEP` or `Crypto.Cipher.PKCS1_v1_5` instead. + + :Return: A tuple with two items. The first item is the ciphertext + of the same type as the plaintext (string or long). The second item + is always None. + """ + return pubkey.pubkey.encrypt(self, plaintext, K) + + def decrypt(self, ciphertext): + """Decrypt a piece of data with RSA. + + Decryption always takes place with blinding. + + :attention: this function performs the plain, primitive RSA decryption + (*textbook*). In real applications, you always need to use proper + cryptographic padding, and you should not directly decrypt data with + this method. Failure to do so may lead to security vulnerabilities. + It is recommended to use modules + `Crypto.Cipher.PKCS1_OAEP` or `Crypto.Cipher.PKCS1_v1_5` instead. + + :Parameter ciphertext: The piece of data to decrypt with RSA. It may + not be numerically larger than the RSA module (**n**). If a tuple, + the first item is the actual ciphertext; the second item is ignored. + + :Type ciphertext: byte string, long or a 2-item tuple as returned by + `encrypt` + + :Return: A byte string if ciphertext was a byte string or a tuple + of byte strings. A long otherwise. + """ + return pubkey.pubkey.decrypt(self, ciphertext) + + def sign(self, M, K): + """Sign a piece of data with RSA. + + Signing always takes place with blinding. + + :attention: this function performs the plain, primitive RSA decryption + (*textbook*). In real applications, you always need to use proper + cryptographic padding, and you should not directly sign data with + this method. Failure to do so may lead to security vulnerabilities. + It is recommended to use modules + `Crypto.Signature.PKCS1_PSS` or `Crypto.Signature.PKCS1_v1_5` instead. + + :Parameter M: The piece of data to sign with RSA. It may + not be numerically larger than the RSA module (**n**). + :Type M: byte string or long + + :Parameter K: A random parameter (*for compatibility only. This + value will be ignored*) + :Type K: byte string or long + + :Return: A 2-item tuple. The first item is the actual signature (a + long). The second item is always None. + """ + return pubkey.pubkey.sign(self, M, K) + + def verify(self, M, signature): + """Verify the validity of an RSA signature. + + :attention: this function performs the plain, primitive RSA encryption + (*textbook*). In real applications, you always need to use proper + cryptographic padding, and you should not directly verify data with + this method. Failure to do so may lead to security vulnerabilities. + It is recommended to use modules + `Crypto.Signature.PKCS1_PSS` or `Crypto.Signature.PKCS1_v1_5` instead. + + :Parameter M: The expected message. + :Type M: byte string or long + + :Parameter signature: The RSA signature to verify. The first item of + the tuple is the actual signature (a long not larger than the modulus + **n**), whereas the second item is always ignored. + :Type signature: A 2-item tuple as return by `sign` + + :Return: True if the signature is correct, False otherwise. + """ + return pubkey.pubkey.verify(self, M, signature) + + def _encrypt(self, c, K): + return (self.key._encrypt(c),) + + def _decrypt(self, c): + #(ciphertext,) = c + (ciphertext,) = c[:1] # HACK - We should use the previous line + # instead, but this is more compatible and we're + # going to replace the Crypto.PublicKey API soon + # anyway. + + # Blinded RSA decryption (to prevent timing attacks): + # Step 1: Generate random secret blinding factor r, such that 0 < r < n-1 + r = getRandomRange(1, self.key.n-1, randfunc=self._randfunc) + # Step 2: Compute c' = c * r**e mod n + cp = self.key._blind(ciphertext, r) + # Step 3: Compute m' = c'**d mod n (ordinary RSA decryption) + mp = self.key._decrypt(cp) + # Step 4: Compute m = m**(r-1) mod n + return self.key._unblind(mp, r) + + def _blind(self, m, r): + return self.key._blind(m, r) + + def _unblind(self, m, r): + return self.key._unblind(m, r) + + def _sign(self, m, K=None): + return (self.key._sign(m),) + + def _verify(self, m, sig): + #(s,) = sig + (s,) = sig[:1] # HACK - We should use the previous line instead, but + # this is more compatible and we're going to replace + # the Crypto.PublicKey API soon anyway. + return self.key._verify(m, s) + + def has_private(self): + return self.key.has_private() + + def size(self): + return self.key.size() + + def can_blind(self): + return True + + def can_encrypt(self): + return True + + def can_sign(self): + return True + + def publickey(self): + return self.implementation.construct((self.key.n, self.key.e)) + + def __getstate__(self): + d = {} + for k in self.keydata: + try: + d[k] = getattr(self.key, k) + except AttributeError: + pass + return d + + def __setstate__(self, d): + if not hasattr(self, 'implementation'): + self.implementation = RSAImplementation() + t = [] + for k in self.keydata: + if not d.has_key(k): + break + t.append(d[k]) + self.key = self.implementation._math.rsa_construct(*tuple(t)) + + def __repr__(self): + attrs = [] + for k in self.keydata: + if k == 'n': + attrs.append("n(%d)" % (self.size()+1,)) + elif hasattr(self.key, k): + attrs.append(k) + if self.has_private(): + attrs.append("private") + # PY3K: This is meant to be text, do not change to bytes (data) + return "<%s @0x%x %s>" % (self.__class__.__name__, id(self), ",".join(attrs)) + + def exportKey(self, format='PEM', passphrase=None, pkcs=1): + """Export this RSA key. + + :Parameter format: The format to use for wrapping the key. + + - *'DER'*. Binary encoding, always unencrypted. + - *'PEM'*. Textual encoding, done according to `RFC1421`_/`RFC1423`_. + Unencrypted (default) or encrypted. + - *'OpenSSH'*. Textual encoding, done according to OpenSSH specification. + Only suitable for public keys (not private keys). + :Type format: string + + :Parameter passphrase: In case of PEM, the pass phrase to derive the encryption key from. + :Type passphrase: string + + :Parameter pkcs: The PKCS standard to follow for assembling the key. + You have two choices: + + - with **1**, the public key is embedded into an X.509 `SubjectPublicKeyInfo` DER SEQUENCE. + The private key is embedded into a `PKCS#1`_ `RSAPrivateKey` DER SEQUENCE. + This mode is the default. + - with **8**, the private key is embedded into a `PKCS#8`_ `PrivateKeyInfo` DER SEQUENCE. + This mode is not available for public keys. + + PKCS standards are not relevant for the *OpenSSH* format. + :Type pkcs: integer + + :Return: A byte string with the encoded public or private half. + :Raise ValueError: + When the format is unknown. + + .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt + .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt + .. _`PKCS#1`: http://www.ietf.org/rfc/rfc3447.txt + .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt + """ + if passphrase is not None: + passphrase = tobytes(passphrase) + if format=='OpenSSH': + eb = long_to_bytes(self.e) + nb = long_to_bytes(self.n) + if bord(eb[0]) & 0x80: eb=bchr(0x00)+eb + if bord(nb[0]) & 0x80: nb=bchr(0x00)+nb + keyparts = [ 'ssh-rsa', eb, nb ] + keystring = ''.join([ struct.pack(">I",len(kp))+kp for kp in keyparts]) + return 'ssh-rsa '+binascii.b2a_base64(keystring)[:-1] + + # DER format is always used, even in case of PEM, which simply + # encodes it into BASE64. + der = DerSequence() + if self.has_private(): + keyType= { 1: 'RSA PRIVATE', 8: 'PRIVATE' }[pkcs] + der[:] = [ 0, self.n, self.e, self.d, self.p, self.q, + self.d % (self.p-1), self.d % (self.q-1), + inverse(self.q, self.p) ] + if pkcs==8: + derkey = der.encode() + der = DerSequence([0]) + der.append(algorithmIdentifier) + der.append(DerObject('OCTET STRING', derkey).encode()) + else: + keyType = "PUBLIC" + der.append(algorithmIdentifier) + bitmap = DerObject('BIT STRING') + derPK = DerSequence( [ self.n, self.e ] ) + bitmap.payload = bchr(0x00) + derPK.encode() + der.append(bitmap.encode()) + if format=='DER': + return der.encode() + if format=='PEM': + pem = b("-----BEGIN " + keyType + " KEY-----\n") + objenc = None + if passphrase and keyType.endswith('PRIVATE'): + # We only support 3DES for encryption + import Crypto.Hash.MD5 + from Crypto.Cipher import DES3 + from Crypto.Protocol.KDF import PBKDF1 + salt = self._randfunc(8) + key = PBKDF1(passphrase, salt, 16, 1, Crypto.Hash.MD5) + key += PBKDF1(key+passphrase, salt, 8, 1, Crypto.Hash.MD5) + objenc = DES3.new(key, Crypto.Cipher.DES3.MODE_CBC, salt) + pem += b('Proc-Type: 4,ENCRYPTED\n') + pem += b('DEK-Info: DES-EDE3-CBC,') + binascii.b2a_hex(salt).upper() + b('\n\n') + + binaryKey = der.encode() + if objenc: + # Add PKCS#7-like padding + padding = objenc.block_size-len(binaryKey)%objenc.block_size + binaryKey = objenc.encrypt(binaryKey+bchr(padding)*padding) + + # Each BASE64 line can take up to 64 characters (=48 bytes of data) + chunks = [ binascii.b2a_base64(binaryKey[i:i+48]) for i in range(0, len(binaryKey), 48) ] + pem += b('').join(chunks) + pem += b("-----END " + keyType + " KEY-----") + return pem + return ValueError("Unknown key format '%s'. Cannot export the RSA key." % format) + +class RSAImplementation(object): + """ + An RSA key factory. + + This class is only internally used to implement the methods of the `Crypto.PublicKey.RSA` module. + + :sort: __init__,generate,construct,importKey + :undocumented: _g*, _i* + """ + + def __init__(self, **kwargs): + """Create a new RSA key factory. + + :Keywords: + use_fast_math : bool + Specify which mathematic library to use: + + - *None* (default). Use fastest math available. + - *True* . Use fast math. + - *False* . Use slow math. + default_randfunc : callable + Specify how to collect random data: + + - *None* (default). Use Random.new().read(). + - not *None* . Use the specified function directly. + :Raise RuntimeError: + When **use_fast_math** =True but fast math is not available. + """ + use_fast_math = kwargs.get('use_fast_math', None) + if use_fast_math is None: # Automatic + if _fastmath is not None: + self._math = _fastmath + else: + self._math = _slowmath + + elif use_fast_math: # Explicitly select fast math + if _fastmath is not None: + self._math = _fastmath + else: + raise RuntimeError("fast math module not available") + + else: # Explicitly select slow math + self._math = _slowmath + + self.error = self._math.error + + self._default_randfunc = kwargs.get('default_randfunc', None) + self._current_randfunc = None + + def _get_randfunc(self, randfunc): + if randfunc is not None: + return randfunc + elif self._current_randfunc is None: + self._current_randfunc = Random.new().read + return self._current_randfunc + + def generate(self, bits, randfunc=None, progress_func=None, e=65537): + """Randomly generate a fresh, new RSA key. + + :Parameters: + bits : int + Key length, or size (in bits) of the RSA modulus. + It must be a multiple of 256, and no smaller than 1024. + + randfunc : callable + Random number generation function; it should accept + a single integer N and return a string of random data + N bytes long. + If not specified, a new one will be instantiated + from ``Crypto.Random``. + + progress_func : callable + Optional function that will be called with a short string + containing the key parameter currently being generated; + it's useful for interactive applications where a user is + waiting for a key to be generated. + + e : int + Public RSA exponent. It must be an odd positive integer. + It is typically a small number with very few ones in its + binary representation. + The default value 65537 (= ``0b10000000000000001`` ) is a safe + choice: other common values are 5, 7, 17, and 257. + + :attention: You should always use a cryptographically secure random number generator, + such as the one defined in the ``Crypto.Random`` module; **don't** just use the + current time and the ``random`` module. + + :attention: Exponent 3 is also widely used, but it requires very special care when padding + the message. + + :Return: An RSA key object (`_RSAobj`). + + :Raise ValueError: + When **bits** is too little or not a multiple of 256, or when + **e** is not odd or smaller than 2. + """ + if bits < 1024 or (bits & 0xff) != 0: + # pubkey.getStrongPrime doesn't like anything that's not a multiple of 256 and >= 1024 + raise ValueError("RSA modulus length must be a multiple of 256 and >= 1024") + if e%2==0 or e<3: + raise ValueError("RSA public exponent must be a positive, odd integer larger than 2.") + rf = self._get_randfunc(randfunc) + obj = _RSA.generate_py(bits, rf, progress_func, e) # TODO: Don't use legacy _RSA module + key = self._math.rsa_construct(obj.n, obj.e, obj.d, obj.p, obj.q, obj.u) + return _RSAobj(self, key) + + def construct(self, tup): + """Construct an RSA key from a tuple of valid RSA components. + + The modulus **n** must be the product of two primes. + The public exponent **e** must be odd and larger than 1. + + In case of a private key, the following equations must apply: + + - e != 1 + - p*q = n + - e*d = 1 mod (p-1)(q-1) + - p*u = 1 mod q + + :Parameters: + tup : tuple + A tuple of long integers, with at least 2 and no + more than 6 items. The items come in the following order: + + 1. RSA modulus (n). + 2. Public exponent (e). + 3. Private exponent (d). Only required if the key is private. + 4. First factor of n (p). Optional. + 5. Second factor of n (q). Optional. + 6. CRT coefficient, (1/p) mod q (u). Optional. + + :Return: An RSA key object (`_RSAobj`). + """ + key = self._math.rsa_construct(*tup) + return _RSAobj(self, key) + + def _importKeyDER(self, externKey): + """Import an RSA key (public or private half), encoded in DER form.""" + + try: + + der = DerSequence() + der.decode(externKey, True) + + # Try PKCS#1 first, for a private key + if len(der)==9 and der.hasOnlyInts() and der[0]==0: + # ASN.1 RSAPrivateKey element + del der[6:] # Remove d mod (p-1), d mod (q-1), and q^{-1} mod p + der.append(inverse(der[4],der[5])) # Add p^{-1} mod q + del der[0] # Remove version + return self.construct(der[:]) + + # Keep on trying PKCS#1, but now for a public key + if len(der)==2: + # The DER object is an RSAPublicKey SEQUENCE with two elements + if der.hasOnlyInts(): + return self.construct(der[:]) + # The DER object is a SubjectPublicKeyInfo SEQUENCE with two elements: + # an 'algorithm' (or 'algorithmIdentifier') SEQUENCE and a 'subjectPublicKey' BIT STRING. + # 'algorithm' takes the value given a few lines above. + # 'subjectPublicKey' encapsulates the actual ASN.1 RSAPublicKey element. + if der[0]==algorithmIdentifier: + bitmap = DerObject() + bitmap.decode(der[1], True) + if bitmap.isType('BIT STRING') and bord(bitmap.payload[0])==0x00: + der.decode(bitmap.payload[1:], True) + if len(der)==2 and der.hasOnlyInts(): + return self.construct(der[:]) + + # Try unencrypted PKCS#8 + if der[0]==0: + # The second element in the SEQUENCE is algorithmIdentifier. + # It must say RSA (see above for description). + if der[1]==algorithmIdentifier: + privateKey = DerObject() + privateKey.decode(der[2], True) + if privateKey.isType('OCTET STRING'): + return self._importKeyDER(privateKey.payload) + + except ValueError, IndexError: + pass + + raise ValueError("RSA key format is not supported") + + def importKey(self, externKey, passphrase=None): + """Import an RSA key (public or private half), encoded in standard form. + + :Parameter externKey: + The RSA key to import, encoded as a string. + + An RSA public key can be in any of the following formats: + + - X.509 `subjectPublicKeyInfo` DER SEQUENCE (binary or PEM encoding) + - `PKCS#1`_ `RSAPublicKey` DER SEQUENCE (binary or PEM encoding) + - OpenSSH (textual public key only) + + An RSA private key can be in any of the following formats: + + - PKCS#1 `RSAPrivateKey` DER SEQUENCE (binary or PEM encoding) + - `PKCS#8`_ `PrivateKeyInfo` DER SEQUENCE (binary or PEM encoding) + - OpenSSH (textual public key only) + + For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. + + In case of PEM encoding, the private key can be encrypted with DES or 3TDES according to a certain ``pass phrase``. + Only OpenSSL-compatible pass phrases are supported. + :Type externKey: string + + :Parameter passphrase: + In case of an encrypted PEM key, this is the pass phrase from which the encryption key is derived. + :Type passphrase: string + + :Return: An RSA key object (`_RSAobj`). + + :Raise ValueError/IndexError/TypeError: + When the given key cannot be parsed (possibly because the pass phrase is wrong). + + .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt + .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt + .. _`PKCS#1`: http://www.ietf.org/rfc/rfc3447.txt + .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt + """ + externKey = tobytes(externKey) + if passphrase is not None: + passphrase = tobytes(passphrase) + + if externKey.startswith(b('-----')): + # This is probably a PEM encoded key + lines = externKey.replace(b(" "),b('')).split() + keyobj = None + + # The encrypted PEM format + if lines[1].startswith(b('Proc-Type:4,ENCRYPTED')): + DEK = lines[2].split(b(':')) + if len(DEK)!=2 or DEK[0]!=b('DEK-Info') or not passphrase: + raise ValueError("PEM encryption format not supported.") + algo, salt = DEK[1].split(b(',')) + salt = binascii.a2b_hex(salt) + import Crypto.Hash.MD5 + from Crypto.Cipher import DES, DES3 + from Crypto.Protocol.KDF import PBKDF1 + if algo==b("DES-CBC"): + # This is EVP_BytesToKey in OpenSSL + key = PBKDF1(passphrase, salt, 8, 1, Crypto.Hash.MD5) + keyobj = DES.new(key, Crypto.Cipher.DES.MODE_CBC, salt) + elif algo==b("DES-EDE3-CBC"): + # Note that EVP_BytesToKey is note exactly the same as PBKDF1 + key = PBKDF1(passphrase, salt, 16, 1, Crypto.Hash.MD5) + key += PBKDF1(key+passphrase, salt, 8, 1, Crypto.Hash.MD5) + keyobj = DES3.new(key, Crypto.Cipher.DES3.MODE_CBC, salt) + else: + raise ValueError("Unsupport PEM encryption algorithm.") + lines = lines[2:] + + der = binascii.a2b_base64(b('').join(lines[1:-1])) + if keyobj: + der = keyobj.decrypt(der) + padding = bord(der[-1]) + der = der[:-padding] + return self._importKeyDER(der) + + if externKey.startswith(b('ssh-rsa ')): + # This is probably an OpenSSH key + keystring = binascii.a2b_base64(externKey.split(b(' '))[1]) + keyparts = [] + while len(keystring)>4: + l = struct.unpack(">I",keystring[:4])[0] + keyparts.append(keystring[4:4+l]) + keystring = keystring[4+l:] + e = bytes_to_long(keyparts[1]) + n = bytes_to_long(keyparts[2]) + return self.construct([n, e]) + if bord(externKey[0])==0x30: + # This is probably a DER encoded key + return self._importKeyDER(externKey) + + raise ValueError("RSA key format is not supported") + +#: This is the ASN.1 DER object that qualifies an algorithm as +#: compliant to PKCS#1 (that is, the standard RSA). +# It is found in all 'algorithm' fields (also called 'algorithmIdentifier'). +# It is a SEQUENCE with the oid assigned to RSA and with its parameters (none). +# 0x06 0x09 OBJECT IDENTIFIER, 9 bytes of payload +# 0x2A 0x86 0x48 0x86 0xF7 0x0D 0x01 0x01 0x01 +# rsaEncryption (1 2 840 113549 1 1 1) (PKCS #1) +# 0x05 0x00 NULL +algorithmIdentifier = DerSequence( + [ b('\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01'), + DerNull().encode() ] + ).encode() + +_impl = RSAImplementation() +#: +#: Randomly generate a fresh, new RSA key object. +#: +#: See `RSAImplementation.generate`. +#: +generate = _impl.generate +#: +#: Construct an RSA key object from a tuple of valid RSA components. +#: +#: See `RSAImplementation.construct`. +#: +construct = _impl.construct +#: +#: Import an RSA key (public or private half), encoded in standard form. +#: +#: See `RSAImplementation.importKey`. +#: +importKey = _impl.importKey +error = _impl.error + +# vim:set ts=4 sw=4 sts=4 expandtab: + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/PublicKey/_DSA.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/PublicKey/_DSA.py new file mode 100644 index 0000000..6b7a964 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/PublicKey/_DSA.py @@ -0,0 +1,115 @@ + +# +# DSA.py : Digital Signature Algorithm +# +# Part of the Python Cryptography Toolkit +# +# Written by Andrew Kuchling, Paul Swartz, and others +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== +# + +__revision__ = "$Id$" + +from Crypto.PublicKey.pubkey import * +from Crypto.Util import number +from Crypto.Util.number import bytes_to_long, long_to_bytes +from Crypto.Hash import SHA +from Crypto.Util.py3compat import * + +class error (Exception): + pass + +def generateQ(randfunc): + S=randfunc(20) + hash1=SHA.new(S).digest() + hash2=SHA.new(long_to_bytes(bytes_to_long(S)+1)).digest() + q = bignum(0) + for i in range(0,20): + c=bord(hash1[i])^bord(hash2[i]) + if i==0: + c=c | 128 + if i==19: + c= c | 1 + q=q*256+c + while (not isPrime(q)): + q=q+2 + if pow(2,159L) < q < pow(2,160L): + return S, q + raise RuntimeError('Bad q value generated') + +def generate_py(bits, randfunc, progress_func=None): + """generate(bits:int, randfunc:callable, progress_func:callable) + + Generate a DSA key of length 'bits', using 'randfunc' to get + random data and 'progress_func', if present, to display + the progress of the key generation. + """ + + if bits<160: + raise ValueError('Key length < 160 bits') + obj=DSAobj() + # Generate string S and prime q + if progress_func: + progress_func('p,q\n') + while (1): + S, obj.q = generateQ(randfunc) + n=divmod(bits-1, 160)[0] + C, N, V = 0, 2, {} + b=(obj.q >> 5) & 15 + powb=pow(bignum(2), b) + powL1=pow(bignum(2), bits-1) + while C<4096: + for k in range(0, n+1): + V[k]=bytes_to_long(SHA.new(S+bstr(N)+bstr(k)).digest()) + W=V[n] % powb + for k in range(n-1, -1, -1): + W=(W<<160L)+V[k] + X=W+powL1 + p=X-(X%(2*obj.q)-1) + if powL1<=p and isPrime(p): + break + C, N = C+1, N+n+1 + if C<4096: + break + if progress_func: + progress_func('4096 multiples failed\n') + + obj.p = p + power=divmod(p-1, obj.q)[0] + if progress_func: + progress_func('h,g\n') + while (1): + h=bytes_to_long(randfunc(bits)) % (p-1) + g=pow(h, power, p) + if 11: + break + obj.g=g + if progress_func: + progress_func('x,y\n') + while (1): + x=bytes_to_long(randfunc(20)) + if 0 < x < obj.q: + break + obj.x, obj.y = x, pow(g, x, p) + return obj + +class DSAobj: + pass + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/PublicKey/_RSA.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/PublicKey/_RSA.py new file mode 100644 index 0000000..9366d19 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/PublicKey/_RSA.py @@ -0,0 +1,81 @@ +# +# RSA.py : RSA encryption/decryption +# +# Part of the Python Cryptography Toolkit +# +# Written by Andrew Kuchling, Paul Swartz, and others +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== +# + +__revision__ = "$Id$" + +from Crypto.PublicKey import pubkey +from Crypto.Util import number + +def generate_py(bits, randfunc, progress_func=None, e=65537): + """generate(bits:int, randfunc:callable, progress_func:callable, e:int) + + Generate an RSA key of length 'bits', public exponent 'e'(which must be + odd), using 'randfunc' to get random data and 'progress_func', + if present, to display the progress of the key generation. + """ + obj=RSAobj() + obj.e = long(e) + + # Generate the prime factors of n + if progress_func: + progress_func('p,q\n') + p = q = 1L + while number.size(p*q) < bits: + # Note that q might be one bit longer than p if somebody specifies an odd + # number of bits for the key. (Why would anyone do that? You don't get + # more security.) + p = pubkey.getStrongPrime(bits>>1, obj.e, 1e-12, randfunc) + q = pubkey.getStrongPrime(bits - (bits>>1), obj.e, 1e-12, randfunc) + + # It's OK for p to be larger than q, but let's be + # kind to the function that will invert it for + # th calculation of u. + if p > q: + (p, q)=(q, p) + obj.p = p + obj.q = q + + if progress_func: + progress_func('u\n') + obj.u = pubkey.inverse(obj.p, obj.q) + obj.n = obj.p*obj.q + + if progress_func: + progress_func('d\n') + obj.d=pubkey.inverse(obj.e, (obj.p-1)*(obj.q-1)) + + assert bits <= 1+obj.size(), "Generated key is too small" + + return obj + +class RSAobj(pubkey.pubkey): + + def size(self): + """size() : int + Return the maximum number of bits that can be handled by this key. + """ + return number.size(self.n) - 1 + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/PublicKey/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/PublicKey/__init__.py new file mode 100644 index 0000000..503809f --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/PublicKey/__init__.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Public-key encryption and signature algorithms. + +Public-key encryption uses two different keys, one for encryption and +one for decryption. The encryption key can be made public, and the +decryption key is kept private. Many public-key algorithms can also +be used to sign messages, and some can *only* be used for signatures. + +======================== ============================================= +Module Description +======================== ============================================= +Crypto.PublicKey.DSA Digital Signature Algorithm (Signature only) +Crypto.PublicKey.ElGamal (Signing and encryption) +Crypto.PublicKey.RSA (Signing, encryption, and blinding) +======================== ============================================= + +:undocumented: _DSA, _RSA, _fastmath, _slowmath, pubkey +""" + +__all__ = ['RSA', 'DSA', 'ElGamal'] +__revision__ = "$Id$" + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/PublicKey/_fastmath.so b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/PublicKey/_fastmath.so new file mode 100755 index 0000000..2155b4c Binary files /dev/null and b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/PublicKey/_fastmath.so differ diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/PublicKey/_slowmath.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/PublicKey/_slowmath.py new file mode 100644 index 0000000..d926596 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/PublicKey/_slowmath.py @@ -0,0 +1,187 @@ +# -*- coding: utf-8 -*- +# +# PubKey/RSA/_slowmath.py : Pure Python implementation of the RSA portions of _fastmath +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Pure Python implementation of the RSA-related portions of Crypto.PublicKey._fastmath.""" + +__revision__ = "$Id$" + +__all__ = ['rsa_construct'] + +import sys + +if sys.version_info[0] == 2 and sys.version_info[1] == 1: + from Crypto.Util.py21compat import * +from Crypto.Util.number import size, inverse, GCD + +class error(Exception): + pass + +class _RSAKey(object): + def _blind(self, m, r): + # compute r**e * m (mod n) + return m * pow(r, self.e, self.n) + + def _unblind(self, m, r): + # compute m / r (mod n) + return inverse(r, self.n) * m % self.n + + def _decrypt(self, c): + # compute c**d (mod n) + if not self.has_private(): + raise TypeError("No private key") + if (hasattr(self,'p') and hasattr(self,'q') and hasattr(self,'u')): + m1 = pow(c, self.d % (self.p-1), self.p) + m2 = pow(c, self.d % (self.q-1), self.q) + h = m2 - m1 + if (h<0): + h = h + self.q + h = h*self.u % self.q + return h*self.p+m1 + return pow(c, self.d, self.n) + + def _encrypt(self, m): + # compute m**d (mod n) + return pow(m, self.e, self.n) + + def _sign(self, m): # alias for _decrypt + if not self.has_private(): + raise TypeError("No private key") + return self._decrypt(m) + + def _verify(self, m, sig): + return self._encrypt(sig) == m + + def has_private(self): + return hasattr(self, 'd') + + def size(self): + """Return the maximum number of bits that can be encrypted""" + return size(self.n) - 1 + +def rsa_construct(n, e, d=None, p=None, q=None, u=None): + """Construct an RSAKey object""" + assert isinstance(n, long) + assert isinstance(e, long) + assert isinstance(d, (long, type(None))) + assert isinstance(p, (long, type(None))) + assert isinstance(q, (long, type(None))) + assert isinstance(u, (long, type(None))) + obj = _RSAKey() + obj.n = n + obj.e = e + if d is None: + return obj + obj.d = d + if p is not None and q is not None: + obj.p = p + obj.q = q + else: + # Compute factors p and q from the private exponent d. + # We assume that n has no more than two factors. + # See 8.2.2(i) in Handbook of Applied Cryptography. + ktot = d*e-1 + # The quantity d*e-1 is a multiple of phi(n), even, + # and can be represented as t*2^s. + t = ktot + while t%2==0: + t=divmod(t,2)[0] + # Cycle through all multiplicative inverses in Zn. + # The algorithm is non-deterministic, but there is a 50% chance + # any candidate a leads to successful factoring. + # See "Digitalized Signatures and Public Key Functions as Intractable + # as Factorization", M. Rabin, 1979 + spotted = 0 + a = 2 + while not spotted and a<100: + k = t + # Cycle through all values a^{t*2^i}=a^k + while k +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +__revision__ = "$Id$" + +import sys +if sys.version_info[0] == 2 and sys.version_info[1] == 1: + from Crypto.Util.py21compat import * +from Crypto.Util.py3compat import * + +from binascii import b2a_hex +import time +import warnings + +from Crypto.pct_warnings import ClockRewindWarning +import SHAd256 + +import FortunaGenerator + +class FortunaPool(object): + """Fortuna pool type + + This object acts like a hash object, with the following differences: + + - It keeps a count (the .length attribute) of the number of bytes that + have been added to the pool + - It supports a .reset() method for in-place reinitialization + - The method to add bytes to the pool is .append(), not .update(). + """ + + digest_size = SHAd256.digest_size + + def __init__(self): + self.reset() + + def append(self, data): + self._h.update(data) + self.length += len(data) + + def digest(self): + return self._h.digest() + + def hexdigest(self): + if sys.version_info[0] == 2: + return b2a_hex(self.digest()) + else: + return b2a_hex(self.digest()).decode() + + def reset(self): + self._h = SHAd256.new() + self.length = 0 + +def which_pools(r): + """Return a list of pools indexes (in range(32)) that are to be included during reseed number r. + + According to _Practical Cryptography_, chapter 10.5.2 "Pools": + + "Pool P_i is included if 2**i is a divisor of r. Thus P_0 is used + every reseed, P_1 every other reseed, P_2 every fourth reseed, etc." + """ + # This is a separate function so that it can be unit-tested. + assert r >= 1 + retval = [] + mask = 0 + for i in range(32): + # "Pool P_i is included if 2**i is a divisor of [reseed_count]" + if (r & mask) == 0: + retval.append(i) + else: + break # optimization. once this fails, it always fails + mask = (mask << 1) | 1L + return retval + +class FortunaAccumulator(object): + + # An estimate of how many bytes we must append to pool 0 before it will + # contain 128 bits of entropy (with respect to an attack). We reseed the + # generator only after pool 0 contains `min_pool_size` bytes. Note that + # unlike with some other PRNGs, Fortuna's security does not rely on the + # accuracy of this estimate---we can accord to be optimistic here. + min_pool_size = 64 # size in bytes + + # If an attacker can predict some (but not all) of our entropy sources, the + # `min_pool_size` check may not be sufficient to prevent a successful state + # compromise extension attack. To resist this attack, Fortuna spreads the + # input across 32 pools, which are then consumed (to reseed the output + # generator) with exponentially decreasing frequency. + # + # In order to prevent an attacker from gaining knowledge of all 32 pools + # before we have a chance to fill them with enough information that the + # attacker cannot predict, we impose a rate limit of 10 reseeds/second (one + # per 100 ms). This ensures that a hypothetical 33rd pool would only be + # needed after a minimum of 13 years of sustained attack. + reseed_interval = 0.100 # time in seconds + + def __init__(self): + self.reseed_count = 0 + self.generator = FortunaGenerator.AESGenerator() + self.last_reseed = None + + # Initialize 32 FortunaPool instances. + # NB: This is _not_ equivalent to [FortunaPool()]*32, which would give + # us 32 references to the _same_ FortunaPool instance (and cause the + # assertion below to fail). + self.pools = [FortunaPool() for i in range(32)] # 32 pools + assert(self.pools[0] is not self.pools[1]) + + def _forget_last_reseed(self): + # This is not part of the standard Fortuna definition, and using this + # function frequently can weaken Fortuna's ability to resist a state + # compromise extension attack, but we need this in order to properly + # implement Crypto.Random.atfork(). Otherwise, forked child processes + # might continue to use their parent's PRNG state for up to 100ms in + # some cases. (e.g. CVE-2013-1445) + self.last_reseed = None + + def random_data(self, bytes): + current_time = time.time() + if (self.last_reseed is not None and self.last_reseed > current_time): # Avoid float comparison to None to make Py3k happy + warnings.warn("Clock rewind detected. Resetting last_reseed.", ClockRewindWarning) + self.last_reseed = None + if (self.pools[0].length >= self.min_pool_size and + (self.last_reseed is None or + current_time > self.last_reseed + self.reseed_interval)): + self._reseed(current_time) + # The following should fail if we haven't seeded the pool yet. + return self.generator.pseudo_random_data(bytes) + + def _reseed(self, current_time=None): + if current_time is None: + current_time = time.time() + seed = [] + self.reseed_count += 1 + self.last_reseed = current_time + for i in which_pools(self.reseed_count): + seed.append(self.pools[i].digest()) + self.pools[i].reset() + + seed = b("").join(seed) + self.generator.reseed(seed) + + def add_random_event(self, source_number, pool_number, data): + assert 1 <= len(data) <= 32 + assert 0 <= source_number <= 255 + assert 0 <= pool_number <= 31 + self.pools[pool_number].append(bchr(source_number)) + self.pools[pool_number].append(bchr(len(data))) + self.pools[pool_number].append(data) + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/Fortuna/FortunaGenerator.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/Fortuna/FortunaGenerator.py new file mode 100644 index 0000000..723fa63 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/Fortuna/FortunaGenerator.py @@ -0,0 +1,132 @@ +# -*- coding: ascii -*- +# +# FortunaGenerator.py : Fortuna's internal PRNG +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +__revision__ = "$Id$" + +import sys +if sys.version_info[0] is 2 and sys.version_info[1] is 1: + from Crypto.Util.py21compat import * +from Crypto.Util.py3compat import * + +import struct + +from Crypto.Util.number import ceil_shift, exact_log2, exact_div +from Crypto.Util import Counter +from Crypto.Cipher import AES + +import SHAd256 + +class AESGenerator(object): + """The Fortuna "generator" + + This is used internally by the Fortuna PRNG to generate arbitrary amounts + of pseudorandom data from a smaller amount of seed data. + + The output is generated by running AES-256 in counter mode and re-keying + after every mebibyte (2**16 blocks) of output. + """ + + block_size = AES.block_size # output block size in octets (128 bits) + key_size = 32 # key size in octets (256 bits) + + # Because of the birthday paradox, we expect to find approximately one + # collision for every 2**64 blocks of output from a real random source. + # However, this code generates pseudorandom data by running AES in + # counter mode, so there will be no collisions until the counter + # (theoretically) wraps around at 2**128 blocks. Thus, in order to prevent + # Fortuna's pseudorandom output from deviating perceptibly from a true + # random source, Ferguson and Schneier specify a limit of 2**16 blocks + # without rekeying. + max_blocks_per_request = 2**16 # Allow no more than this number of blocks per _pseudo_random_data request + + _four_kiblocks_of_zeros = b("\0") * block_size * 4096 + + def __init__(self): + self.counter = Counter.new(nbits=self.block_size*8, initial_value=0, little_endian=True) + self.key = None + + # Set some helper constants + self.block_size_shift = exact_log2(self.block_size) + assert (1 << self.block_size_shift) == self.block_size + + self.blocks_per_key = exact_div(self.key_size, self.block_size) + assert self.key_size == self.blocks_per_key * self.block_size + + self.max_bytes_per_request = self.max_blocks_per_request * self.block_size + + def reseed(self, seed): + if self.key is None: + self.key = b("\0") * self.key_size + + self._set_key(SHAd256.new(self.key + seed).digest()) + self.counter() # increment counter + assert len(self.key) == self.key_size + + def pseudo_random_data(self, bytes): + assert bytes >= 0 + + num_full_blocks = bytes >> 20 + remainder = bytes & ((1<<20)-1) + + retval = [] + for i in xrange(num_full_blocks): + retval.append(self._pseudo_random_data(1<<20)) + retval.append(self._pseudo_random_data(remainder)) + + return b("").join(retval) + + def _set_key(self, key): + self.key = key + self._cipher = AES.new(key, AES.MODE_CTR, counter=self.counter) + + def _pseudo_random_data(self, bytes): + if not (0 <= bytes <= self.max_bytes_per_request): + raise AssertionError("You cannot ask for more than 1 MiB of data per request") + + num_blocks = ceil_shift(bytes, self.block_size_shift) # num_blocks = ceil(bytes / self.block_size) + + # Compute the output + retval = self._generate_blocks(num_blocks)[:bytes] + + # Switch to a new key to avoid later compromises of this output (i.e. + # state compromise extension attacks) + self._set_key(self._generate_blocks(self.blocks_per_key)) + + assert len(retval) == bytes + assert len(self.key) == self.key_size + + return retval + + def _generate_blocks(self, num_blocks): + if self.key is None: + raise AssertionError("generator must be seeded before use") + assert 0 <= num_blocks <= self.max_blocks_per_request + retval = [] + for i in xrange(num_blocks >> 12): # xrange(num_blocks / 4096) + retval.append(self._cipher.encrypt(self._four_kiblocks_of_zeros)) + remaining_bytes = (num_blocks & 4095) << self.block_size_shift # (num_blocks % 4095) * self.block_size + retval.append(self._cipher.encrypt(self._four_kiblocks_of_zeros[:remaining_bytes])) + return b("").join(retval) + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/Fortuna/SHAd256.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/Fortuna/SHAd256.py new file mode 100644 index 0000000..2e135c9 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/Fortuna/SHAd256.py @@ -0,0 +1,98 @@ +# -*- coding: ascii -*- +# +# Random/Fortuna/SHAd256.py : SHA_d-256 hash function implementation +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""\ +SHA_d-256 hash function implementation. + +This module should comply with PEP 247. +""" + +__revision__ = "$Id$" +__all__ = ['new', 'digest_size'] + +import sys +if sys.version_info[0] == 2 and sys.version_info[1] == 1: + from Crypto.Util.py21compat import * +from Crypto.Util.py3compat import * + +from binascii import b2a_hex + +from Crypto.Hash import SHA256 + +assert SHA256.digest_size == 32 + +class _SHAd256(object): + """SHA-256, doubled. + + Returns SHA-256(SHA-256(data)). + """ + + digest_size = SHA256.digest_size + + _internal = object() + + def __init__(self, internal_api_check, sha256_hash_obj): + if internal_api_check is not self._internal: + raise AssertionError("Do not instantiate this class directly. Use %s.new()" % (__name__,)) + self._h = sha256_hash_obj + + # PEP 247 "copy" method + def copy(self): + """Return a copy of this hashing object""" + return _SHAd256(SHAd256._internal, self._h.copy()) + + # PEP 247 "digest" method + def digest(self): + """Return the hash value of this object as a binary string""" + retval = SHA256.new(self._h.digest()).digest() + assert len(retval) == 32 + return retval + + # PEP 247 "hexdigest" method + def hexdigest(self): + """Return the hash value of this object as a (lowercase) hexadecimal string""" + retval = b2a_hex(self.digest()) + assert len(retval) == 64 + if sys.version_info[0] == 2: + return retval + else: + return retval.decode() + + # PEP 247 "update" method + def update(self, data): + self._h.update(data) + +# PEP 247 module-level "digest_size" variable +digest_size = _SHAd256.digest_size + +# PEP 247 module-level "new" function +def new(data=None): + """Return a new SHAd256 hashing object""" + if not data: + data=b("") + sha = _SHAd256(_SHAd256._internal, SHA256.new(data)) + sha.new = globals()['new'] + return sha + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/Fortuna/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/Fortuna/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/OSRNG/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/OSRNG/__init__.py new file mode 100644 index 0000000..2fbbecb --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/OSRNG/__init__.py @@ -0,0 +1,40 @@ +# +# Random/OSRNG/__init__.py : Platform-independent OS RNG API +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Provides a platform-independent interface to the random number generators +supplied by various operating systems.""" + +__revision__ = "$Id$" + +import os + +if os.name == 'posix': + from Crypto.Random.OSRNG.posix import new +elif os.name == 'nt': + from Crypto.Random.OSRNG.nt import new +elif hasattr(os, 'urandom'): + from Crypto.Random.OSRNG.fallback import new +else: + raise ImportError("Not implemented") + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/OSRNG/fallback.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/OSRNG/fallback.py new file mode 100644 index 0000000..5bb6126 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/OSRNG/fallback.py @@ -0,0 +1,46 @@ +# +# Random/OSRNG/fallback.py : Fallback entropy source for systems with os.urandom +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + + +__revision__ = "$Id$" +__all__ = ['PythonOSURandomRNG'] + +import os + +from rng_base import BaseRNG + +class PythonOSURandomRNG(BaseRNG): + + name = "" + + def __init__(self): + self._read = os.urandom + BaseRNG.__init__(self) + + def _close(self): + self._read = None + +def new(*args, **kwargs): + return PythonOSURandomRNG(*args, **kwargs) + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/OSRNG/nt.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/OSRNG/nt.py new file mode 100644 index 0000000..c1c2f44 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/OSRNG/nt.py @@ -0,0 +1,74 @@ +# +# Random/OSRNG/nt.py : OS entropy source for MS Windows +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + + +__revision__ = "$Id$" +__all__ = ['WindowsRNG'] + +import winrandom +from rng_base import BaseRNG + +class WindowsRNG(BaseRNG): + + name = "" + + def __init__(self): + self.__winrand = winrandom.new() + BaseRNG.__init__(self) + + def flush(self): + """Work around weakness in Windows RNG. + + The CryptGenRandom mechanism in some versions of Windows allows an + attacker to learn 128 KiB of past and future output. As a workaround, + this function reads 128 KiB of 'random' data from Windows and discards + it. + + For more information about the weaknesses in CryptGenRandom, see + _Cryptanalysis of the Random Number Generator of the Windows Operating + System_, by Leo Dorrendorf and Zvi Gutterman and Benny Pinkas + http://eprint.iacr.org/2007/419 + """ + if self.closed: + raise ValueError("I/O operation on closed file") + data = self.__winrand.get_bytes(128*1024) + assert (len(data) == 128*1024) + BaseRNG.flush(self) + + def _close(self): + self.__winrand = None + + def _read(self, N): + # Unfortunately, research shows that CryptGenRandom doesn't provide + # forward secrecy and fails the next-bit test unless we apply a + # workaround, which we do here. See http://eprint.iacr.org/2007/419 + # for information on the vulnerability. + self.flush() + data = self.__winrand.get_bytes(N) + self.flush() + return data + +def new(*args, **kwargs): + return WindowsRNG(*args, **kwargs) + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/OSRNG/posix.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/OSRNG/posix.py new file mode 100644 index 0000000..ca6ac05 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/OSRNG/posix.py @@ -0,0 +1,86 @@ +# +# Random/OSRNG/posix.py : OS entropy source for POSIX systems +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + + +__revision__ = "$Id$" +__all__ = ['DevURandomRNG'] + +import errno +import os +import stat + +from rng_base import BaseRNG +from Crypto.Util.py3compat import b + +class DevURandomRNG(BaseRNG): + + def __init__(self, devname=None): + if devname is None: + self.name = "/dev/urandom" + else: + self.name = devname + + # Test that /dev/urandom is a character special device + f = open(self.name, "rb", 0) + fmode = os.fstat(f.fileno())[stat.ST_MODE] + if not stat.S_ISCHR(fmode): + f.close() + raise TypeError("%r is not a character special device" % (self.name,)) + + self.__file = f + + BaseRNG.__init__(self) + + def _close(self): + self.__file.close() + + def _read(self, N): + # Starting with Python 3 open with buffering=0 returns a FileIO object. + # FileIO.read behaves like read(2) and not like fread(3) and thus we + # have to handle the case that read returns less data as requested here + # more carefully. + data = b("") + while len(data) < N: + try: + d = self.__file.read(N - len(data)) + except IOError, e: + # read(2) has been interrupted by a signal; redo the read + if e.errno == errno.EINTR: + continue + raise + + if d is None: + # __file is in non-blocking mode and no data is available + return data + if len(d) == 0: + # __file is in blocking mode and arrived at EOF + return data + + data += d + return data + +def new(*args, **kwargs): + return DevURandomRNG(*args, **kwargs) + + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/OSRNG/rng_base.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/OSRNG/rng_base.py new file mode 100644 index 0000000..54c3aa0 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/OSRNG/rng_base.py @@ -0,0 +1,88 @@ +# +# Random/OSRNG/rng_base.py : Base class for OSRNG +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +__revision__ = "$Id$" + +import sys +if sys.version_info[0] == 2 and sys.version_info[1] == 1: + from Crypto.Util.py21compat import * + +class BaseRNG(object): + + def __init__(self): + self.closed = False + self._selftest() + + def __del__(self): + self.close() + + def _selftest(self): + # Test that urandom can return data + data = self.read(16) + if len(data) != 16: + raise AssertionError("read truncated") + + # Test that we get different data every time (if we don't, the RNG is + # probably malfunctioning) + data2 = self.read(16) + if data == data2: + raise AssertionError("OS RNG returned duplicate data") + + # PEP 343: Support for the "with" statement + def __enter__(self): + pass + def __exit__(self): + """PEP 343 support""" + self.close() + + def close(self): + if not self.closed: + self._close() + self.closed = True + + def flush(self): + pass + + def read(self, N=-1): + """Return N bytes from the RNG.""" + if self.closed: + raise ValueError("I/O operation on closed file") + if not isinstance(N, (long, int)): + raise TypeError("an integer is required") + if N < 0: + raise ValueError("cannot read to end of infinite stream") + elif N == 0: + return "" + data = self._read(N) + if len(data) != N: + raise AssertionError("%s produced truncated output (requested %d, got %d)" % (self.name, N, len(data))) + return data + + def _close(self): + raise NotImplementedError("child class must implement this") + + def _read(self, N): + raise NotImplementedError("child class must implement this") + + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/_UserFriendlyRNG.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/_UserFriendlyRNG.py new file mode 100644 index 0000000..957e006 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/_UserFriendlyRNG.py @@ -0,0 +1,230 @@ +# -*- coding: utf-8 -*- +# +# Random/_UserFriendlyRNG.py : A user-friendly random number generator +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +__revision__ = "$Id$" + +import sys +if sys.version_info[0] == 2 and sys.version_info[1] == 1: + from Crypto.Util.py21compat import * + +import os +import threading +import struct +import time +from math import floor + +from Crypto.Random import OSRNG +from Crypto.Random.Fortuna import FortunaAccumulator + +class _EntropySource(object): + def __init__(self, accumulator, src_num): + self._fortuna = accumulator + self._src_num = src_num + self._pool_num = 0 + + def feed(self, data): + self._fortuna.add_random_event(self._src_num, self._pool_num, data) + self._pool_num = (self._pool_num + 1) & 31 + +class _EntropyCollector(object): + + def __init__(self, accumulator): + self._osrng = OSRNG.new() + self._osrng_es = _EntropySource(accumulator, 255) + self._time_es = _EntropySource(accumulator, 254) + self._clock_es = _EntropySource(accumulator, 253) + + def reinit(self): + # Add 256 bits to each of the 32 pools, twice. (For a total of 16384 + # bits collected from the operating system.) + for i in range(2): + block = self._osrng.read(32*32) + for p in range(32): + self._osrng_es.feed(block[p*32:(p+1)*32]) + block = None + self._osrng.flush() + + def collect(self): + # Collect 64 bits of entropy from the operating system and feed it to Fortuna. + self._osrng_es.feed(self._osrng.read(8)) + + # Add the fractional part of time.time() + t = time.time() + self._time_es.feed(struct.pack("@I", int(2**30 * (t - floor(t))))) + + # Add the fractional part of time.clock() + t = time.clock() + self._clock_es.feed(struct.pack("@I", int(2**30 * (t - floor(t))))) + + +class _UserFriendlyRNG(object): + + def __init__(self): + self.closed = False + self._fa = FortunaAccumulator.FortunaAccumulator() + self._ec = _EntropyCollector(self._fa) + self.reinit() + + def reinit(self): + """Initialize the random number generator and seed it with entropy from + the operating system. + """ + + # Save the pid (helps ensure that Crypto.Random.atfork() gets called) + self._pid = os.getpid() + + # Collect entropy from the operating system and feed it to + # FortunaAccumulator + self._ec.reinit() + + # Override FortunaAccumulator's 100ms minimum re-seed interval. This + # is necessary to avoid a race condition between this function and + # self.read(), which that can otherwise cause forked child processes to + # produce identical output. (e.g. CVE-2013-1445) + # + # Note that if this function can be called frequently by an attacker, + # (and if the bits from OSRNG are insufficiently random) it will weaken + # Fortuna's ability to resist a state compromise extension attack. + self._fa._forget_last_reseed() + + def close(self): + self.closed = True + self._osrng = None + self._fa = None + + def flush(self): + pass + + def read(self, N): + """Return N bytes from the RNG.""" + if self.closed: + raise ValueError("I/O operation on closed file") + if not isinstance(N, (long, int)): + raise TypeError("an integer is required") + if N < 0: + raise ValueError("cannot read to end of infinite stream") + + # Collect some entropy and feed it to Fortuna + self._ec.collect() + + # Ask Fortuna to generate some bytes + retval = self._fa.random_data(N) + + # Check that we haven't forked in the meantime. (If we have, we don't + # want to use the data, because it might have been duplicated in the + # parent process. + self._check_pid() + + # Return the random data. + return retval + + def _check_pid(self): + # Lame fork detection to remind developers to invoke Random.atfork() + # after every call to os.fork(). Note that this check is not reliable, + # since process IDs can be reused on most operating systems. + # + # You need to do Random.atfork() in the child process after every call + # to os.fork() to avoid reusing PRNG state. If you want to avoid + # leaking PRNG state to child processes (for example, if you are using + # os.setuid()) then you should also invoke Random.atfork() in the + # *parent* process. + if os.getpid() != self._pid: + raise AssertionError("PID check failed. RNG must be re-initialized after fork(). Hint: Try Random.atfork()") + + +class _LockingUserFriendlyRNG(_UserFriendlyRNG): + def __init__(self): + self._lock = threading.Lock() + _UserFriendlyRNG.__init__(self) + + def close(self): + self._lock.acquire() + try: + return _UserFriendlyRNG.close(self) + finally: + self._lock.release() + + def reinit(self): + self._lock.acquire() + try: + return _UserFriendlyRNG.reinit(self) + finally: + self._lock.release() + + def read(self, bytes): + self._lock.acquire() + try: + return _UserFriendlyRNG.read(self, bytes) + finally: + self._lock.release() + +class RNGFile(object): + def __init__(self, singleton): + self.closed = False + self._singleton = singleton + + # PEP 343: Support for the "with" statement + def __enter__(self): + """PEP 343 support""" + def __exit__(self): + """PEP 343 support""" + self.close() + + def close(self): + # Don't actually close the singleton, just close this RNGFile instance. + self.closed = True + self._singleton = None + + def read(self, bytes): + if self.closed: + raise ValueError("I/O operation on closed file") + return self._singleton.read(bytes) + + def flush(self): + if self.closed: + raise ValueError("I/O operation on closed file") + +_singleton_lock = threading.Lock() +_singleton = None +def _get_singleton(): + global _singleton + _singleton_lock.acquire() + try: + if _singleton is None: + _singleton = _LockingUserFriendlyRNG() + return _singleton + finally: + _singleton_lock.release() + +def new(): + return RNGFile(_get_singleton()) + +def reinit(): + _get_singleton().reinit() + +def get_random_bytes(n): + """Return the specified number of cryptographically-strong random bytes.""" + return _get_singleton().read(n) + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/__init__.py new file mode 100644 index 0000000..659ffee --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/__init__.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +# +# Random/__init__.py : PyCrypto random number generation +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +__revision__ = "$Id$" +__all__ = ['new'] + +from Crypto.Random import OSRNG +from Crypto.Random import _UserFriendlyRNG + +def new(*args, **kwargs): + """Return a file-like object that outputs cryptographically random bytes.""" + return _UserFriendlyRNG.new(*args, **kwargs) + +def atfork(): + """Call this whenever you call os.fork()""" + _UserFriendlyRNG.reinit() + +def get_random_bytes(n): + """Return the specified number of cryptographically-strong random bytes.""" + return _UserFriendlyRNG.get_random_bytes(n) + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/random.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/random.py new file mode 100644 index 0000000..bef02e6 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Random/random.py @@ -0,0 +1,142 @@ +# -*- coding: utf-8 -*- +# +# Random/random.py : Strong alternative for the standard 'random' module +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""A cryptographically strong version of Python's standard "random" module.""" + +__revision__ = "$Id$" +__all__ = ['StrongRandom', 'getrandbits', 'randrange', 'randint', 'choice', 'shuffle', 'sample'] + +from Crypto import Random +import sys +if sys.version_info[0] == 2 and sys.version_info[1] == 1: + from Crypto.Util.py21compat import * + +class StrongRandom(object): + def __init__(self, rng=None, randfunc=None): + if randfunc is None and rng is None: + self._randfunc = None + elif randfunc is not None and rng is None: + self._randfunc = randfunc + elif randfunc is None and rng is not None: + self._randfunc = rng.read + else: + raise ValueError("Cannot specify both 'rng' and 'randfunc'") + + def getrandbits(self, k): + """Return a python long integer with k random bits.""" + if self._randfunc is None: + self._randfunc = Random.new().read + mask = (1L << k) - 1 + return mask & bytes_to_long(self._randfunc(ceil_div(k, 8))) + + def randrange(self, *args): + """randrange([start,] stop[, step]): + Return a randomly-selected element from range(start, stop, step).""" + if len(args) == 3: + (start, stop, step) = args + elif len(args) == 2: + (start, stop) = args + step = 1 + elif len(args) == 1: + (stop,) = args + start = 0 + step = 1 + else: + raise TypeError("randrange expected at most 3 arguments, got %d" % (len(args),)) + if (not isinstance(start, (int, long)) + or not isinstance(stop, (int, long)) + or not isinstance(step, (int, long))): + raise TypeError("randrange requires integer arguments") + if step == 0: + raise ValueError("randrange step argument must not be zero") + + num_choices = ceil_div(stop - start, step) + if num_choices < 0: + num_choices = 0 + if num_choices < 1: + raise ValueError("empty range for randrange(%r, %r, %r)" % (start, stop, step)) + + # Pick a random number in the range of possible numbers + r = num_choices + while r >= num_choices: + r = self.getrandbits(size(num_choices)) + + return start + (step * r) + + def randint(self, a, b): + """Return a random integer N such that a <= N <= b.""" + if not isinstance(a, (int, long)) or not isinstance(b, (int, long)): + raise TypeError("randint requires integer arguments") + N = self.randrange(a, b+1) + assert a <= N <= b + return N + + def choice(self, seq): + """Return a random element from a (non-empty) sequence. + + If the seqence is empty, raises IndexError. + """ + if len(seq) == 0: + raise IndexError("empty sequence") + return seq[self.randrange(len(seq))] + + def shuffle(self, x): + """Shuffle the sequence in place.""" + # Make a (copy) of the list of objects we want to shuffle + items = list(x) + + # Choose a random item (without replacement) until all the items have been + # chosen. + for i in xrange(len(x)): + x[i] = items.pop(self.randrange(len(items))) + + def sample(self, population, k): + """Return a k-length list of unique elements chosen from the population sequence.""" + + num_choices = len(population) + if k > num_choices: + raise ValueError("sample larger than population") + + retval = [] + selected = {} # we emulate a set using a dict here + for i in xrange(k): + r = None + while r is None or selected.has_key(r): + r = self.randrange(num_choices) + retval.append(population[r]) + selected[r] = 1 + return retval + +_r = StrongRandom() +getrandbits = _r.getrandbits +randrange = _r.randrange +randint = _r.randint +choice = _r.choice +shuffle = _r.shuffle +sample = _r.sample + +# These are at the bottom to avoid problems with recursive imports +from Crypto.Util.number import ceil_div, bytes_to_long, long_to_bytes, size + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/__init__.py new file mode 100644 index 0000000..63e9c57 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/__init__.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Cipher/__init__.py: Self-test for cipher modules +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test for cipher modules""" + +__revision__ = "$Id$" + +def get_tests(config={}): + tests = [] + from Crypto.SelfTest.Cipher import test_AES; tests += test_AES.get_tests(config=config) + from Crypto.SelfTest.Cipher import test_ARC2; tests += test_ARC2.get_tests(config=config) + from Crypto.SelfTest.Cipher import test_ARC4; tests += test_ARC4.get_tests(config=config) + from Crypto.SelfTest.Cipher import test_Blowfish; tests += test_Blowfish.get_tests(config=config) + from Crypto.SelfTest.Cipher import test_CAST; tests += test_CAST.get_tests(config=config) + from Crypto.SelfTest.Cipher import test_DES3; tests += test_DES3.get_tests(config=config) + from Crypto.SelfTest.Cipher import test_DES; tests += test_DES.get_tests(config=config) + from Crypto.SelfTest.Cipher import test_XOR; tests += test_XOR.get_tests(config=config) + from Crypto.SelfTest.Cipher import test_pkcs1_15; tests += test_pkcs1_15.get_tests(config=config) + from Crypto.SelfTest.Cipher import test_pkcs1_oaep; tests += test_pkcs1_oaep.get_tests(config=config) + return tests + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/common.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/common.py new file mode 100644 index 0000000..8bebed9 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/common.py @@ -0,0 +1,399 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Hash/common.py: Common code for Crypto.SelfTest.Hash +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-testing for PyCrypto hash modules""" + +__revision__ = "$Id$" + +import sys +import unittest +from binascii import a2b_hex, b2a_hex +from Crypto.Util.py3compat import * + +# For compatibility with Python 2.1 and Python 2.2 +if sys.hexversion < 0x02030000: + # Python 2.1 doesn't have a dict() function + # Python 2.2 dict() function raises TypeError if you do dict(MD5='blah') + def dict(**kwargs): + return kwargs.copy() +else: + dict = dict + +class _NoDefault: pass # sentinel object +def _extract(d, k, default=_NoDefault): + """Get an item from a dictionary, and remove it from the dictionary.""" + try: + retval = d[k] + except KeyError: + if default is _NoDefault: + raise + return default + del d[k] + return retval + +# Generic cipher test case +class CipherSelfTest(unittest.TestCase): + + def __init__(self, module, params): + unittest.TestCase.__init__(self) + self.module = module + + # Extract the parameters + params = params.copy() + self.description = _extract(params, 'description') + self.key = b(_extract(params, 'key')) + self.plaintext = b(_extract(params, 'plaintext')) + self.ciphertext = b(_extract(params, 'ciphertext')) + self.module_name = _extract(params, 'module_name', None) + + mode = _extract(params, 'mode', None) + self.mode_name = str(mode) + if mode is not None: + # Block cipher + self.mode = getattr(self.module, "MODE_" + mode) + self.iv = _extract(params, 'iv', None) + if self.iv is not None: self.iv = b(self.iv) + + # Only relevant for OPENPGP mode + self.encrypted_iv = _extract(params, 'encrypted_iv', None) + if self.encrypted_iv is not None: + self.encrypted_iv = b(self.encrypted_iv) + else: + # Stream cipher + self.mode = None + self.iv = None + + self.extra_params = params + + def shortDescription(self): + return self.description + + def _new(self, do_decryption=0): + params = self.extra_params.copy() + + # Handle CTR mode parameters. By default, we use Counter.new(self.module.block_size) + if hasattr(self.module, "MODE_CTR") and self.mode == self.module.MODE_CTR: + from Crypto.Util import Counter + ctr_class = _extract(params, 'ctr_class', Counter.new) + ctr_params = _extract(params, 'ctr_params', {}).copy() + if ctr_params.has_key('prefix'): ctr_params['prefix'] = a2b_hex(b(ctr_params['prefix'])) + if ctr_params.has_key('suffix'): ctr_params['suffix'] = a2b_hex(b(ctr_params['suffix'])) + if not ctr_params.has_key('nbits'): + ctr_params['nbits'] = 8*(self.module.block_size - len(ctr_params.get('prefix', '')) - len(ctr_params.get('suffix', ''))) + params['counter'] = ctr_class(**ctr_params) + + if self.mode is None: + # Stream cipher + return self.module.new(a2b_hex(self.key), **params) + elif self.iv is None: + # Block cipher without iv + return self.module.new(a2b_hex(self.key), self.mode, **params) + else: + # Block cipher with iv + if do_decryption and self.mode == self.module.MODE_OPENPGP: + # In PGP mode, the IV to feed for decryption is the *encrypted* one + return self.module.new(a2b_hex(self.key), self.mode, a2b_hex(self.encrypted_iv), **params) + else: + return self.module.new(a2b_hex(self.key), self.mode, a2b_hex(self.iv), **params) + + def runTest(self): + plaintext = a2b_hex(self.plaintext) + ciphertext = a2b_hex(self.ciphertext) + + ct1 = b2a_hex(self._new().encrypt(plaintext)) + pt1 = b2a_hex(self._new(1).decrypt(ciphertext)) + ct2 = b2a_hex(self._new().encrypt(plaintext)) + pt2 = b2a_hex(self._new(1).decrypt(ciphertext)) + + if hasattr(self.module, "MODE_OPENPGP") and self.mode == self.module.MODE_OPENPGP: + # In PGP mode, data returned by the first encrypt() + # is prefixed with the encrypted IV. + # Here we check it and then remove it from the ciphertexts. + eilen = len(self.encrypted_iv) + self.assertEqual(self.encrypted_iv, ct1[:eilen]) + self.assertEqual(self.encrypted_iv, ct2[:eilen]) + ct1 = ct1[eilen:] + ct2 = ct2[eilen:] + + self.assertEqual(self.ciphertext, ct1) # encrypt + self.assertEqual(self.ciphertext, ct2) # encrypt (second time) + self.assertEqual(self.plaintext, pt1) # decrypt + self.assertEqual(self.plaintext, pt2) # decrypt (second time) + +class CipherStreamingSelfTest(CipherSelfTest): + + def shortDescription(self): + desc = self.module_name + if self.mode is not None: + desc += " in %s mode" % (self.mode_name,) + return "%s should behave like a stream cipher" % (desc,) + + def runTest(self): + plaintext = a2b_hex(self.plaintext) + ciphertext = a2b_hex(self.ciphertext) + + # The cipher should work like a stream cipher + + # Test counter mode encryption, 3 bytes at a time + ct3 = [] + cipher = self._new() + for i in range(0, len(plaintext), 3): + ct3.append(cipher.encrypt(plaintext[i:i+3])) + ct3 = b2a_hex(b("").join(ct3)) + self.assertEqual(self.ciphertext, ct3) # encryption (3 bytes at a time) + + # Test counter mode decryption, 3 bytes at a time + pt3 = [] + cipher = self._new() + for i in range(0, len(ciphertext), 3): + pt3.append(cipher.encrypt(ciphertext[i:i+3])) + # PY3K: This is meant to be text, do not change to bytes (data) + pt3 = b2a_hex(b("").join(pt3)) + self.assertEqual(self.plaintext, pt3) # decryption (3 bytes at a time) + +class CTRSegfaultTest(unittest.TestCase): + + def __init__(self, module, params): + unittest.TestCase.__init__(self) + self.module = module + self.key = b(params['key']) + self.module_name = params.get('module_name', None) + + def shortDescription(self): + return """Regression test: %s.new(key, %s.MODE_CTR) should raise TypeError, not segfault""" % (self.module_name, self.module_name) + + def runTest(self): + self.assertRaises(TypeError, self.module.new, a2b_hex(self.key), self.module.MODE_CTR) + +class CTRWraparoundTest(unittest.TestCase): + + def __init__(self, module, params): + unittest.TestCase.__init__(self) + self.module = module + self.key = b(params['key']) + self.module_name = params.get('module_name', None) + + def shortDescription(self): + return """Regression test: %s with MODE_CTR should raise OverflowError on wraparound when shortcut used""" % (self.module_name,) + + def runTest(self): + from Crypto.Util import Counter + + for disable_shortcut in (0, 1): # (False, True) Test CTR-mode shortcut and PyObject_CallObject code paths + for little_endian in (0, 1): # (False, True) Test both endiannesses + ctr = Counter.new(8*self.module.block_size, initial_value=2L**(8*self.module.block_size)-1, little_endian=little_endian, disable_shortcut=disable_shortcut) + cipher = self.module.new(a2b_hex(self.key), self.module.MODE_CTR, counter=ctr) + block = b("\x00") * self.module.block_size + cipher.encrypt(block) + self.assertRaises(OverflowError, cipher.encrypt, block) + +class CFBSegmentSizeTest(unittest.TestCase): + + def __init__(self, module, params): + unittest.TestCase.__init__(self) + self.module = module + self.key = b(params['key']) + self.description = params['description'] + + def shortDescription(self): + return self.description + + def runTest(self): + """Regression test: m.new(key, m.MODE_CFB, segment_size=N) should require segment_size to be a multiple of 8 bits""" + for i in range(1, 8): + self.assertRaises(ValueError, self.module.new, a2b_hex(self.key), self.module.MODE_CFB, segment_size=i) + self.module.new(a2b_hex(self.key), self.module.MODE_CFB, "\0"*self.module.block_size, segment_size=8) # should succeed + +class RoundtripTest(unittest.TestCase): + def __init__(self, module, params): + from Crypto import Random + unittest.TestCase.__init__(self) + self.module = module + self.iv = Random.get_random_bytes(module.block_size) + self.key = b(params['key']) + self.plaintext = 100 * b(params['plaintext']) + self.module_name = params.get('module_name', None) + + def shortDescription(self): + return """%s .decrypt() output of .encrypt() should not be garbled""" % (self.module_name,) + + def runTest(self): + for mode in (self.module.MODE_ECB, self.module.MODE_CBC, self.module.MODE_CFB, self.module.MODE_OFB, self.module.MODE_OPENPGP): + encryption_cipher = self.module.new(a2b_hex(self.key), mode, self.iv) + ciphertext = encryption_cipher.encrypt(self.plaintext) + + if mode != self.module.MODE_OPENPGP: + decryption_cipher = self.module.new(a2b_hex(self.key), mode, self.iv) + else: + eiv = ciphertext[:self.module.block_size+2] + ciphertext = ciphertext[self.module.block_size+2:] + decryption_cipher = self.module.new(a2b_hex(self.key), mode, eiv) + decrypted_plaintext = decryption_cipher.decrypt(ciphertext) + self.assertEqual(self.plaintext, decrypted_plaintext) + +class PGPTest(unittest.TestCase): + def __init__(self, module, params): + unittest.TestCase.__init__(self) + self.module = module + self.key = b(params['key']) + + def shortDescription(self): + return "MODE_PGP was implemented incorrectly and insecurely. It's completely banished now." + + def runTest(self): + self.assertRaises(ValueError, self.module.new, a2b_hex(self.key), + self.module.MODE_PGP) + +class IVLengthTest(unittest.TestCase): + def __init__(self, module, params): + unittest.TestCase.__init__(self) + self.module = module + self.key = b(params['key']) + + def shortDescription(self): + return "Check that all modes except MODE_ECB and MODE_CTR require an IV of the proper length" + + def runTest(self): + self.assertRaises(ValueError, self.module.new, a2b_hex(self.key), + self.module.MODE_CBC, "") + self.assertRaises(ValueError, self.module.new, a2b_hex(self.key), + self.module.MODE_CFB, "") + self.assertRaises(ValueError, self.module.new, a2b_hex(self.key), + self.module.MODE_OFB, "") + self.assertRaises(ValueError, self.module.new, a2b_hex(self.key), + self.module.MODE_OPENPGP, "") + self.module.new(a2b_hex(self.key), self.module.MODE_ECB, "") + self.module.new(a2b_hex(self.key), self.module.MODE_CTR, "", counter=self._dummy_counter) + + def _dummy_counter(self): + return "\0" * self.module.block_size + +def make_block_tests(module, module_name, test_data): + tests = [] + extra_tests_added = 0 + for i in range(len(test_data)): + row = test_data[i] + + # Build the "params" dictionary + params = {'mode': 'ECB'} + if len(row) == 3: + (params['plaintext'], params['ciphertext'], params['key']) = row + elif len(row) == 4: + (params['plaintext'], params['ciphertext'], params['key'], params['description']) = row + elif len(row) == 5: + (params['plaintext'], params['ciphertext'], params['key'], params['description'], extra_params) = row + params.update(extra_params) + else: + raise AssertionError("Unsupported tuple size %d" % (len(row),)) + + # Build the display-name for the test + p2 = params.copy() + p_key = _extract(p2, 'key') + p_plaintext = _extract(p2, 'plaintext') + p_ciphertext = _extract(p2, 'ciphertext') + p_description = _extract(p2, 'description', None) + p_mode = p2.get('mode', 'ECB') + if p_mode == 'ECB': + _extract(p2, 'mode', 'ECB') + + if p_description is not None: + description = p_description + elif p_mode == 'ECB' and not p2: + description = "p=%s, k=%s" % (p_plaintext, p_key) + else: + description = "p=%s, k=%s, %r" % (p_plaintext, p_key, p2) + name = "%s #%d: %s" % (module_name, i+1, description) + params['description'] = name + params['module_name'] = module_name + + # Add extra test(s) to the test suite before the current test + if not extra_tests_added: + tests += [ + CTRSegfaultTest(module, params), + CTRWraparoundTest(module, params), + CFBSegmentSizeTest(module, params), + RoundtripTest(module, params), + PGPTest(module, params), + IVLengthTest(module, params), + ] + extra_tests_added = 1 + + # Add the current test to the test suite + tests.append(CipherSelfTest(module, params)) + + # When using CTR mode, test that the interface behaves like a stream cipher + if p_mode == 'CTR': + tests.append(CipherStreamingSelfTest(module, params)) + + # When using CTR mode, test the non-shortcut code path. + if p_mode == 'CTR' and not params.has_key('ctr_class'): + params2 = params.copy() + params2['description'] += " (shortcut disabled)" + ctr_params2 = params.get('ctr_params', {}).copy() + params2['ctr_params'] = ctr_params2 + if not params2['ctr_params'].has_key('disable_shortcut'): + params2['ctr_params']['disable_shortcut'] = 1 + tests.append(CipherSelfTest(module, params2)) + return tests + +def make_stream_tests(module, module_name, test_data): + tests = [] + for i in range(len(test_data)): + row = test_data[i] + + # Build the "params" dictionary + params = {} + if len(row) == 3: + (params['plaintext'], params['ciphertext'], params['key']) = row + elif len(row) == 4: + (params['plaintext'], params['ciphertext'], params['key'], params['description']) = row + elif len(row) == 5: + (params['plaintext'], params['ciphertext'], params['key'], params['description'], extra_params) = row + params.update(extra_params) + else: + raise AssertionError("Unsupported tuple size %d" % (len(row),)) + + # Build the display-name for the test + p2 = params.copy() + p_key = _extract(p2, 'key') + p_plaintext = _extract(p2, 'plaintext') + p_ciphertext = _extract(p2, 'ciphertext') + p_description = _extract(p2, 'description', None) + + if p_description is not None: + description = p_description + elif not p2: + description = "p=%s, k=%s" % (p_plaintext, p_key) + else: + description = "p=%s, k=%s, %r" % (p_plaintext, p_key, p2) + name = "%s #%d: %s" % (module_name, i+1, description) + params['description'] = name + params['module_name'] = module_name + + # Add the test to the test suite + tests.append(CipherSelfTest(module, params)) + tests.append(CipherStreamingSelfTest(module, params)) + return tests + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_AES.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_AES.py new file mode 100644 index 0000000..ea7c323 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_AES.py @@ -0,0 +1,1433 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Cipher/AES.py: Self-test for the AES cipher +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for Crypto.Cipher.AES""" + +__revision__ = "$Id$" + +from common import dict # For compatibility with Python 2.1 and 2.2 +from Crypto.Util.py3compat import * +from binascii import hexlify + +# This is a list of (plaintext, ciphertext, key[, description[, params]]) tuples. +test_data = [ + # FIPS PUB 197 test vectors + # http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf + + ('00112233445566778899aabbccddeeff', '69c4e0d86a7b0430d8cdb78070b4c55a', + '000102030405060708090a0b0c0d0e0f', 'FIPS 197 C.1 (AES-128)'), + + ('00112233445566778899aabbccddeeff', 'dda97ca4864cdfe06eaf70a0ec0d7191', + '000102030405060708090a0b0c0d0e0f1011121314151617', + 'FIPS 197 C.2 (AES-192)'), + + ('00112233445566778899aabbccddeeff', '8ea2b7ca516745bfeafc49904b496089', + '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f', + 'FIPS 197 C.3 (AES-256)'), + + # Rijndael128 test vectors + # Downloaded 2008-09-13 from + # http://www.iaik.tugraz.at/Research/krypto/AES/old/~rijmen/rijndael/testvalues.tar.gz + + # ecb_tbl.txt, KEYSIZE=128 + ('506812a45f08c889b97f5980038b8359', 'd8f532538289ef7d06b506a4fd5be9c9', + '00010203050607080a0b0c0d0f101112', + 'ecb-tbl-128: I=1'), + ('5c6d71ca30de8b8b00549984d2ec7d4b', '59ab30f4d4ee6e4ff9907ef65b1fb68c', + '14151617191a1b1c1e1f202123242526', + 'ecb-tbl-128: I=2'), + ('53f3f4c64f8616e4e7c56199f48f21f6', 'bf1ed2fcb2af3fd41443b56d85025cb1', + '28292a2b2d2e2f30323334353738393a', + 'ecb-tbl-128: I=3'), + ('a1eb65a3487165fb0f1c27ff9959f703', '7316632d5c32233edcb0780560eae8b2', + '3c3d3e3f41424344464748494b4c4d4e', + 'ecb-tbl-128: I=4'), + ('3553ecf0b1739558b08e350a98a39bfa', '408c073e3e2538072b72625e68b8364b', + '50515253555657585a5b5c5d5f606162', + 'ecb-tbl-128: I=5'), + ('67429969490b9711ae2b01dc497afde8', 'e1f94dfa776597beaca262f2f6366fea', + '64656667696a6b6c6e6f707173747576', + 'ecb-tbl-128: I=6'), + ('93385c1f2aec8bed192f5a8e161dd508', 'f29e986c6a1c27d7b29ffd7ee92b75f1', + '78797a7b7d7e7f80828384858788898a', + 'ecb-tbl-128: I=7'), + ('b5bf946be19beb8db3983b5f4c6e8ddb', '131c886a57f8c2e713aba6955e2b55b5', + '8c8d8e8f91929394969798999b9c9d9e', + 'ecb-tbl-128: I=8'), + ('41321ee10e21bd907227c4450ff42324', 'd2ab7662df9b8c740210e5eeb61c199d', + 'a0a1a2a3a5a6a7a8aaabacadafb0b1b2', + 'ecb-tbl-128: I=9'), + ('00a82f59c91c8486d12c0a80124f6089', '14c10554b2859c484cab5869bbe7c470', + 'b4b5b6b7b9babbbcbebfc0c1c3c4c5c6', + 'ecb-tbl-128: I=10'), + ('7ce0fd076754691b4bbd9faf8a1372fe', 'db4d498f0a49cf55445d502c1f9ab3b5', + 'c8c9cacbcdcecfd0d2d3d4d5d7d8d9da', + 'ecb-tbl-128: I=11'), + ('23605a8243d07764541bc5ad355b3129', '6d96fef7d66590a77a77bb2056667f7f', + 'dcdddedfe1e2e3e4e6e7e8e9ebecedee', + 'ecb-tbl-128: I=12'), + ('12a8cfa23ea764fd876232b4e842bc44', '316fb68edba736c53e78477bf913725c', + 'f0f1f2f3f5f6f7f8fafbfcfdfe010002', + 'ecb-tbl-128: I=13'), + ('bcaf32415e8308b3723e5fdd853ccc80', '6936f2b93af8397fd3a771fc011c8c37', + '04050607090a0b0c0e0f101113141516', + 'ecb-tbl-128: I=14'), + ('89afae685d801ad747ace91fc49adde0', 'f3f92f7a9c59179c1fcc2c2ba0b082cd', + '2c2d2e2f31323334363738393b3c3d3e', + 'ecb-tbl-128: I=15'), + ('f521d07b484357c4a69e76124a634216', '6a95ea659ee3889158e7a9152ff04ebc', + '40414243454647484a4b4c4d4f505152', + 'ecb-tbl-128: I=16'), + ('3e23b3bc065bcc152407e23896d77783', '1959338344e945670678a5d432c90b93', + '54555657595a5b5c5e5f606163646566', + 'ecb-tbl-128: I=17'), + ('79f0fba002be1744670e7e99290d8f52', 'e49bddd2369b83ee66e6c75a1161b394', + '68696a6b6d6e6f70727374757778797a', + 'ecb-tbl-128: I=18'), + ('da23fe9d5bd63e1d72e3dafbe21a6c2a', 'd3388f19057ff704b70784164a74867d', + '7c7d7e7f81828384868788898b8c8d8e', + 'ecb-tbl-128: I=19'), + ('e3f5698ba90b6a022efd7db2c7e6c823', '23aa03e2d5e4cd24f3217e596480d1e1', + 'a4a5a6a7a9aaabacaeafb0b1b3b4b5b6', + 'ecb-tbl-128: I=20'), + ('bdc2691d4f1b73d2700679c3bcbf9c6e', 'c84113d68b666ab2a50a8bdb222e91b9', + 'e0e1e2e3e5e6e7e8eaebecedeff0f1f2', + 'ecb-tbl-128: I=21'), + ('ba74e02093217ee1ba1b42bd5624349a', 'ac02403981cd4340b507963db65cb7b6', + '08090a0b0d0e0f10121314151718191a', + 'ecb-tbl-128: I=22'), + ('b5c593b5851c57fbf8b3f57715e8f680', '8d1299236223359474011f6bf5088414', + '6c6d6e6f71727374767778797b7c7d7e', + 'ecb-tbl-128: I=23'), + ('3da9bd9cec072381788f9387c3bbf4ee', '5a1d6ab8605505f7977e55b9a54d9b90', + '80818283858687888a8b8c8d8f909192', + 'ecb-tbl-128: I=24'), + ('4197f3051121702ab65d316b3c637374', '72e9c2d519cf555e4208805aabe3b258', + '94959697999a9b9c9e9fa0a1a3a4a5a6', + 'ecb-tbl-128: I=25'), + ('9f46c62ec4f6ee3f6e8c62554bc48ab7', 'a8f3e81c4a23a39ef4d745dffe026e80', + 'a8a9aaabadaeafb0b2b3b4b5b7b8b9ba', + 'ecb-tbl-128: I=26'), + ('0220673fe9e699a4ebc8e0dbeb6979c8', '546f646449d31458f9eb4ef5483aee6c', + 'bcbdbebfc1c2c3c4c6c7c8c9cbcccdce', + 'ecb-tbl-128: I=27'), + ('b2b99171337ded9bc8c2c23ff6f18867', '4dbe4bc84ac797c0ee4efb7f1a07401c', + 'd0d1d2d3d5d6d7d8dadbdcdddfe0e1e2', + 'ecb-tbl-128: I=28'), + ('a7facf4e301e984e5efeefd645b23505', '25e10bfb411bbd4d625ac8795c8ca3b3', + 'e4e5e6e7e9eaebeceeeff0f1f3f4f5f6', + 'ecb-tbl-128: I=29'), + ('f7c762e4a9819160fd7acfb6c4eedcdd', '315637405054ec803614e43def177579', + 'f8f9fafbfdfefe00020304050708090a', + 'ecb-tbl-128: I=30'), + ('9b64fc21ea08709f4915436faa70f1be', '60c5bc8a1410247295c6386c59e572a8', + '0c0d0e0f11121314161718191b1c1d1e', + 'ecb-tbl-128: I=31'), + ('52af2c3de07ee6777f55a4abfc100b3f', '01366fc8ca52dfe055d6a00a76471ba6', + '20212223252627282a2b2c2d2f303132', + 'ecb-tbl-128: I=32'), + ('2fca001224386c57aa3f968cbe2c816f', 'ecc46595516ec612449c3f581e7d42ff', + '34353637393a3b3c3e3f404143444546', + 'ecb-tbl-128: I=33'), + ('4149c73658a4a9c564342755ee2c132f', '6b7ffe4c602a154b06ee9c7dab5331c9', + '48494a4b4d4e4f50525354555758595a', + 'ecb-tbl-128: I=34'), + ('af60005a00a1772f7c07a48a923c23d2', '7da234c14039a240dd02dd0fbf84eb67', + '5c5d5e5f61626364666768696b6c6d6e', + 'ecb-tbl-128: I=35'), + ('6fccbc28363759914b6f0280afaf20c6', 'c7dc217d9e3604ffe7e91f080ecd5a3a', + '70717273757677787a7b7c7d7f808182', + 'ecb-tbl-128: I=36'), + ('7d82a43ddf4fefa2fc5947499884d386', '37785901863f5c81260ea41e7580cda5', + '84858687898a8b8c8e8f909193949596', + 'ecb-tbl-128: I=37'), + ('5d5a990eaab9093afe4ce254dfa49ef9', 'a07b9338e92ed105e6ad720fccce9fe4', + '98999a9b9d9e9fa0a2a3a4a5a7a8a9aa', + 'ecb-tbl-128: I=38'), + ('4cd1e2fd3f4434b553aae453f0ed1a02', 'ae0fb9722418cc21a7da816bbc61322c', + 'acadaeafb1b2b3b4b6b7b8b9bbbcbdbe', + 'ecb-tbl-128: I=39'), + ('5a2c9a9641d4299125fa1b9363104b5e', 'c826a193080ff91ffb21f71d3373c877', + 'c0c1c2c3c5c6c7c8cacbcccdcfd0d1d2', + 'ecb-tbl-128: I=40'), + ('b517fe34c0fa217d341740bfd4fe8dd4', '1181b11b0e494e8d8b0aa6b1d5ac2c48', + 'd4d5d6d7d9dadbdcdedfe0e1e3e4e5e6', + 'ecb-tbl-128: I=41'), + ('014baf2278a69d331d5180103643e99a', '6743c3d1519ab4f2cd9a78ab09a511bd', + 'e8e9eaebedeeeff0f2f3f4f5f7f8f9fa', + 'ecb-tbl-128: I=42'), + ('b529bd8164f20d0aa443d4932116841c', 'dc55c076d52bacdf2eefd952946a439d', + 'fcfdfeff01020304060708090b0c0d0e', + 'ecb-tbl-128: I=43'), + ('2e596dcbb2f33d4216a1176d5bd1e456', '711b17b590ffc72b5c8e342b601e8003', + '10111213151617181a1b1c1d1f202122', + 'ecb-tbl-128: I=44'), + ('7274a1ea2b7ee2424e9a0e4673689143', '19983bb0950783a537e1339f4aa21c75', + '24252627292a2b2c2e2f303133343536', + 'ecb-tbl-128: I=45'), + ('ae20020bd4f13e9d90140bee3b5d26af', '3ba7762e15554169c0f4fa39164c410c', + '38393a3b3d3e3f40424344454748494a', + 'ecb-tbl-128: I=46'), + ('baac065da7ac26e855e79c8849d75a02', 'a0564c41245afca7af8aa2e0e588ea89', + '4c4d4e4f51525354565758595b5c5d5e', + 'ecb-tbl-128: I=47'), + ('7c917d8d1d45fab9e2540e28832540cc', '5e36a42a2e099f54ae85ecd92e2381ed', + '60616263656667686a6b6c6d6f707172', + 'ecb-tbl-128: I=48'), + ('bde6f89e16daadb0e847a2a614566a91', '770036f878cd0f6ca2268172f106f2fe', + '74757677797a7b7c7e7f808183848586', + 'ecb-tbl-128: I=49'), + ('c9de163725f1f5be44ebb1db51d07fbc', '7e4e03908b716116443ccf7c94e7c259', + '88898a8b8d8e8f90929394959798999a', + 'ecb-tbl-128: I=50'), + ('3af57a58f0c07dffa669572b521e2b92', '482735a48c30613a242dd494c7f9185d', + '9c9d9e9fa1a2a3a4a6a7a8a9abacadae', + 'ecb-tbl-128: I=51'), + ('3d5ebac306dde4604f1b4fbbbfcdae55', 'b4c0f6c9d4d7079addf9369fc081061d', + 'b0b1b2b3b5b6b7b8babbbcbdbfc0c1c2', + 'ecb-tbl-128: I=52'), + ('c2dfa91bceb76a1183c995020ac0b556', 'd5810fe0509ac53edcd74f89962e6270', + 'c4c5c6c7c9cacbcccecfd0d1d3d4d5d6', + 'ecb-tbl-128: I=53'), + ('c70f54305885e9a0746d01ec56c8596b', '03f17a16b3f91848269ecdd38ebb2165', + 'd8d9dadbdddedfe0e2e3e4e5e7e8e9ea', + 'ecb-tbl-128: I=54'), + ('c4f81b610e98012ce000182050c0c2b2', 'da1248c3180348bad4a93b4d9856c9df', + 'ecedeeeff1f2f3f4f6f7f8f9fbfcfdfe', + 'ecb-tbl-128: I=55'), + ('eaab86b1d02a95d7404eff67489f97d4', '3d10d7b63f3452c06cdf6cce18be0c2c', + '00010203050607080a0b0c0d0f101112', + 'ecb-tbl-128: I=56'), + ('7c55bdb40b88870b52bec3738de82886', '4ab823e7477dfddc0e6789018fcb6258', + '14151617191a1b1c1e1f202123242526', + 'ecb-tbl-128: I=57'), + ('ba6eaa88371ff0a3bd875e3f2a975ce0', 'e6478ba56a77e70cfdaa5c843abde30e', + '28292a2b2d2e2f30323334353738393a', + 'ecb-tbl-128: I=58'), + ('08059130c4c24bd30cf0575e4e0373dc', '1673064895fbeaf7f09c5429ff75772d', + '3c3d3e3f41424344464748494b4c4d4e', + 'ecb-tbl-128: I=59'), + ('9a8eab004ef53093dfcf96f57e7eda82', '4488033ae9f2efd0ca9383bfca1a94e9', + '50515253555657585a5b5c5d5f606162', + 'ecb-tbl-128: I=60'), + ('0745b589e2400c25f117b1d796c28129', '978f3b8c8f9d6f46626cac3c0bcb9217', + '64656667696a6b6c6e6f707173747576', + 'ecb-tbl-128: I=61'), + ('2f1777781216cec3f044f134b1b92bbe', 'e08c8a7e582e15e5527f1d9e2eecb236', + '78797a7b7d7e7f80828384858788898a', + 'ecb-tbl-128: I=62'), + ('353a779ffc541b3a3805d90ce17580fc', 'cec155b76ac5ffda4cf4f9ca91e49a7a', + '8c8d8e8f91929394969798999b9c9d9e', + 'ecb-tbl-128: I=63'), + ('1a1eae4415cefcf08c4ac1c8f68bea8f', 'd5ac7165763225dd2a38cdc6862c29ad', + 'a0a1a2a3a5a6a7a8aaabacadafb0b1b2', + 'ecb-tbl-128: I=64'), + ('e6e7e4e5b0b3b2b5d4d5aaab16111013', '03680fe19f7ce7275452020be70e8204', + 'b4b5b6b7b9babbbcbebfc0c1c3c4c5c6', + 'ecb-tbl-128: I=65'), + ('f8f9fafbfbf8f9e677767170efe0e1e2', '461df740c9781c388e94bb861ceb54f6', + 'c8c9cacbcdcecfd0d2d3d4d5d7d8d9da', + 'ecb-tbl-128: I=66'), + ('63626160a1a2a3a445444b4a75727370', '451bd60367f96483042742219786a074', + 'dcdddedfe1e2e3e4e6e7e8e9ebecedee', + 'ecb-tbl-128: I=67'), + ('717073720605040b2d2c2b2a05fafbf9', 'e4dfa42671a02e57ef173b85c0ea9f2b', + 'f0f1f2f3f5f6f7f8fafbfcfdfe010002', + 'ecb-tbl-128: I=68'), + ('78797a7beae9e8ef3736292891969794', 'ed11b89e76274282227d854700a78b9e', + '04050607090a0b0c0e0f101113141516', + 'ecb-tbl-128: I=69'), + ('838281803231300fdddcdbdaa0afaead', '433946eaa51ea47af33895f2b90b3b75', + '18191a1b1d1e1f20222324252728292a', + 'ecb-tbl-128: I=70'), + ('18191a1bbfbcbdba75747b7a7f78797a', '6bc6d616a5d7d0284a5910ab35022528', + '2c2d2e2f31323334363738393b3c3d3e', + 'ecb-tbl-128: I=71'), + ('848586879b989996a3a2a5a4849b9a99', 'd2a920ecfe919d354b5f49eae9719c98', + '40414243454647484a4b4c4d4f505152', + 'ecb-tbl-128: I=72'), + ('0001020322212027cacbf4f551565754', '3a061b17f6a92885efbd0676985b373d', + '54555657595a5b5c5e5f606163646566', + 'ecb-tbl-128: I=73'), + ('cecfcccdafacadb2515057564a454447', 'fadeec16e33ea2f4688499d157e20d8f', + '68696a6b6d6e6f70727374757778797a', + 'ecb-tbl-128: I=74'), + ('92939091cdcecfc813121d1c80878685', '5cdefede59601aa3c3cda36fa6b1fa13', + '7c7d7e7f81828384868788898b8c8d8e', + 'ecb-tbl-128: I=75'), + ('d2d3d0d16f6c6d6259585f5ed1eeefec', '9574b00039844d92ebba7ee8719265f8', + '90919293959697989a9b9c9d9fa0a1a2', + 'ecb-tbl-128: I=76'), + ('acadaeaf878485820f0e1110d5d2d3d0', '9a9cf33758671787e5006928188643fa', + 'a4a5a6a7a9aaabacaeafb0b1b3b4b5b6', + 'ecb-tbl-128: I=77'), + ('9091929364676619e6e7e0e1757a7b78', '2cddd634c846ba66bb46cbfea4a674f9', + 'b8b9babbbdbebfc0c2c3c4c5c7c8c9ca', + 'ecb-tbl-128: I=78'), + ('babbb8b98a89888f74757a7b92959497', 'd28bae029393c3e7e26e9fafbbb4b98f', + 'cccdcecfd1d2d3d4d6d7d8d9dbdcddde', + 'ecb-tbl-128: I=79'), + ('8d8c8f8e6e6d6c633b3a3d3ccad5d4d7', 'ec27529b1bee0a9ab6a0d73ebc82e9b7', + 'e0e1e2e3e5e6e7e8eaebecedeff0f1f2', + 'ecb-tbl-128: I=80'), + ('86878485010203040808f7f767606162', '3cb25c09472aff6ee7e2b47ccd7ccb17', + 'f4f5f6f7f9fafbfcfefe010103040506', + 'ecb-tbl-128: I=81'), + ('8e8f8c8d656667788a8b8c8d010e0f0c', 'dee33103a7283370d725e44ca38f8fe5', + '08090a0b0d0e0f10121314151718191a', + 'ecb-tbl-128: I=82'), + ('c8c9cacb858687807a7b7475e7e0e1e2', '27f9bcd1aac64bffc11e7815702c1a69', + '1c1d1e1f21222324262728292b2c2d2e', + 'ecb-tbl-128: I=83'), + ('6d6c6f6e5053525d8c8d8a8badd2d3d0', '5df534ffad4ed0749a9988e9849d0021', + '30313233353637383a3b3c3d3f404142', + 'ecb-tbl-128: I=84'), + ('28292a2b393a3b3c0607181903040506', 'a48bee75db04fb60ca2b80f752a8421b', + '44454647494a4b4c4e4f505153545556', + 'ecb-tbl-128: I=85'), + ('a5a4a7a6b0b3b28ddbdadddcbdb2b3b0', '024c8cf70bc86ee5ce03678cb7af45f9', + '58595a5b5d5e5f60626364656768696a', + 'ecb-tbl-128: I=86'), + ('323330316467666130313e3f2c2b2a29', '3c19ac0f8a3a3862ce577831301e166b', + '6c6d6e6f71727374767778797b7c7d7e', + 'ecb-tbl-128: I=87'), + ('27262524080b0a05171611100b141516', 'c5e355b796a57421d59ca6be82e73bca', + '80818283858687888a8b8c8d8f909192', + 'ecb-tbl-128: I=88'), + ('040506074142434435340b0aa3a4a5a6', 'd94033276417abfb05a69d15b6e386e2', + '94959697999a9b9c9e9fa0a1a3a4a5a6', + 'ecb-tbl-128: I=89'), + ('242526271112130c61606766bdb2b3b0', '24b36559ea3a9b9b958fe6da3e5b8d85', + 'a8a9aaabadaeafb0b2b3b4b5b7b8b9ba', + 'ecb-tbl-128: I=90'), + ('4b4a4948252627209e9f9091cec9c8cb', '20fd4feaa0e8bf0cce7861d74ef4cb72', + 'bcbdbebfc1c2c3c4c6c7c8c9cbcccdce', + 'ecb-tbl-128: I=91'), + ('68696a6b6665646b9f9e9998d9e6e7e4', '350e20d5174277b9ec314c501570a11d', + 'd0d1d2d3d5d6d7d8dadbdcdddfe0e1e2', + 'ecb-tbl-128: I=92'), + ('34353637c5c6c7c0f0f1eeef7c7b7a79', '87a29d61b7c604d238fe73045a7efd57', + 'e4e5e6e7e9eaebeceeeff0f1f3f4f5f6', + 'ecb-tbl-128: I=93'), + ('32333031c2c1c13f0d0c0b0a050a0b08', '2c3164c1cc7d0064816bdc0faa362c52', + 'f8f9fafbfdfefe00020304050708090a', + 'ecb-tbl-128: I=94'), + ('cdcccfcebebdbcbbabaaa5a4181f1e1d', '195fe5e8a05a2ed594f6e4400eee10b3', + '0c0d0e0f11121314161718191b1c1d1e', + 'ecb-tbl-128: I=95'), + ('212023223635343ba0a1a6a7445b5a59', 'e4663df19b9a21a5a284c2bd7f905025', + '20212223252627282a2b2c2d2f303132', + 'ecb-tbl-128: I=96'), + ('0e0f0c0da8abaaad2f2e515002050407', '21b88714cfb4e2a933bd281a2c4743fd', + '34353637393a3b3c3e3f404143444546', + 'ecb-tbl-128: I=97'), + ('070605042a2928378e8f8889bdb2b3b0', 'cbfc3980d704fd0fc54378ab84e17870', + '48494a4b4d4e4f50525354555758595a', + 'ecb-tbl-128: I=98'), + ('cbcac9c893909196a9a8a7a6a5a2a3a0', 'bc5144baa48bdeb8b63e22e03da418ef', + '5c5d5e5f61626364666768696b6c6d6e', + 'ecb-tbl-128: I=99'), + ('80818283c1c2c3cc9c9d9a9b0cf3f2f1', '5a1dbaef1ee2984b8395da3bdffa3ccc', + '70717273757677787a7b7c7d7f808182', + 'ecb-tbl-128: I=100'), + ('1213101125262720fafbe4e5b1b6b7b4', 'f0b11cd0729dfcc80cec903d97159574', + '84858687898a8b8c8e8f909193949596', + 'ecb-tbl-128: I=101'), + ('7f7e7d7c3033320d97969190222d2c2f', '9f95314acfddc6d1914b7f19a9cc8209', + '98999a9b9d9e9fa0a2a3a4a5a7a8a9aa', + 'ecb-tbl-128: I=102'), + ('4e4f4c4d484b4a4d81808f8e53545556', '595736f6f0f70914a94e9e007f022519', + 'acadaeafb1b2b3b4b6b7b8b9bbbcbdbe', + 'ecb-tbl-128: I=103'), + ('dcdddedfb0b3b2bd15141312a1bebfbc', '1f19f57892cae586fcdfb4c694deb183', + 'c0c1c2c3c5c6c7c8cacbcccdcfd0d1d2', + 'ecb-tbl-128: I=104'), + ('93929190282b2a2dc4c5fafb92959497', '540700ee1f6f3dab0b3eddf6caee1ef5', + 'd4d5d6d7d9dadbdcdedfe0e1e3e4e5e6', + 'ecb-tbl-128: I=105'), + ('f5f4f7f6c4c7c6d9373631307e717073', '14a342a91019a331687a2254e6626ca2', + 'e8e9eaebedeeeff0f2f3f4f5f7f8f9fa', + 'ecb-tbl-128: I=106'), + ('93929190b6b5b4b364656a6b05020300', '7b25f3c3b2eea18d743ef283140f29ff', + 'fcfdfeff01020304060708090b0c0d0e', + 'ecb-tbl-128: I=107'), + ('babbb8b90d0e0f00a4a5a2a3043b3a39', '46c2587d66e5e6fa7f7ca6411ad28047', + '10111213151617181a1b1c1d1f202122', + 'ecb-tbl-128: I=108'), + ('d8d9dadb7f7c7d7a10110e0f787f7e7d', '09470e72229d954ed5ee73886dfeeba9', + '24252627292a2b2c2e2f303133343536', + 'ecb-tbl-128: I=109'), + ('fefffcfdefeced923b3a3d3c6768696a', 'd77c03de92d4d0d79ef8d4824ef365eb', + '38393a3b3d3e3f40424344454748494a', + 'ecb-tbl-128: I=110'), + ('d6d7d4d58a89888f96979899a5a2a3a0', '1d190219f290e0f1715d152d41a23593', + '4c4d4e4f51525354565758595b5c5d5e', + 'ecb-tbl-128: I=111'), + ('18191a1ba8abaaa5303136379b848586', 'a2cd332ce3a0818769616292e87f757b', + '60616263656667686a6b6c6d6f707172', + 'ecb-tbl-128: I=112'), + ('6b6a6968a4a7a6a1d6d72829b0b7b6b5', 'd54afa6ce60fbf9341a3690e21385102', + '74757677797a7b7c7e7f808183848586', + 'ecb-tbl-128: I=113'), + ('000102038a89889755545352a6a9a8ab', '06e5c364ded628a3f5e05e613e356f46', + '88898a8b8d8e8f90929394959798999a', + 'ecb-tbl-128: I=114'), + ('2d2c2f2eb3b0b1b6b6b7b8b9f2f5f4f7', 'eae63c0e62556dac85d221099896355a', + '9c9d9e9fa1a2a3a4a6a7a8a9abacadae', + 'ecb-tbl-128: I=115'), + ('979695943536373856575051e09f9e9d', '1fed060e2c6fc93ee764403a889985a2', + 'b0b1b2b3b5b6b7b8babbbcbdbfc0c1c2', + 'ecb-tbl-128: I=116'), + ('a4a5a6a7989b9a9db1b0afae7a7d7c7f', 'c25235c1a30fdec1c7cb5c5737b2a588', + 'c4c5c6c7c9cacbcccecfd0d1d3d4d5d6', + 'ecb-tbl-128: I=117'), + ('c1c0c3c2686b6a55a8a9aeafeae5e4e7', '796dbef95147d4d30873ad8b7b92efc0', + 'd8d9dadbdddedfe0e2e3e4e5e7e8e9ea', + 'ecb-tbl-128: I=118'), + ('c1c0c3c2141716118c8d828364636261', 'cbcf0fb34d98d0bd5c22ce37211a46bf', + 'ecedeeeff1f2f3f4f6f7f8f9fbfcfdfe', + 'ecb-tbl-128: I=119'), + ('93929190cccfcec196979091e0fffefd', '94b44da6466126cafa7c7fd09063fc24', + '00010203050607080a0b0c0d0f101112', + 'ecb-tbl-128: I=120'), + ('b4b5b6b7f9fafbfc25241b1a6e69686b', 'd78c5b5ebf9b4dbda6ae506c5074c8fe', + '14151617191a1b1c1e1f202123242526', + 'ecb-tbl-128: I=121'), + ('868784850704051ac7c6c1c08788898a', '6c27444c27204b043812cf8cf95f9769', + '28292a2b2d2e2f30323334353738393a', + 'ecb-tbl-128: I=122'), + ('f4f5f6f7aaa9a8affdfcf3f277707172', 'be94524ee5a2aa50bba8b75f4c0aebcf', + '3c3d3e3f41424344464748494b4c4d4e', + 'ecb-tbl-128: I=123'), + ('d3d2d1d00605040bc3c2c5c43e010003', 'a0aeaae91ba9f31f51aeb3588cf3a39e', + '50515253555657585a5b5c5d5f606162', + 'ecb-tbl-128: I=124'), + ('73727170424140476a6b74750d0a0b08', '275297779c28266ef9fe4c6a13c08488', + '64656667696a6b6c6e6f707173747576', + 'ecb-tbl-128: I=125'), + ('c2c3c0c10a0908f754555253a1aeafac', '86523d92bb8672cb01cf4a77fd725882', + '78797a7b7d7e7f80828384858788898a', + 'ecb-tbl-128: I=126'), + ('6d6c6f6ef8fbfafd82838c8df8fffefd', '4b8327640e9f33322a04dd96fcbf9a36', + '8c8d8e8f91929394969798999b9c9d9e', + 'ecb-tbl-128: I=127'), + ('f5f4f7f684878689a6a7a0a1d2cdcccf', 'ce52af650d088ca559425223f4d32694', + 'a0a1a2a3a5a6a7a8aaabacadafb0b1b2', + 'ecb-tbl-128: I=128'), + + # ecb_tbl.txt, KEYSIZE=192 + ('2d33eef2c0430a8a9ebf45e809c40bb6', 'dff4945e0336df4c1c56bc700eff837f', + '00010203050607080a0b0c0d0f10111214151617191a1b1c', + 'ecb-tbl-192: I=1'), + ('6aa375d1fa155a61fb72353e0a5a8756', 'b6fddef4752765e347d5d2dc196d1252', + '1e1f20212324252628292a2b2d2e2f30323334353738393a', + 'ecb-tbl-192: I=2'), + ('bc3736518b9490dcb8ed60eb26758ed4', 'd23684e3d963b3afcf1a114aca90cbd6', + '3c3d3e3f41424344464748494b4c4d4e5051525355565758', + 'ecb-tbl-192: I=3'), + ('aa214402b46cffb9f761ec11263a311e', '3a7ac027753e2a18c2ceab9e17c11fd0', + '5a5b5c5d5f60616264656667696a6b6c6e6f707173747576', + 'ecb-tbl-192: I=4'), + ('02aea86e572eeab66b2c3af5e9a46fd6', '8f6786bd007528ba26603c1601cdd0d8', + '78797a7b7d7e7f80828384858788898a8c8d8e8f91929394', + 'ecb-tbl-192: I=5'), + ('e2aef6acc33b965c4fa1f91c75ff6f36', 'd17d073b01e71502e28b47ab551168b3', + '969798999b9c9d9ea0a1a2a3a5a6a7a8aaabacadafb0b1b2', + 'ecb-tbl-192: I=6'), + ('0659df46427162b9434865dd9499f91d', 'a469da517119fab95876f41d06d40ffa', + 'b4b5b6b7b9babbbcbebfc0c1c3c4c5c6c8c9cacbcdcecfd0', + 'ecb-tbl-192: I=7'), + ('49a44239c748feb456f59c276a5658df', '6091aa3b695c11f5c0b6ad26d3d862ff', + 'd2d3d4d5d7d8d9dadcdddedfe1e2e3e4e6e7e8e9ebecedee', + 'ecb-tbl-192: I=8'), + ('66208f6e9d04525bdedb2733b6a6be37', '70f9e67f9f8df1294131662dc6e69364', + 'f0f1f2f3f5f6f7f8fafbfcfdfe01000204050607090a0b0c', + 'ecb-tbl-192: I=9'), + ('3393f8dfc729c97f5480b950bc9666b0', 'd154dcafad8b207fa5cbc95e9996b559', + '0e0f10111314151618191a1b1d1e1f20222324252728292a', + 'ecb-tbl-192: I=10'), + ('606834c8ce063f3234cf1145325dbd71', '4934d541e8b46fa339c805a7aeb9e5da', + '2c2d2e2f31323334363738393b3c3d3e4041424345464748', + 'ecb-tbl-192: I=11'), + ('fec1c04f529bbd17d8cecfcc4718b17f', '62564c738f3efe186e1a127a0c4d3c61', + '4a4b4c4d4f50515254555657595a5b5c5e5f606163646566', + 'ecb-tbl-192: I=12'), + ('32df99b431ed5dc5acf8caf6dc6ce475', '07805aa043986eb23693e23bef8f3438', + '68696a6b6d6e6f70727374757778797a7c7d7e7f81828384', + 'ecb-tbl-192: I=13'), + ('7fdc2b746f3f665296943b83710d1f82', 'df0b4931038bade848dee3b4b85aa44b', + '868788898b8c8d8e90919293959697989a9b9c9d9fa0a1a2', + 'ecb-tbl-192: I=14'), + ('8fba1510a3c5b87e2eaa3f7a91455ca2', '592d5fded76582e4143c65099309477c', + 'a4a5a6a7a9aaabacaeafb0b1b3b4b5b6b8b9babbbdbebfc0', + 'ecb-tbl-192: I=15'), + ('2c9b468b1c2eed92578d41b0716b223b', 'c9b8d6545580d3dfbcdd09b954ed4e92', + 'c2c3c4c5c7c8c9cacccdcecfd1d2d3d4d6d7d8d9dbdcddde', + 'ecb-tbl-192: I=16'), + ('0a2bbf0efc6bc0034f8a03433fca1b1a', '5dccd5d6eb7c1b42acb008201df707a0', + 'e0e1e2e3e5e6e7e8eaebecedeff0f1f2f4f5f6f7f9fafbfc', + 'ecb-tbl-192: I=17'), + ('25260e1f31f4104d387222e70632504b', 'a2a91682ffeb6ed1d34340946829e6f9', + 'fefe01010304050608090a0b0d0e0f10121314151718191a', + 'ecb-tbl-192: I=18'), + ('c527d25a49f08a5228d338642ae65137', 'e45d185b797000348d9267960a68435d', + '1c1d1e1f21222324262728292b2c2d2e3031323335363738', + 'ecb-tbl-192: I=19'), + ('3b49fc081432f5890d0e3d87e884a69e', '45e060dae5901cda8089e10d4f4c246b', + '3a3b3c3d3f40414244454647494a4b4c4e4f505153545556', + 'ecb-tbl-192: I=20'), + ('d173f9ed1e57597e166931df2754a083', 'f6951afacc0079a369c71fdcff45df50', + '58595a5b5d5e5f60626364656768696a6c6d6e6f71727374', + 'ecb-tbl-192: I=21'), + ('8c2b7cafa5afe7f13562daeae1adede0', '9e95e00f351d5b3ac3d0e22e626ddad6', + '767778797b7c7d7e80818283858687888a8b8c8d8f909192', + 'ecb-tbl-192: I=22'), + ('aaf4ec8c1a815aeb826cab741339532c', '9cb566ff26d92dad083b51fdc18c173c', + '94959697999a9b9c9e9fa0a1a3a4a5a6a8a9aaabadaeafb0', + 'ecb-tbl-192: I=23'), + ('40be8c5d9108e663f38f1a2395279ecf', 'c9c82766176a9b228eb9a974a010b4fb', + 'd0d1d2d3d5d6d7d8dadbdcdddfe0e1e2e4e5e6e7e9eaebec', + 'ecb-tbl-192: I=24'), + ('0c8ad9bc32d43e04716753aa4cfbe351', 'd8e26aa02945881d5137f1c1e1386e88', + '2a2b2c2d2f30313234353637393a3b3c3e3f404143444546', + 'ecb-tbl-192: I=25'), + ('1407b1d5f87d63357c8dc7ebbaebbfee', 'c0e024ccd68ff5ffa4d139c355a77c55', + '48494a4b4d4e4f50525354555758595a5c5d5e5f61626364', + 'ecb-tbl-192: I=26'), + ('e62734d1ae3378c4549e939e6f123416', '0b18b3d16f491619da338640df391d43', + '84858687898a8b8c8e8f90919394959698999a9b9d9e9fa0', + 'ecb-tbl-192: I=27'), + ('5a752cff2a176db1a1de77f2d2cdee41', 'dbe09ac8f66027bf20cb6e434f252efc', + 'a2a3a4a5a7a8a9aaacadaeafb1b2b3b4b6b7b8b9bbbcbdbe', + 'ecb-tbl-192: I=28'), + ('a9c8c3a4eabedc80c64730ddd018cd88', '6d04e5e43c5b9cbe05feb9606b6480fe', + 'c0c1c2c3c5c6c7c8cacbcccdcfd0d1d2d4d5d6d7d9dadbdc', + 'ecb-tbl-192: I=29'), + ('ee9b3dbbdb86180072130834d305999a', 'dd1d6553b96be526d9fee0fbd7176866', + '1a1b1c1d1f20212224252627292a2b2c2e2f303133343536', + 'ecb-tbl-192: I=30'), + ('a7fa8c3586b8ebde7568ead6f634a879', '0260ca7e3f979fd015b0dd4690e16d2a', + '38393a3b3d3e3f40424344454748494a4c4d4e4f51525354', + 'ecb-tbl-192: I=31'), + ('37e0f4a87f127d45ac936fe7ad88c10a', '9893734de10edcc8a67c3b110b8b8cc6', + '929394959798999a9c9d9e9fa1a2a3a4a6a7a8a9abacadae', + 'ecb-tbl-192: I=32'), + ('3f77d8b5d92bac148e4e46f697a535c5', '93b30b750516b2d18808d710c2ee84ef', + '464748494b4c4d4e50515253555657585a5b5c5d5f606162', + 'ecb-tbl-192: I=33'), + ('d25ebb686c40f7e2c4da1014936571ca', '16f65fa47be3cb5e6dfe7c6c37016c0e', + '828384858788898a8c8d8e8f91929394969798999b9c9d9e', + 'ecb-tbl-192: I=34'), + ('4f1c769d1e5b0552c7eca84dea26a549', 'f3847210d5391e2360608e5acb560581', + 'a0a1a2a3a5a6a7a8aaabacadafb0b1b2b4b5b6b7b9babbbc', + 'ecb-tbl-192: I=35'), + ('8548e2f882d7584d0fafc54372b6633a', '8754462cd223366d0753913e6af2643d', + 'bebfc0c1c3c4c5c6c8c9cacbcdcecfd0d2d3d4d5d7d8d9da', + 'ecb-tbl-192: I=36'), + ('87d7a336cb476f177cd2a51af2a62cdf', '1ea20617468d1b806a1fd58145462017', + 'dcdddedfe1e2e3e4e6e7e8e9ebecedeef0f1f2f3f5f6f7f8', + 'ecb-tbl-192: I=37'), + ('03b1feac668c4e485c1065dfc22b44ee', '3b155d927355d737c6be9dda60136e2e', + 'fafbfcfdfe01000204050607090a0b0c0e0f101113141516', + 'ecb-tbl-192: I=38'), + ('bda15e66819fa72d653a6866aa287962', '26144f7b66daa91b6333dbd3850502b3', + '18191a1b1d1e1f20222324252728292a2c2d2e2f31323334', + 'ecb-tbl-192: I=39'), + ('4d0c7a0d2505b80bf8b62ceb12467f0a', 'e4f9a4ab52ced8134c649bf319ebcc90', + '363738393b3c3d3e40414243454647484a4b4c4d4f505152', + 'ecb-tbl-192: I=40'), + ('626d34c9429b37211330986466b94e5f', 'b9ddd29ac6128a6cab121e34a4c62b36', + '54555657595a5b5c5e5f60616364656668696a6b6d6e6f70', + 'ecb-tbl-192: I=41'), + ('333c3e6bf00656b088a17e5ff0e7f60a', '6fcddad898f2ce4eff51294f5eaaf5c9', + '727374757778797a7c7d7e7f81828384868788898b8c8d8e', + 'ecb-tbl-192: I=42'), + ('687ed0cdc0d2a2bc8c466d05ef9d2891', 'c9a6fe2bf4028080bea6f7fc417bd7e3', + '90919293959697989a9b9c9d9fa0a1a2a4a5a6a7a9aaabac', + 'ecb-tbl-192: I=43'), + ('487830e78cc56c1693e64b2a6660c7b6', '6a2026846d8609d60f298a9c0673127f', + 'aeafb0b1b3b4b5b6b8b9babbbdbebfc0c2c3c4c5c7c8c9ca', + 'ecb-tbl-192: I=44'), + ('7a48d6b7b52b29392aa2072a32b66160', '2cb25c005e26efea44336c4c97a4240b', + 'cccdcecfd1d2d3d4d6d7d8d9dbdcdddee0e1e2e3e5e6e7e8', + 'ecb-tbl-192: I=45'), + ('907320e64c8c5314d10f8d7a11c8618d', '496967ab8680ddd73d09a0e4c7dcc8aa', + 'eaebecedeff0f1f2f4f5f6f7f9fafbfcfefe010103040506', + 'ecb-tbl-192: I=46'), + ('b561f2ca2d6e65a4a98341f3ed9ff533', 'd5af94de93487d1f3a8c577cb84a66a4', + '08090a0b0d0e0f10121314151718191a1c1d1e1f21222324', + 'ecb-tbl-192: I=47'), + ('df769380d212792d026f049e2e3e48ef', '84bdac569cae2828705f267cc8376e90', + '262728292b2c2d2e30313233353637383a3b3c3d3f404142', + 'ecb-tbl-192: I=48'), + ('79f374bc445bdabf8fccb8843d6054c6', 'f7401dda5ad5ab712b7eb5d10c6f99b6', + '44454647494a4b4c4e4f50515354555658595a5b5d5e5f60', + 'ecb-tbl-192: I=49'), + ('4e02f1242fa56b05c68dbae8fe44c9d6', '1c9d54318539ebd4c3b5b7e37bf119f0', + '626364656768696a6c6d6e6f71727374767778797b7c7d7e', + 'ecb-tbl-192: I=50'), + ('cf73c93cbff57ac635a6f4ad2a4a1545', 'aca572d65fb2764cffd4a6eca090ea0d', + '80818283858687888a8b8c8d8f90919294959697999a9b9c', + 'ecb-tbl-192: I=51'), + ('9923548e2875750725b886566784c625', '36d9c627b8c2a886a10ccb36eae3dfbb', + '9e9fa0a1a3a4a5a6a8a9aaabadaeafb0b2b3b4b5b7b8b9ba', + 'ecb-tbl-192: I=52'), + ('4888336b723a022c9545320f836a4207', '010edbf5981e143a81d646e597a4a568', + 'bcbdbebfc1c2c3c4c6c7c8c9cbcccdced0d1d2d3d5d6d7d8', + 'ecb-tbl-192: I=53'), + ('f84d9a5561b0608b1160dee000c41ba8', '8db44d538dc20cc2f40f3067fd298e60', + 'dadbdcdddfe0e1e2e4e5e6e7e9eaebeceeeff0f1f3f4f5f6', + 'ecb-tbl-192: I=54'), + ('c23192a0418e30a19b45ae3e3625bf22', '930eb53bc71e6ac4b82972bdcd5aafb3', + 'f8f9fafbfdfefe00020304050708090a0c0d0e0f11121314', + 'ecb-tbl-192: I=55'), + ('b84e0690b28b0025381ad82a15e501a7', '6c42a81edcbc9517ccd89c30c95597b4', + '161718191b1c1d1e20212223252627282a2b2c2d2f303132', + 'ecb-tbl-192: I=56'), + ('acef5e5c108876c4f06269f865b8f0b0', 'da389847ad06df19d76ee119c71e1dd3', + '34353637393a3b3c3e3f40414344454648494a4b4d4e4f50', + 'ecb-tbl-192: I=57'), + ('0f1b3603e0f5ddea4548246153a5e064', 'e018fdae13d3118f9a5d1a647a3f0462', + '525354555758595a5c5d5e5f61626364666768696b6c6d6e', + 'ecb-tbl-192: I=58'), + ('fbb63893450d42b58c6d88cd3c1809e3', '2aa65db36264239d3846180fabdfad20', + '70717273757677787a7b7c7d7f80818284858687898a8b8c', + 'ecb-tbl-192: I=59'), + ('4bef736df150259dae0c91354e8a5f92', '1472163e9a4f780f1ceb44b07ecf4fdb', + '8e8f90919394959698999a9b9d9e9fa0a2a3a4a5a7a8a9aa', + 'ecb-tbl-192: I=60'), + ('7d2d46242056ef13d3c3fc93c128f4c7', 'c8273fdc8f3a9f72e91097614b62397c', + 'acadaeafb1b2b3b4b6b7b8b9bbbcbdbec0c1c2c3c5c6c7c8', + 'ecb-tbl-192: I=61'), + ('e9c1ba2df415657a256edb33934680fd', '66c8427dcd733aaf7b3470cb7d976e3f', + 'cacbcccdcfd0d1d2d4d5d6d7d9dadbdcdedfe0e1e3e4e5e6', + 'ecb-tbl-192: I=62'), + ('e23ee277b0aa0a1dfb81f7527c3514f1', '146131cb17f1424d4f8da91e6f80c1d0', + 'e8e9eaebedeeeff0f2f3f4f5f7f8f9fafcfdfeff01020304', + 'ecb-tbl-192: I=63'), + ('3e7445b0b63caaf75e4a911e12106b4c', '2610d0ad83659081ae085266a88770dc', + '060708090b0c0d0e10111213151617181a1b1c1d1f202122', + 'ecb-tbl-192: I=64'), + ('767774752023222544455a5be6e1e0e3', '38a2b5a974b0575c5d733917fb0d4570', + '24252627292a2b2c2e2f30313334353638393a3b3d3e3f40', + 'ecb-tbl-192: I=65'), + ('72737475717e7f7ce9e8ebea696a6b6c', 'e21d401ebc60de20d6c486e4f39a588b', + '424344454748494a4c4d4e4f51525354565758595b5c5d5e', + 'ecb-tbl-192: I=66'), + ('dfdedddc25262728c9c8cfcef1eeefec', 'e51d5f88c670b079c0ca1f0c2c4405a2', + '60616263656667686a6b6c6d6f70717274757677797a7b7c', + 'ecb-tbl-192: I=67'), + ('fffe0100707776755f5e5d5c7675746b', '246a94788a642fb3d1b823c8762380c8', + '7e7f80818384858688898a8b8d8e8f90929394959798999a', + 'ecb-tbl-192: I=68'), + ('e0e1e2e3424140479f9e9190292e2f2c', 'b80c391c5c41a4c3b30c68e0e3d7550f', + '9c9d9e9fa1a2a3a4a6a7a8a9abacadaeb0b1b2b3b5b6b7b8', + 'ecb-tbl-192: I=69'), + ('2120272690efeeed3b3a39384e4d4c4b', 'b77c4754fc64eb9a1154a9af0bb1f21c', + 'babbbcbdbfc0c1c2c4c5c6c7c9cacbcccecfd0d1d3d4d5d6', + 'ecb-tbl-192: I=70'), + ('ecedeeef5350516ea1a0a7a6a3acadae', 'fb554de520d159a06bf219fc7f34a02f', + 'd8d9dadbdddedfe0e2e3e4e5e7e8e9eaecedeeeff1f2f3f4', + 'ecb-tbl-192: I=71'), + ('32333c3d25222320e9e8ebeacecdccc3', 'a89fba152d76b4927beed160ddb76c57', + 'f6f7f8f9fbfcfdfe00010203050607080a0b0c0d0f101112', + 'ecb-tbl-192: I=72'), + ('40414243626160678a8bb4b511161714', '5676eab4a98d2e8473b3f3d46424247c', + '14151617191a1b1c1e1f20212324252628292a2b2d2e2f30', + 'ecb-tbl-192: I=73'), + ('94959293f5fafbf81f1e1d1c7c7f7e79', '4e8f068bd7ede52a639036ec86c33568', + '323334353738393a3c3d3e3f41424344464748494b4c4d4e', + 'ecb-tbl-192: I=74'), + ('bebfbcbd191a1b14cfcec9c8546b6a69', 'f0193c4d7aff1791ee4c07eb4a1824fc', + '50515253555657585a5b5c5d5f60616264656667696a6b6c', + 'ecb-tbl-192: I=75'), + ('2c2d3233898e8f8cbbbab9b8333031ce', 'ac8686eeca9ba761afe82d67b928c33f', + '6e6f70717374757678797a7b7d7e7f80828384858788898a', + 'ecb-tbl-192: I=76'), + ('84858687bfbcbdba37363938fdfafbf8', '5faf8573e33b145b6a369cd3606ab2c9', + '8c8d8e8f91929394969798999b9c9d9ea0a1a2a3a5a6a7a8', + 'ecb-tbl-192: I=77'), + ('828384857669686b909192930b08090e', '31587e9944ab1c16b844ecad0df2e7da', + 'aaabacadafb0b1b2b4b5b6b7b9babbbcbebfc0c1c3c4c5c6', + 'ecb-tbl-192: I=78'), + ('bebfbcbd9695948b707176779e919093', 'd017fecd91148aba37f6f3068aa67d8a', + 'c8c9cacbcdcecfd0d2d3d4d5d7d8d9dadcdddedfe1e2e3e4', + 'ecb-tbl-192: I=79'), + ('8b8a85846067666521202322d0d3d2dd', '788ef2f021a73cba2794b616078a8500', + 'e6e7e8e9ebecedeef0f1f2f3f5f6f7f8fafbfcfdfe010002', + 'ecb-tbl-192: I=80'), + ('76777475f1f2f3f4f8f9e6e777707172', '5d1ef20dced6bcbc12131ac7c54788aa', + '04050607090a0b0c0e0f10111314151618191a1b1d1e1f20', + 'ecb-tbl-192: I=81'), + ('a4a5a2a34f404142b4b5b6b727242522', 'b3c8cf961faf9ea05fdde6d1e4d8f663', + '222324252728292a2c2d2e2f31323334363738393b3c3d3e', + 'ecb-tbl-192: I=82'), + ('94959697e1e2e3ec16171011839c9d9e', '143075c70605861c7fac6526199e459f', + '40414243454647484a4b4c4d4f50515254555657595a5b5c', + 'ecb-tbl-192: I=83'), + ('03023d3c06010003dedfdcddfffcfde2', 'a5ae12eade9a87268d898bfc8fc0252a', + '5e5f60616364656668696a6b6d6e6f70727374757778797a', + 'ecb-tbl-192: I=84'), + ('10111213f1f2f3f4cecfc0c1dbdcddde', '0924f7cf2e877a4819f5244a360dcea9', + '7c7d7e7f81828384868788898b8c8d8e9091929395969798', + 'ecb-tbl-192: I=85'), + ('67666160724d4c4f1d1c1f1e73707176', '3d9e9635afcc3e291cc7ab3f27d1c99a', + '9a9b9c9d9fa0a1a2a4a5a6a7a9aaabacaeafb0b1b3b4b5b6', + 'ecb-tbl-192: I=86'), + ('e6e7e4e5a8abaad584858283909f9e9d', '9d80feebf87510e2b8fb98bb54fd788c', + 'b8b9babbbdbebfc0c2c3c4c5c7c8c9cacccdcecfd1d2d3d4', + 'ecb-tbl-192: I=87'), + ('71707f7e565150537d7c7f7e6162636c', '5f9d1a082a1a37985f174002eca01309', + 'd6d7d8d9dbdcdddee0e1e2e3e5e6e7e8eaebecedeff0f1f2', + 'ecb-tbl-192: I=88'), + ('64656667212223245555aaaa03040506', 'a390ebb1d1403930184a44b4876646e4', + 'f4f5f6f7f9fafbfcfefe01010304050608090a0b0d0e0f10', + 'ecb-tbl-192: I=89'), + ('9e9f9899aba4a5a6cfcecdcc2b28292e', '700fe918981c3195bb6c4bcb46b74e29', + '121314151718191a1c1d1e1f21222324262728292b2c2d2e', + 'ecb-tbl-192: I=90'), + ('c7c6c5c4d1d2d3dc626364653a454447', '907984406f7bf2d17fb1eb15b673d747', + '30313233353637383a3b3c3d3f40414244454647494a4b4c', + 'ecb-tbl-192: I=91'), + ('f6f7e8e9e0e7e6e51d1c1f1e5b585966', 'c32a956dcfc875c2ac7c7cc8b8cc26e1', + '4e4f50515354555658595a5b5d5e5f60626364656768696a', + 'ecb-tbl-192: I=92'), + ('bcbdbebf5d5e5f5868696667f4f3f2f1', '02646e2ebfa9b820cf8424e9b9b6eb51', + '6c6d6e6f71727374767778797b7c7d7e8081828385868788', + 'ecb-tbl-192: I=93'), + ('40414647b0afaead9b9a99989b98999e', '621fda3a5bbd54c6d3c685816bd4ead8', + '8a8b8c8d8f90919294959697999a9b9c9e9fa0a1a3a4a5a6', + 'ecb-tbl-192: I=94'), + ('69686b6a0201001f0f0e0908b4bbbab9', 'd4e216040426dfaf18b152469bc5ac2f', + 'a8a9aaabadaeafb0b2b3b4b5b7b8b9babcbdbebfc1c2c3c4', + 'ecb-tbl-192: I=95'), + ('c7c6c9c8d8dfdedd5a5b5859bebdbcb3', '9d0635b9d33b6cdbd71f5d246ea17cc8', + 'c6c7c8c9cbcccdced0d1d2d3d5d6d7d8dadbdcdddfe0e1e2', + 'ecb-tbl-192: I=96'), + ('dedfdcdd787b7a7dfffee1e0b2b5b4b7', '10abad1bd9bae5448808765583a2cc1a', + 'e4e5e6e7e9eaebeceeeff0f1f3f4f5f6f8f9fafbfdfefe00', + 'ecb-tbl-192: I=97'), + ('4d4c4b4a606f6e6dd0d1d2d3fbf8f9fe', '6891889e16544e355ff65a793c39c9a8', + '020304050708090a0c0d0e0f11121314161718191b1c1d1e', + 'ecb-tbl-192: I=98'), + ('b7b6b5b4d7d4d5dae5e4e3e2e1fefffc', 'cc735582e68072c163cd9ddf46b91279', + '20212223252627282a2b2c2d2f30313234353637393a3b3c', + 'ecb-tbl-192: I=99'), + ('cecfb0b1f7f0f1f2aeafacad3e3d3c23', 'c5c68b9aeeb7f878df578efa562f9574', + '3e3f40414344454648494a4b4d4e4f50525354555758595a', + 'ecb-tbl-192: I=100'), + ('cacbc8c9cdcecfc812131c1d494e4f4c', '5f4764395a667a47d73452955d0d2ce8', + '5c5d5e5f61626364666768696b6c6d6e7071727375767778', + 'ecb-tbl-192: I=101'), + ('9d9c9b9ad22d2c2fb1b0b3b20c0f0e09', '701448331f66106cefddf1eb8267c357', + '7a7b7c7d7f80818284858687898a8b8c8e8f909193949596', + 'ecb-tbl-192: I=102'), + ('7a7b787964676659959493924f404142', 'cb3ee56d2e14b4e1941666f13379d657', + '98999a9b9d9e9fa0a2a3a4a5a7a8a9aaacadaeafb1b2b3b4', + 'ecb-tbl-192: I=103'), + ('aaaba4a5cec9c8cb1f1e1d1caba8a9a6', '9fe16efd18ab6e1981191851fedb0764', + 'b6b7b8b9bbbcbdbec0c1c2c3c5c6c7c8cacbcccdcfd0d1d2', + 'ecb-tbl-192: I=104'), + ('93929190282b2a2dc4c5fafb92959497', '3dc9ba24e1b223589b147adceb4c8e48', + 'd4d5d6d7d9dadbdcdedfe0e1e3e4e5e6e8e9eaebedeeeff0', + 'ecb-tbl-192: I=105'), + ('efeee9e8ded1d0d339383b3a888b8a8d', '1c333032682e7d4de5e5afc05c3e483c', + 'f2f3f4f5f7f8f9fafcfdfeff01020304060708090b0c0d0e', + 'ecb-tbl-192: I=106'), + ('7f7e7d7ca2a1a0af78797e7f112e2f2c', 'd593cc99a95afef7e92038e05a59d00a', + '10111213151617181a1b1c1d1f20212224252627292a2b2c', + 'ecb-tbl-192: I=107'), + ('84859a9b2b2c2d2e868784852625245b', '51e7f96f53b4353923452c222134e1ec', + '2e2f30313334353638393a3b3d3e3f40424344454748494a', + 'ecb-tbl-192: I=108'), + ('b0b1b2b3070405026869666710171615', '4075b357a1a2b473400c3b25f32f81a4', + '4c4d4e4f51525354565758595b5c5d5e6061626365666768', + 'ecb-tbl-192: I=109'), + ('acadaaabbda2a3a00d0c0f0e595a5b5c', '302e341a3ebcd74f0d55f61714570284', + '6a6b6c6d6f70717274757677797a7b7c7e7f808183848586', + 'ecb-tbl-192: I=110'), + ('121310115655544b5253545569666764', '57abdd8231280da01c5042b78cf76522', + '88898a8b8d8e8f90929394959798999a9c9d9e9fa1a2a3a4', + 'ecb-tbl-192: I=111'), + ('dedfd0d166616063eaebe8e94142434c', '17f9ea7eea17ac1adf0e190fef799e92', + 'a6a7a8a9abacadaeb0b1b2b3b5b6b7b8babbbcbdbfc0c1c2', + 'ecb-tbl-192: I=112'), + ('dbdad9d81417161166677879e0e7e6e5', '2e1bdd563dd87ee5c338dd6d098d0a7a', + 'c4c5c6c7c9cacbcccecfd0d1d3d4d5d6d8d9dadbdddedfe0', + 'ecb-tbl-192: I=113'), + ('6a6b6c6de0efeeed2b2a2928c0c3c2c5', 'eb869996e6f8bfb2bfdd9e0c4504dbb2', + 'e2e3e4e5e7e8e9eaecedeeeff1f2f3f4f6f7f8f9fbfcfdfe', + 'ecb-tbl-192: I=114'), + ('b1b0b3b21714151a1a1b1c1d5649484b', 'c2e01549e9decf317468b3e018c61ba8', + '00010203050607080a0b0c0d0f10111214151617191a1b1c', + 'ecb-tbl-192: I=115'), + ('39380706a3a4a5a6c4c5c6c77271706f', '8da875d033c01dd463b244a1770f4a22', + '1e1f20212324252628292a2b2d2e2f30323334353738393a', + 'ecb-tbl-192: I=116'), + ('5c5d5e5f1013121539383736e2e5e4e7', '8ba0dcf3a186844f026d022f8839d696', + '3c3d3e3f41424344464748494b4c4d4e5051525355565758', + 'ecb-tbl-192: I=117'), + ('43424544ead5d4d72e2f2c2d64676661', 'e9691ff9a6cc6970e51670a0fd5b88c1', + '5a5b5c5d5f60616264656667696a6b6c6e6f707173747576', + 'ecb-tbl-192: I=118'), + ('55545756989b9a65f8f9feff18171615', 'f2baec06faeed30f88ee63ba081a6e5b', + '78797a7b7d7e7f80828384858788898a8c8d8e8f91929394', + 'ecb-tbl-192: I=119'), + ('05040b0a525554573c3d3e3f4a494847', '9c39d4c459ae5753394d6094adc21e78', + '969798999b9c9d9ea0a1a2a3a5a6a7a8aaabacadafb0b1b2', + 'ecb-tbl-192: I=120'), + ('14151617595a5b5c8584fbfa8e89888b', '6345b532a11904502ea43ba99c6bd2b2', + 'b4b5b6b7b9babbbcbebfc0c1c3c4c5c6c8c9cacbcdcecfd0', + 'ecb-tbl-192: I=121'), + ('7c7d7a7bfdf2f3f029282b2a51525354', '5ffae3061a95172e4070cedce1e428c8', + 'd2d3d4d5d7d8d9dadcdddedfe1e2e3e4e6e7e8e9ebecedee', + 'ecb-tbl-192: I=122'), + ('38393a3b1e1d1c1341404746c23d3c3e', '0a4566be4cdf9adce5dec865b5ab34cd', + 'f0f1f2f3f5f6f7f8fafbfcfdfe01000204050607090a0b0c', + 'ecb-tbl-192: I=123'), + ('8d8c939240474645818083827c7f7e41', 'ca17fcce79b7404f2559b22928f126fb', + '0e0f10111314151618191a1b1d1e1f20222324252728292a', + 'ecb-tbl-192: I=124'), + ('3b3a39381a19181f32333c3d45424340', '97ca39b849ed73a6470a97c821d82f58', + '2c2d2e2f31323334363738393b3c3d3e4041424345464748', + 'ecb-tbl-192: I=125'), + ('f0f1f6f738272625828380817f7c7d7a', '8198cb06bc684c6d3e9b7989428dcf7a', + '4a4b4c4d4f50515254555657595a5b5c5e5f606163646566', + 'ecb-tbl-192: I=126'), + ('89888b8a0407061966676061141b1a19', 'f53c464c705ee0f28d9a4c59374928bd', + '68696a6b6d6e6f70727374757778797a7c7d7e7f81828384', + 'ecb-tbl-192: I=127'), + ('d3d2dddcaaadacaf9c9d9e9fe8ebeae5', '9adb3d4cca559bb98c3e2ed73dbf1154', + '868788898b8c8d8e90919293959697989a9b9c9d9fa0a1a2', + 'ecb-tbl-192: I=128'), + + # ecb_tbl.txt, KEYSIZE=256 + ('834eadfccac7e1b30664b1aba44815ab', '1946dabf6a03a2a2c3d0b05080aed6fc', + '00010203050607080a0b0c0d0f10111214151617191a1b1c1e1f202123242526', + 'ecb-tbl-256: I=1'), + ('d9dc4dba3021b05d67c0518f72b62bf1', '5ed301d747d3cc715445ebdec62f2fb4', + '28292a2b2d2e2f30323334353738393a3c3d3e3f41424344464748494b4c4d4e', + 'ecb-tbl-256: I=2'), + ('a291d86301a4a739f7392173aa3c604c', '6585c8f43d13a6beab6419fc5935b9d0', + '50515253555657585a5b5c5d5f60616264656667696a6b6c6e6f707173747576', + 'ecb-tbl-256: I=3'), + ('4264b2696498de4df79788a9f83e9390', '2a5b56a596680fcc0e05f5e0f151ecae', + '78797a7b7d7e7f80828384858788898a8c8d8e8f91929394969798999b9c9d9e', + 'ecb-tbl-256: I=4'), + ('ee9932b3721804d5a83ef5949245b6f6', 'f5d6ff414fd2c6181494d20c37f2b8c4', + 'a0a1a2a3a5a6a7a8aaabacadafb0b1b2b4b5b6b7b9babbbcbebfc0c1c3c4c5c6', + 'ecb-tbl-256: I=5'), + ('e6248f55c5fdcbca9cbbb01c88a2ea77', '85399c01f59fffb5204f19f8482f00b8', + 'c8c9cacbcdcecfd0d2d3d4d5d7d8d9dadcdddedfe1e2e3e4e6e7e8e9ebecedee', + 'ecb-tbl-256: I=6'), + ('b8358e41b9dff65fd461d55a99266247', '92097b4c88a041ddf98144bc8d22e8e7', + 'f0f1f2f3f5f6f7f8fafbfcfdfe01000204050607090a0b0c0e0f101113141516', + 'ecb-tbl-256: I=7'), + ('f0e2d72260af58e21e015ab3a4c0d906', '89bd5b73b356ab412aef9f76cea2d65c', + '18191a1b1d1e1f20222324252728292a2c2d2e2f31323334363738393b3c3d3e', + 'ecb-tbl-256: I=8'), + ('475b8b823ce8893db3c44a9f2a379ff7', '2536969093c55ff9454692f2fac2f530', + '40414243454647484a4b4c4d4f50515254555657595a5b5c5e5f606163646566', + 'ecb-tbl-256: I=9'), + ('688f5281945812862f5f3076cf80412f', '07fc76a872843f3f6e0081ee9396d637', + '68696a6b6d6e6f70727374757778797a7c7d7e7f81828384868788898b8c8d8e', + 'ecb-tbl-256: I=10'), + ('08d1d2bc750af553365d35e75afaceaa', 'e38ba8ec2aa741358dcc93e8f141c491', + '90919293959697989a9b9c9d9fa0a1a2a4a5a6a7a9aaabacaeafb0b1b3b4b5b6', + 'ecb-tbl-256: I=11'), + ('8707121f47cc3efceca5f9a8474950a1', 'd028ee23e4a89075d0b03e868d7d3a42', + 'b8b9babbbdbebfc0c2c3c4c5c7c8c9cacccdcecfd1d2d3d4d6d7d8d9dbdcddde', + 'ecb-tbl-256: I=12'), + ('e51aa0b135dba566939c3b6359a980c5', '8cd9423dfc459e547155c5d1d522e540', + 'e0e1e2e3e5e6e7e8eaebecedeff0f1f2f4f5f6f7f9fafbfcfefe010103040506', + 'ecb-tbl-256: I=13'), + ('069a007fc76a459f98baf917fedf9521', '080e9517eb1677719acf728086040ae3', + '08090a0b0d0e0f10121314151718191a1c1d1e1f21222324262728292b2c2d2e', + 'ecb-tbl-256: I=14'), + ('726165c1723fbcf6c026d7d00b091027', '7c1700211a3991fc0ecded0ab3e576b0', + '30313233353637383a3b3c3d3f40414244454647494a4b4c4e4f505153545556', + 'ecb-tbl-256: I=15'), + ('d7c544de91d55cfcde1f84ca382200ce', 'dabcbcc855839251db51e224fbe87435', + '58595a5b5d5e5f60626364656768696a6c6d6e6f71727374767778797b7c7d7e', + 'ecb-tbl-256: I=16'), + ('fed3c9a161b9b5b2bd611b41dc9da357', '68d56fad0406947a4dd27a7448c10f1d', + '80818283858687888a8b8c8d8f90919294959697999a9b9c9e9fa0a1a3a4a5a6', + 'ecb-tbl-256: I=17'), + ('4f634cdc6551043409f30b635832cf82', 'da9a11479844d1ffee24bbf3719a9925', + 'a8a9aaabadaeafb0b2b3b4b5b7b8b9babcbdbebfc1c2c3c4c6c7c8c9cbcccdce', + 'ecb-tbl-256: I=18'), + ('109ce98db0dfb36734d9f3394711b4e6', '5e4ba572f8d23e738da9b05ba24b8d81', + 'd0d1d2d3d5d6d7d8dadbdcdddfe0e1e2e4e5e6e7e9eaebeceeeff0f1f3f4f5f6', + 'ecb-tbl-256: I=19'), + ('4ea6dfaba2d8a02ffdffa89835987242', 'a115a2065d667e3f0b883837a6e903f8', + '70717273757677787a7b7c7d7f80818284858687898a8b8c8e8f909193949596', + 'ecb-tbl-256: I=20'), + ('5ae094f54af58e6e3cdbf976dac6d9ef', '3e9e90dc33eac2437d86ad30b137e66e', + '98999a9b9d9e9fa0a2a3a4a5a7a8a9aaacadaeafb1b2b3b4b6b7b8b9bbbcbdbe', + 'ecb-tbl-256: I=21'), + ('764d8e8e0f29926dbe5122e66354fdbe', '01ce82d8fbcdae824cb3c48e495c3692', + 'c0c1c2c3c5c6c7c8cacbcccdcfd0d1d2d4d5d6d7d9dadbdcdedfe0e1e3e4e5e6', + 'ecb-tbl-256: I=22'), + ('3f0418f888cdf29a982bf6b75410d6a9', '0c9cff163ce936faaf083cfd3dea3117', + 'e8e9eaebedeeeff0f2f3f4f5f7f8f9fafcfdfeff01020304060708090b0c0d0e', + 'ecb-tbl-256: I=23'), + ('e4a3e7cb12cdd56aa4a75197a9530220', '5131ba9bd48f2bba85560680df504b52', + '10111213151617181a1b1c1d1f20212224252627292a2b2c2e2f303133343536', + 'ecb-tbl-256: I=24'), + ('211677684aac1ec1a160f44c4ebf3f26', '9dc503bbf09823aec8a977a5ad26ccb2', + '38393a3b3d3e3f40424344454748494a4c4d4e4f51525354565758595b5c5d5e', + 'ecb-tbl-256: I=25'), + ('d21e439ff749ac8f18d6d4b105e03895', '9a6db0c0862e506a9e397225884041d7', + '60616263656667686a6b6c6d6f70717274757677797a7b7c7e7f808183848586', + 'ecb-tbl-256: I=26'), + ('d9f6ff44646c4725bd4c0103ff5552a7', '430bf9570804185e1ab6365fc6a6860c', + '88898a8b8d8e8f90929394959798999a9c9d9e9fa1a2a3a4a6a7a8a9abacadae', + 'ecb-tbl-256: I=27'), + ('0b1256c2a00b976250cfc5b0c37ed382', '3525ebc02f4886e6a5a3762813e8ce8a', + 'b0b1b2b3b5b6b7b8babbbcbdbfc0c1c2c4c5c6c7c9cacbcccecfd0d1d3d4d5d6', + 'ecb-tbl-256: I=28'), + ('b056447ffc6dc4523a36cc2e972a3a79', '07fa265c763779cce224c7bad671027b', + 'd8d9dadbdddedfe0e2e3e4e5e7e8e9eaecedeeeff1f2f3f4f6f7f8f9fbfcfdfe', + 'ecb-tbl-256: I=29'), + ('5e25ca78f0de55802524d38da3fe4456', 'e8b72b4e8be243438c9fff1f0e205872', + '00010203050607080a0b0c0d0f10111214151617191a1b1c1e1f202123242526', + 'ecb-tbl-256: I=30'), + ('a5bcf4728fa5eaad8567c0dc24675f83', '109d4f999a0e11ace1f05e6b22cbcb50', + '28292a2b2d2e2f30323334353738393a3c3d3e3f41424344464748494b4c4d4e', + 'ecb-tbl-256: I=31'), + ('814e59f97ed84646b78b2ca022e9ca43', '45a5e8d4c3ed58403ff08d68a0cc4029', + '50515253555657585a5b5c5d5f60616264656667696a6b6c6e6f707173747576', + 'ecb-tbl-256: I=32'), + ('15478beec58f4775c7a7f5d4395514d7', '196865964db3d417b6bd4d586bcb7634', + '78797a7b7d7e7f80828384858788898a8c8d8e8f91929394969798999b9c9d9e', + 'ecb-tbl-256: I=33'), + ('253548ffca461c67c8cbc78cd59f4756', '60436ad45ac7d30d99195f815d98d2ae', + 'a0a1a2a3a5a6a7a8aaabacadafb0b1b2b4b5b6b7b9babbbcbebfc0c1c3c4c5c6', + 'ecb-tbl-256: I=34'), + ('fd7ad8d73b9b0f8cc41600640f503d65', 'bb07a23f0b61014b197620c185e2cd75', + 'c8c9cacbcdcecfd0d2d3d4d5d7d8d9dadcdddedfe1e2e3e4e6e7e8e9ebecedee', + 'ecb-tbl-256: I=35'), + ('06199de52c6cbf8af954cd65830bcd56', '5bc0b2850129c854423aff0751fe343b', + 'f0f1f2f3f5f6f7f8fafbfcfdfe01000204050607090a0b0c0e0f101113141516', + 'ecb-tbl-256: I=36'), + ('f17c4ffe48e44c61bd891e257e725794', '7541a78f96738e6417d2a24bd2beca40', + '18191a1b1d1e1f20222324252728292a2c2d2e2f31323334363738393b3c3d3e', + 'ecb-tbl-256: I=37'), + ('9a5b4a402a3e8a59be6bf5cd8154f029', 'b0a303054412882e464591f1546c5b9e', + '40414243454647484a4b4c4d4f50515254555657595a5b5c5e5f606163646566', + 'ecb-tbl-256: I=38'), + ('79bd40b91a7e07dc939d441782ae6b17', '778c06d8a355eeee214fcea14b4e0eef', + '68696a6b6d6e6f70727374757778797a7c7d7e7f81828384868788898b8c8d8e', + 'ecb-tbl-256: I=39'), + ('d8ceaaf8976e5fbe1012d8c84f323799', '09614206d15cbace63227d06db6beebb', + '90919293959697989a9b9c9d9fa0a1a2a4a5a6a7a9aaabacaeafb0b1b3b4b5b6', + 'ecb-tbl-256: I=40'), + ('3316e2751e2e388b083da23dd6ac3fbe', '41b97fb20e427a9fdbbb358d9262255d', + 'b8b9babbbdbebfc0c2c3c4c5c7c8c9cacccdcecfd1d2d3d4d6d7d8d9dbdcddde', + 'ecb-tbl-256: I=41'), + ('8b7cfbe37de7dca793521819242c5816', 'c1940f703d845f957652c2d64abd7adf', + 'e0e1e2e3e5e6e7e8eaebecedeff0f1f2f4f5f6f7f9fafbfcfefe010103040506', + 'ecb-tbl-256: I=42'), + ('f23f033c0eebf8ec55752662fd58ce68', 'd2d44fcdae5332343366db297efcf21b', + '08090a0b0d0e0f10121314151718191a1c1d1e1f21222324262728292b2c2d2e', + 'ecb-tbl-256: I=43'), + ('59eb34f6c8bdbacc5fc6ad73a59a1301', 'ea8196b79dbe167b6aa9896e287eed2b', + '30313233353637383a3b3c3d3f40414244454647494a4b4c4e4f505153545556', + 'ecb-tbl-256: I=44'), + ('dcde8b6bd5cf7cc22d9505e3ce81261a', 'd6b0b0c4ba6c7dbe5ed467a1e3f06c2d', + '58595a5b5d5e5f60626364656768696a6c6d6e6f71727374767778797b7c7d7e', + 'ecb-tbl-256: I=45'), + ('e33cf7e524fed781e7042ff9f4b35dc7', 'ec51eb295250c22c2fb01816fb72bcae', + '80818283858687888a8b8c8d8f90919294959697999a9b9c9e9fa0a1a3a4a5a6', + 'ecb-tbl-256: I=46'), + ('27963c8facdf73062867d164df6d064c', 'aded6630a07ce9c7408a155d3bd0d36f', + 'a8a9aaabadaeafb0b2b3b4b5b7b8b9babcbdbebfc1c2c3c4c6c7c8c9cbcccdce', + 'ecb-tbl-256: I=47'), + ('77b1ce386b551b995f2f2a1da994eef8', '697c9245b9937f32f5d1c82319f0363a', + 'd0d1d2d3d5d6d7d8dadbdcdddfe0e1e2e4e5e6e7e9eaebeceeeff0f1f3f4f5f6', + 'ecb-tbl-256: I=48'), + ('f083388b013679efcf0bb9b15d52ae5c', 'aad5ad50c6262aaec30541a1b7b5b19c', + 'f8f9fafbfdfefe00020304050708090a0c0d0e0f11121314161718191b1c1d1e', + 'ecb-tbl-256: I=49'), + ('c5009e0dab55db0abdb636f2600290c8', '7d34b893855341ec625bd6875ac18c0d', + '20212223252627282a2b2c2d2f30313234353637393a3b3c3e3f404143444546', + 'ecb-tbl-256: I=50'), + ('7804881e26cd532d8514d3683f00f1b9', '7ef05105440f83862f5d780e88f02b41', + '48494a4b4d4e4f50525354555758595a5c5d5e5f61626364666768696b6c6d6e', + 'ecb-tbl-256: I=51'), + ('46cddcd73d1eb53e675ca012870a92a3', 'c377c06403382061af2c9c93a8e70df6', + '70717273757677787a7b7c7d7f80818284858687898a8b8c8e8f909193949596', + 'ecb-tbl-256: I=52'), + ('a9fb44062bb07fe130a8e8299eacb1ab', '1dbdb3ffdc052dacc83318853abc6de5', + '98999a9b9d9e9fa0a2a3a4a5a7a8a9aaacadaeafb1b2b3b4b6b7b8b9bbbcbdbe', + 'ecb-tbl-256: I=53'), + ('2b6ff8d7a5cc3a28a22d5a6f221af26b', '69a6eab00432517d0bf483c91c0963c7', + 'c0c1c2c3c5c6c7c8cacbcccdcfd0d1d2d4d5d6d7d9dadbdcdedfe0e1e3e4e5e6', + 'ecb-tbl-256: I=54'), + ('1a9527c29b8add4b0e3e656dbb2af8b4', '0797f41dc217c80446e1d514bd6ab197', + 'e8e9eaebedeeeff0f2f3f4f5f7f8f9fafcfdfeff01020304060708090b0c0d0e', + 'ecb-tbl-256: I=55'), + ('7f99cf2c75244df015eb4b0c1050aeae', '9dfd76575902a637c01343c58e011a03', + '10111213151617181a1b1c1d1f20212224252627292a2b2c2e2f303133343536', + 'ecb-tbl-256: I=56'), + ('e84ff85b0d9454071909c1381646c4ed', 'acf4328ae78f34b9fa9b459747cc2658', + '38393a3b3d3e3f40424344454748494a4c4d4e4f51525354565758595b5c5d5e', + 'ecb-tbl-256: I=57'), + ('89afd40f99521280d5399b12404f6db4', 'b0479aea12bac4fe2384cf98995150c6', + '60616263656667686a6b6c6d6f70717274757677797a7b7c7e7f808183848586', + 'ecb-tbl-256: I=58'), + ('a09ef32dbc5119a35ab7fa38656f0329', '9dd52789efe3ffb99f33b3da5030109a', + '88898a8b8d8e8f90929394959798999a9c9d9e9fa1a2a3a4a6a7a8a9abacadae', + 'ecb-tbl-256: I=59'), + ('61773457f068c376c7829b93e696e716', 'abbb755e4621ef8f1214c19f649fb9fd', + 'b0b1b2b3b5b6b7b8babbbcbdbfc0c1c2c4c5c6c7c9cacbcccecfd0d1d3d4d5d6', + 'ecb-tbl-256: I=60'), + ('a34f0cae726cce41dd498747d891b967', 'da27fb8174357bce2bed0e7354f380f9', + 'd8d9dadbdddedfe0e2e3e4e5e7e8e9eaecedeeeff1f2f3f4f6f7f8f9fbfcfdfe', + 'ecb-tbl-256: I=61'), + ('856f59496c7388ee2d2b1a27b7697847', 'c59a0663f0993838f6e5856593bdc5ef', + '00010203050607080a0b0c0d0f10111214151617191a1b1c1e1f202123242526', + 'ecb-tbl-256: I=62'), + ('cb090c593ef7720bd95908fb93b49df4', 'ed60b264b5213e831607a99c0ce5e57e', + '28292a2b2d2e2f30323334353738393a3c3d3e3f41424344464748494b4c4d4e', + 'ecb-tbl-256: I=63'), + ('a0ac75cd2f1923d460fc4d457ad95baf', 'e50548746846f3eb77b8c520640884ed', + '50515253555657585a5b5c5d5f60616264656667696a6b6c6e6f707173747576', + 'ecb-tbl-256: I=64'), + ('2a2b282974777689e8e9eeef525d5c5f', '28282cc7d21d6a2923641e52d188ef0c', + '78797a7b7d7e7f80828384858788898a8c8d8e8f91929394969798999b9c9d9e', + 'ecb-tbl-256: I=65'), + ('909192939390919e0f0e09089788898a', '0dfa5b02abb18e5a815305216d6d4f8e', + 'a0a1a2a3a5a6a7a8aaabacadafb0b1b2b4b5b6b7b9babbbcbebfc0c1c3c4c5c6', + 'ecb-tbl-256: I=66'), + ('777675748d8e8f907170777649464744', '7359635c0eecefe31d673395fb46fb99', + 'c8c9cacbcdcecfd0d2d3d4d5d7d8d9dadcdddedfe1e2e3e4e6e7e8e9ebecedee', + 'ecb-tbl-256: I=67'), + ('717073720605040b2d2c2b2a05fafbf9', '73c679f7d5aef2745c9737bb4c47fb36', + 'f0f1f2f3f5f6f7f8fafbfcfdfe01000204050607090a0b0c0e0f101113141516', + 'ecb-tbl-256: I=68'), + ('64656667fefdfcc31b1a1d1ca5aaaba8', 'b192bd472a4d2eafb786e97458967626', + '18191a1b1d1e1f20222324252728292a2c2d2e2f31323334363738393b3c3d3e', + 'ecb-tbl-256: I=69'), + ('dbdad9d86a696867b5b4b3b2c8d7d6d5', '0ec327f6c8a2b147598ca3fde61dc6a4', + '40414243454647484a4b4c4d4f50515254555657595a5b5c5e5f606163646566', + 'ecb-tbl-256: I=70'), + ('5c5d5e5fe3e0e1fe31303736333c3d3e', 'fc418eb3c41b859b38d4b6f646629729', + '68696a6b6d6e6f70727374757778797a7c7d7e7f81828384868788898b8c8d8e', + 'ecb-tbl-256: I=71'), + ('545556574b48494673727574546b6a69', '30249e5ac282b1c981ea64b609f3a154', + '90919293959697989a9b9c9d9fa0a1a2a4a5a6a7a9aaabacaeafb0b1b3b4b5b6', + 'ecb-tbl-256: I=72'), + ('ecedeeefc6c5c4bb56575051f5fafbf8', '5e6e08646d12150776bb43c2d78a9703', + 'b8b9babbbdbebfc0c2c3c4c5c7c8c9cacccdcecfd1d2d3d4d6d7d8d9dbdcddde', + 'ecb-tbl-256: I=73'), + ('464744452724252ac9c8cfced2cdcccf', 'faeb3d5de652cd3447dceb343f30394a', + 'e0e1e2e3e5e6e7e8eaebecedeff0f1f2f4f5f6f7f9fafbfcfefe010103040506', + 'ecb-tbl-256: I=74'), + ('e6e7e4e54142435c878681801c131211', 'a8e88706823f6993ef80d05c1c7b2cf0', + '08090a0b0d0e0f10121314151718191a1c1d1e1f21222324262728292b2c2d2e', + 'ecb-tbl-256: I=75'), + ('72737071cfcccdc2f9f8fffe710e0f0c', '8ced86677e6e00a1a1b15968f2d3cce6', + '30313233353637383a3b3c3d3f40414244454647494a4b4c4e4f505153545556', + 'ecb-tbl-256: I=76'), + ('505152537370714ec3c2c5c4010e0f0c', '9fc7c23858be03bdebb84e90db6786a9', + '58595a5b5d5e5f60626364656768696a6c6d6e6f71727374767778797b7c7d7e', + 'ecb-tbl-256: I=77'), + ('a8a9aaab5c5f5e51aeafa8a93d222320', 'b4fbd65b33f70d8cf7f1111ac4649c36', + '80818283858687888a8b8c8d8f90919294959697999a9b9c9e9fa0a1a3a4a5a6', + 'ecb-tbl-256: I=78'), + ('dedfdcddf6f5f4eb10111617fef1f0f3', 'c5c32d5ed03c4b53cc8c1bd0ef0dbbf6', + 'a8a9aaabadaeafb0b2b3b4b5b7b8b9babcbdbebfc1c2c3c4c6c7c8c9cbcccdce', + 'ecb-tbl-256: I=79'), + ('bdbcbfbe5e5d5c530b0a0d0cfac5c4c7', 'd1a7f03b773e5c212464b63709c6a891', + 'd0d1d2d3d5d6d7d8dadbdcdddfe0e1e2e4e5e6e7e9eaebeceeeff0f1f3f4f5f6', + 'ecb-tbl-256: I=80'), + ('8a8b8889050606f8f4f5f2f3636c6d6e', '6b7161d8745947ac6950438ea138d028', + 'f8f9fafbfdfefe00020304050708090a0c0d0e0f11121314161718191b1c1d1e', + 'ecb-tbl-256: I=81'), + ('a6a7a4a54d4e4f40b2b3b4b539262724', 'fd47a9f7e366ee7a09bc508b00460661', + '20212223252627282a2b2c2d2f30313234353637393a3b3c3e3f404143444546', + 'ecb-tbl-256: I=82'), + ('9c9d9e9fe9eaebf40e0f08099b949596', '00d40b003dc3a0d9310b659b98c7e416', + '48494a4b4d4e4f50525354555758595a5c5d5e5f61626364666768696b6c6d6e', + 'ecb-tbl-256: I=83'), + ('2d2c2f2e1013121dcccdcacbed121310', 'eea4c79dcc8e2bda691f20ac48be0717', + '70717273757677787a7b7c7d7f80818284858687898a8b8c8e8f909193949596', + 'ecb-tbl-256: I=84'), + ('f4f5f6f7edeeefd0eaebecedf7f8f9fa', 'e78f43b11c204403e5751f89d05a2509', + '98999a9b9d9e9fa0a2a3a4a5a7a8a9aaacadaeafb1b2b3b4b6b7b8b9bbbcbdbe', + 'ecb-tbl-256: I=85'), + ('3d3c3f3e282b2a2573727574150a0b08', 'd0f0e3d1f1244bb979931e38dd1786ef', + 'c0c1c2c3c5c6c7c8cacbcccdcfd0d1d2d4d5d6d7d9dadbdcdedfe0e1e3e4e5e6', + 'ecb-tbl-256: I=86'), + ('b6b7b4b5f8fbfae5b4b5b2b3a0afaead', '042e639dc4e1e4dde7b75b749ea6f765', + 'e8e9eaebedeeeff0f2f3f4f5f7f8f9fafcfdfeff01020304060708090b0c0d0e', + 'ecb-tbl-256: I=87'), + ('b7b6b5b4989b9a95878681809ba4a5a6', 'bc032fdd0efe29503a980a7d07ab46a8', + '10111213151617181a1b1c1d1f20212224252627292a2b2c2e2f303133343536', + 'ecb-tbl-256: I=88'), + ('a8a9aaabe5e6e798e9e8efee4748494a', '0c93ac949c0da6446effb86183b6c910', + '38393a3b3d3e3f40424344454748494a4c4d4e4f51525354565758595b5c5d5e', + 'ecb-tbl-256: I=89'), + ('ecedeeefd9dadbd4b9b8bfbe657a7b78', 'e0d343e14da75c917b4a5cec4810d7c2', + '60616263656667686a6b6c6d6f70717274757677797a7b7c7e7f808183848586', + 'ecb-tbl-256: I=90'), + ('7f7e7d7c696a6b74cacbcccd929d9c9f', '0eafb821748408279b937b626792e619', + '88898a8b8d8e8f90929394959798999a9c9d9e9fa1a2a3a4a6a7a8a9abacadae', + 'ecb-tbl-256: I=91'), + ('08090a0b0605040bfffef9f8b9c6c7c4', 'fa1ac6e02d23b106a1fef18b274a553f', + 'b0b1b2b3b5b6b7b8babbbcbdbfc0c1c2c4c5c6c7c9cacbcccecfd0d1d3d4d5d6', + 'ecb-tbl-256: I=92'), + ('08090a0bf1f2f3ccfcfdfafb68676665', '0dadfe019cd12368075507df33c1a1e9', + 'd8d9dadbdddedfe0e2e3e4e5e7e8e9eaecedeeeff1f2f3f4f6f7f8f9fbfcfdfe', + 'ecb-tbl-256: I=93'), + ('cacbc8c93a393837050403020d121310', '3a0879b414465d9ffbaf86b33a63a1b9', + '00010203050607080a0b0c0d0f10111214151617191a1b1c1e1f202123242526', + 'ecb-tbl-256: I=94'), + ('e9e8ebea8281809f8f8e8988343b3a39', '62199fadc76d0be1805d3ba0b7d914bf', + '28292a2b2d2e2f30323334353738393a3c3d3e3f41424344464748494b4c4d4e', + 'ecb-tbl-256: I=95'), + ('515053524645444bd0d1d6d7340b0a09', '1b06d6c5d333e742730130cf78e719b4', + '50515253555657585a5b5c5d5f60616264656667696a6b6c6e6f707173747576', + 'ecb-tbl-256: I=96'), + ('42434041ecefee1193929594c6c9c8cb', 'f1f848824c32e9dcdcbf21580f069329', + '78797a7b7d7e7f80828384858788898a8c8d8e8f91929394969798999b9c9d9e', + 'ecb-tbl-256: I=97'), + ('efeeedecc2c1c0cf76777071455a5b58', '1a09050cbd684f784d8e965e0782f28a', + 'a0a1a2a3a5a6a7a8aaabacadafb0b1b2b4b5b6b7b9babbbcbebfc0c1c3c4c5c6', + 'ecb-tbl-256: I=98'), + ('5f5e5d5c3f3c3d221d1c1b1a19161714', '79c2969e7ded2ba7d088f3f320692360', + 'c8c9cacbcdcecfd0d2d3d4d5d7d8d9dadcdddedfe1e2e3e4e6e7e8e9ebecedee', + 'ecb-tbl-256: I=99'), + ('000102034142434c1c1d1a1b8d727371', '091a658a2f7444c16accb669450c7b63', + 'f0f1f2f3f5f6f7f8fafbfcfdfe01000204050607090a0b0c0e0f101113141516', + 'ecb-tbl-256: I=100'), + ('8e8f8c8db1b2b38c56575051050a0b08', '97c1e3a72cca65fa977d5ed0e8a7bbfc', + '18191a1b1d1e1f20222324252728292a2c2d2e2f31323334363738393b3c3d3e', + 'ecb-tbl-256: I=101'), + ('a7a6a5a4e8ebeae57f7e7978cad5d4d7', '70c430c6db9a17828937305a2df91a2a', + '40414243454647484a4b4c4d4f50515254555657595a5b5c5e5f606163646566', + 'ecb-tbl-256: I=102'), + ('8a8b888994979689454443429f909192', '629553457fbe2479098571c7c903fde8', + '68696a6b6d6e6f70727374757778797a7c7d7e7f81828384868788898b8c8d8e', + 'ecb-tbl-256: I=103'), + ('8c8d8e8fe0e3e2ed45444342f1cecfcc', 'a25b25a61f612669e7d91265c7d476ba', + '90919293959697989a9b9c9d9fa0a1a2a4a5a6a7a9aaabacaeafb0b1b3b4b5b6', + 'ecb-tbl-256: I=104'), + ('fffefdfc4c4f4e31d8d9dedfb6b9b8bb', 'eb7e4e49b8ae0f024570dda293254fed', + 'b8b9babbbdbebfc0c2c3c4c5c7c8c9cacccdcecfd1d2d3d4d6d7d8d9dbdcddde', + 'ecb-tbl-256: I=105'), + ('fdfcfffecccfcec12f2e29286679787b', '38fe15d61cca84516e924adce5014f67', + 'e0e1e2e3e5e6e7e8eaebecedeff0f1f2f4f5f6f7f9fafbfcfefe010103040506', + 'ecb-tbl-256: I=106'), + ('67666564bab9b8a77071767719161714', '3ad208492249108c9f3ebeb167ad0583', + '08090a0b0d0e0f10121314151718191a1c1d1e1f21222324262728292b2c2d2e', + 'ecb-tbl-256: I=107'), + ('9a9b98992d2e2f2084858283245b5a59', '299ba9f9bf5ab05c3580fc26edd1ed12', + '30313233353637383a3b3c3d3f40414244454647494a4b4c4e4f505153545556', + 'ecb-tbl-256: I=108'), + ('a4a5a6a70b0809365c5d5a5b2c232221', '19dc705b857a60fb07717b2ea5717781', + '58595a5b5d5e5f60626364656768696a6c6d6e6f71727374767778797b7c7d7e', + 'ecb-tbl-256: I=109'), + ('464744455754555af3f2f5f4afb0b1b2', 'ffc8aeb885b5efcad06b6dbebf92e76b', + '80818283858687888a8b8c8d8f90919294959697999a9b9c9e9fa0a1a3a4a5a6', + 'ecb-tbl-256: I=110'), + ('323330317675746b7273747549464744', 'f58900c5e0b385253ff2546250a0142b', + 'a8a9aaabadaeafb0b2b3b4b5b7b8b9babcbdbebfc1c2c3c4c6c7c8c9cbcccdce', + 'ecb-tbl-256: I=111'), + ('a8a9aaab181b1a15808186872b141516', '2ee67b56280bc462429cee6e3370cbc1', + 'd0d1d2d3d5d6d7d8dadbdcdddfe0e1e2e4e5e6e7e9eaebeceeeff0f1f3f4f5f6', + 'ecb-tbl-256: I=112'), + ('e7e6e5e4202323ddaaabacad343b3a39', '20db650a9c8e9a84ab4d25f7edc8f03f', + 'f8f9fafbfdfefe00020304050708090a0c0d0e0f11121314161718191b1c1d1e', + 'ecb-tbl-256: I=113'), + ('a8a9aaab2221202fedecebea1e010003', '3c36da169525cf818843805f25b78ae5', + '20212223252627282a2b2c2d2f30313234353637393a3b3c3e3f404143444546', + 'ecb-tbl-256: I=114'), + ('f9f8fbfa5f5c5d42424344450e010003', '9a781d960db9e45e37779042fea51922', + '48494a4b4d4e4f50525354555758595a5c5d5e5f61626364666768696b6c6d6e', + 'ecb-tbl-256: I=115'), + ('57565554f5f6f7f89697909120dfdedd', '6560395ec269c672a3c288226efdba77', + '70717273757677787a7b7c7d7f80818284858687898a8b8c8e8f909193949596', + 'ecb-tbl-256: I=116'), + ('f8f9fafbcccfcef1dddcdbda0e010003', '8c772b7a189ac544453d5916ebb27b9a', + '98999a9b9d9e9fa0a2a3a4a5a7a8a9aaacadaeafb1b2b3b4b6b7b8b9bbbcbdbe', + 'ecb-tbl-256: I=117'), + ('d9d8dbda7073727d80818687c2dddcdf', '77ca5468cc48e843d05f78eed9d6578f', + 'c0c1c2c3c5c6c7c8cacbcccdcfd0d1d2d4d5d6d7d9dadbdcdedfe0e1e3e4e5e6', + 'ecb-tbl-256: I=118'), + ('c5c4c7c6080b0a1588898e8f68676665', '72cdcc71dc82c60d4429c9e2d8195baa', + 'e8e9eaebedeeeff0f2f3f4f5f7f8f9fafcfdfeff01020304060708090b0c0d0e', + 'ecb-tbl-256: I=119'), + ('83828180dcdfded186878081f0cfcecd', '8080d68ce60e94b40b5b8b69eeb35afa', + '10111213151617181a1b1c1d1f20212224252627292a2b2c2e2f303133343536', + 'ecb-tbl-256: I=120'), + ('98999a9bdddedfa079787f7e0a050407', '44222d3cde299c04369d58ac0eba1e8e', + '38393a3b3d3e3f40424344454748494a4c4d4e4f51525354565758595b5c5d5e', + 'ecb-tbl-256: I=121'), + ('cecfcccd4f4c4d429f9e9998dfc0c1c2', '9b8721b0a8dfc691c5bc5885dbfcb27a', + '60616263656667686a6b6c6d6f70717274757677797a7b7c7e7f808183848586', + 'ecb-tbl-256: I=122'), + ('404142436665647b29282f2eaba4a5a6', '0dc015ce9a3a3414b5e62ec643384183', + '88898a8b8d8e8f90929394959798999a9c9d9e9fa1a2a3a4a6a7a8a9abacadae', + 'ecb-tbl-256: I=123'), + ('33323130e6e5e4eb23222524dea1a0a3', '705715448a8da412025ce38345c2a148', + 'b0b1b2b3b5b6b7b8babbbcbdbfc0c1c2c4c5c6c7c9cacbcccecfd0d1d3d4d5d6', + 'ecb-tbl-256: I=124'), + ('cfcecdccf6f5f4cbe6e7e0e199969794', 'c32b5b0b6fbae165266c569f4b6ecf0b', + 'd8d9dadbdddedfe0e2e3e4e5e7e8e9eaecedeeeff1f2f3f4f6f7f8f9fbfcfdfe', + 'ecb-tbl-256: I=125'), + ('babbb8b97271707fdcdddadb29363734', '4dca6c75192a01ddca9476af2a521e87', + '00010203050607080a0b0c0d0f10111214151617191a1b1c1e1f202123242526', + 'ecb-tbl-256: I=126'), + ('c9c8cbca4447465926272021545b5a59', '058691e627ecbc36ac07b6db423bd698', + '28292a2b2d2e2f30323334353738393a3c3d3e3f41424344464748494b4c4d4e', + 'ecb-tbl-256: I=127'), + ('050407067477767956575051221d1c1f', '7444527095838fe080fc2bcdd30847eb', + '50515253555657585a5b5c5d5f60616264656667696a6b6c6e6f707173747576', + 'ecb-tbl-256: I=128'), + + # FIPS PUB 800-38A test vectors, 2001 edition. Annex F. + + ('6bc1bee22e409f96e93d7e117393172a'+'ae2d8a571e03ac9c9eb76fac45af8e51'+ + '30c81c46a35ce411e5fbc1191a0a52ef'+'f69f2445df4f9b17ad2b417be66c3710', + '3ad77bb40d7a3660a89ecaf32466ef97'+'f5d3d58503b9699de785895a96fdbaaf'+ + '43b1cd7f598ece23881b00e3ed030688'+'7b0c785e27e8ad3f8223207104725dd4', + '2b7e151628aed2a6abf7158809cf4f3c', + 'NIST 800-38A, F.1.1, ECB and AES-128'), + + ('6bc1bee22e409f96e93d7e117393172a'+'ae2d8a571e03ac9c9eb76fac45af8e51'+ + '30c81c46a35ce411e5fbc1191a0a52ef'+'f69f2445df4f9b17ad2b417be66c3710', + 'bd334f1d6e45f25ff712a214571fa5cc'+'974104846d0ad3ad7734ecb3ecee4eef'+ + 'ef7afd2270e2e60adce0ba2face6444e'+'9a4b41ba738d6c72fb16691603c18e0e', + '8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b', + 'NIST 800-38A, F.1.3, ECB and AES-192'), + + ('6bc1bee22e409f96e93d7e117393172a'+'ae2d8a571e03ac9c9eb76fac45af8e51'+ + '30c81c46a35ce411e5fbc1191a0a52ef'+'f69f2445df4f9b17ad2b417be66c3710', + 'f3eed1bdb5d2a03c064b5a7e3db181f8'+'591ccb10d410ed26dc5ba74a31362870'+ + 'b6ed21b99ca6f4f9f153e7b1beafed1d'+'23304b7a39f9f3ff067d8d8f9e24ecc7', + '603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4', + 'NIST 800-38A, F.1.3, ECB and AES-256'), + + ('6bc1bee22e409f96e93d7e117393172a'+'ae2d8a571e03ac9c9eb76fac45af8e51'+ + '30c81c46a35ce411e5fbc1191a0a52ef'+'f69f2445df4f9b17ad2b417be66c3710', + '7649abac8119b246cee98e9b12e9197d'+'5086cb9b507219ee95db113a917678b2'+ + '73bed6b8e3c1743b7116e69e22229516'+'3ff1caa1681fac09120eca307586e1a7', + '2b7e151628aed2a6abf7158809cf4f3c', + 'NIST 800-38A, F.2.1, CBC and AES-128', + dict(mode='CBC', iv='000102030405060708090a0b0c0d0e0f')), + + ('6bc1bee22e409f96e93d7e117393172a'+'ae2d8a571e03ac9c9eb76fac45af8e51'+ + '30c81c46a35ce411e5fbc1191a0a52ef'+'f69f2445df4f9b17ad2b417be66c3710', + '4f021db243bc633d7178183a9fa071e8'+'b4d9ada9ad7dedf4e5e738763f69145a'+ + '571b242012fb7ae07fa9baac3df102e0'+'08b0e27988598881d920a9e64f5615cd', + '8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b', + 'NIST 800-38A, F.2.1, CBC and AES-192', + dict(mode='CBC', iv='000102030405060708090a0b0c0d0e0f')), + + ('6bc1bee22e409f96e93d7e117393172a'+'ae2d8a571e03ac9c9eb76fac45af8e51'+ + '30c81c46a35ce411e5fbc1191a0a52ef'+'f69f2445df4f9b17ad2b417be66c3710', + 'f58c4c04d6e5f1ba779eabfb5f7bfbd6'+'9cfc4e967edb808d679f777bc6702c7d'+ + '39f23369a9d9bacfa530e26304231461'+'b2eb05e2c39be9fcda6c19078c6a9d1b', + '603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4', + 'NIST 800-38A, F.2.1, CBC and AES-256', + dict(mode='CBC', iv='000102030405060708090a0b0c0d0e0f')), + + # Skip CFB-1 since it is not supported by PyCrypto + + ('6bc1bee22e409f96e93d7e117393172aae2d','3b79424c9c0dd436bace9e0ed4586a4f32b9', + '2b7e151628aed2a6abf7158809cf4f3c', + 'NIST 800-38A, F.3.7, CFB-8 and AES-128', + dict(mode='CFB', iv='000102030405060708090a0b0c0d0e0f', segment_size=8)), + + ('6bc1bee22e409f96e93d7e117393172aae2d','cda2521ef0a905ca44cd057cbf0d47a0678a', + '8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b', + 'NIST 800-38A, F.3.9, CFB-8 and AES-192', + dict(mode='CFB', iv='000102030405060708090a0b0c0d0e0f', segment_size=8)), + + ('6bc1bee22e409f96e93d7e117393172aae2d','dc1f1a8520a64db55fcc8ac554844e889700', + '603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4', + 'NIST 800-38A, F.3.11, CFB-8 and AES-256', + dict(mode='CFB', iv='000102030405060708090a0b0c0d0e0f', segment_size=8)), + + ('6bc1bee22e409f96e93d7e117393172a'+'ae2d8a571e03ac9c9eb76fac45af8e51'+ + '30c81c46a35ce411e5fbc1191a0a52ef'+'f69f2445df4f9b17ad2b417be66c3710', + '3b3fd92eb72dad20333449f8e83cfb4a'+'c8a64537a0b3a93fcde3cdad9f1ce58b'+ + '26751f67a3cbb140b1808cf187a4f4df'+'c04b05357c5d1c0eeac4c66f9ff7f2e6', + '2b7e151628aed2a6abf7158809cf4f3c', + 'NIST 800-38A, F.3.13, CFB-128 and AES-128', + dict(mode='CFB', iv='000102030405060708090a0b0c0d0e0f', segment_size=128)), + + ('6bc1bee22e409f96e93d7e117393172a'+'ae2d8a571e03ac9c9eb76fac45af8e51'+ + '30c81c46a35ce411e5fbc1191a0a52ef'+'f69f2445df4f9b17ad2b417be66c3710', + 'cdc80d6fddf18cab34c25909c99a4174'+'67ce7f7f81173621961a2b70171d3d7a'+ + '2e1e8a1dd59b88b1c8e60fed1efac4c9'+'c05f9f9ca9834fa042ae8fba584b09ff', + '8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b', + 'NIST 800-38A, F.3.15, CFB-128 and AES-192', + dict(mode='CFB', iv='000102030405060708090a0b0c0d0e0f', segment_size=128)), + + ('6bc1bee22e409f96e93d7e117393172a'+'ae2d8a571e03ac9c9eb76fac45af8e51'+ + '30c81c46a35ce411e5fbc1191a0a52ef'+'f69f2445df4f9b17ad2b417be66c3710', + 'dc7e84bfda79164b7ecd8486985d3860'+'39ffed143b28b1c832113c6331e5407b'+ + 'df10132415e54b92a13ed0a8267ae2f9'+'75a385741ab9cef82031623d55b1e471', + '603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4', + 'NIST 800-38A, F.3.17, CFB-128 and AES-256', + dict(mode='CFB', iv='000102030405060708090a0b0c0d0e0f', segment_size=128)), + + ('6bc1bee22e409f96e93d7e117393172a'+'ae2d8a571e03ac9c9eb76fac45af8e51'+ + '30c81c46a35ce411e5fbc1191a0a52ef'+'f69f2445df4f9b17ad2b417be66c3710', + '3b3fd92eb72dad20333449f8e83cfb4a'+'7789508d16918f03f53c52dac54ed825'+ + '9740051e9c5fecf64344f7a82260edcc'+'304c6528f659c77866a510d9c1d6ae5e', + '2b7e151628aed2a6abf7158809cf4f3c', + 'NIST 800-38A, F.4.1, OFB and AES-128', + dict(mode='OFB', iv='000102030405060708090a0b0c0d0e0f')), + + ('6bc1bee22e409f96e93d7e117393172a'+'ae2d8a571e03ac9c9eb76fac45af8e51'+ + '30c81c46a35ce411e5fbc1191a0a52ef'+'f69f2445df4f9b17ad2b417be66c3710', + 'cdc80d6fddf18cab34c25909c99a4174'+'fcc28b8d4c63837c09e81700c1100401'+ + '8d9a9aeac0f6596f559c6d4daf59a5f2'+'6d9f200857ca6c3e9cac524bd9acc92a', + '8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b', + 'NIST 800-38A, F.4.3, OFB and AES-192', + dict(mode='OFB', iv='000102030405060708090a0b0c0d0e0f')), + + ('6bc1bee22e409f96e93d7e117393172a'+'ae2d8a571e03ac9c9eb76fac45af8e51'+ + '30c81c46a35ce411e5fbc1191a0a52ef'+'f69f2445df4f9b17ad2b417be66c3710', + 'dc7e84bfda79164b7ecd8486985d3860'+'4febdc6740d20b3ac88f6ad82a4fb08d'+ + '71ab47a086e86eedf39d1c5bba97c408'+'0126141d67f37be8538f5a8be740e484', + '603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4', + 'NIST 800-38A, F.4.5, OFB and AES-256', + dict(mode='OFB', iv='000102030405060708090a0b0c0d0e0f')), + + ('6bc1bee22e409f96e93d7e117393172a'+'ae2d8a571e03ac9c9eb76fac45af8e51'+ + '30c81c46a35ce411e5fbc1191a0a52ef'+'f69f2445df4f9b17ad2b417be66c3710', + '874d6191b620e3261bef6864990db6ce'+'9806f66b7970fdff8617187bb9fffdff'+ + '5ae4df3edbd5d35e5b4f09020db03eab'+'1e031dda2fbe03d1792170a0f3009cee', + '2b7e151628aed2a6abf7158809cf4f3c', + 'NIST 800-38A, F.5.1, CTR and AES-128', + dict(mode='CTR', ctr_params=dict(nbits=16, prefix='f0f1f2f3f4f5f6f7f8f9fafbfcfd', initial_value=0xfeff))), + + ('6bc1bee22e409f96e93d7e117393172a'+'ae2d8a571e03ac9c9eb76fac45af8e51'+ + '30c81c46a35ce411e5fbc1191a0a52ef'+'f69f2445df4f9b17ad2b417be66c3710', + '1abc932417521ca24f2b0459fe7e6e0b'+'090339ec0aa6faefd5ccc2c6f4ce8e94'+ + '1e36b26bd1ebc670d1bd1d665620abf7'+'4f78a7f6d29809585a97daec58c6b050', + '8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b', + 'NIST 800-38A, F.5.3, CTR and AES-192', + dict(mode='CTR', ctr_params=dict(nbits=16, prefix='f0f1f2f3f4f5f6f7f8f9fafbfcfd', initial_value=0xfeff))), + + ('6bc1bee22e409f96e93d7e117393172a'+'ae2d8a571e03ac9c9eb76fac45af8e51'+ + '30c81c46a35ce411e5fbc1191a0a52ef'+'f69f2445df4f9b17ad2b417be66c3710', + '601ec313775789a5b7a7f504bbf3d228'+'f443e3ca4d62b59aca84e990cacaf5c5'+ + '2b0930daa23de94ce87017ba2d84988d'+'dfc9c58db67aada613c2dd08457941a6', + '603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4', + 'NIST 800-38A, F.5.5, CTR and AES-256', + dict(mode='CTR', ctr_params=dict(nbits=16, prefix='f0f1f2f3f4f5f6f7f8f9fafbfcfd', initial_value=0xfeff))), + + # RFC 3686 test vectors + # This is a list of (plaintext, ciphertext, key[, description[, params]]) tuples. + ('53696e676c6520626c6f636b206d7367', 'e4095d4fb7a7b3792d6175a3261311b8', + 'ae6852f8121067cc4bf7a5765577f39e', + 'RFC 3686 Test Vector #1: Encrypting 16 octets using AES-CTR with 128-bit key', + dict(mode='CTR', ctr_params=dict(nbits=32, prefix='00000030'+'0000000000000000'))), + ('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f', + '5104a106168a72d9790d41ee8edad388eb2e1efc46da57c8fce630df9141be28', + '7e24067817fae0d743d6ce1f32539163', + 'RFC 3686 Test Vector #2: Encrypting 32 octets using AES-CTR with 128-bit key', + dict(mode='CTR', ctr_params=dict(nbits=32, prefix='006cb6db'+'c0543b59da48d90b'))), + ('000102030405060708090a0b0c0d0e0f'+'101112131415161718191a1b1c1d1e1f'+'20212223', + 'c1cf48a89f2ffdd9cf4652e9efdb72d7'+'4540a42bde6d7836d59a5ceaaef31053'+'25b2072f', + '7691be035e5020a8ac6e618529f9a0dc', + 'RFC 3686 Test Vector #3: Encrypting 36 octets using AES-CTR with 128-bit key', + dict(mode='CTR', ctr_params=dict(nbits=32, prefix='00e0017b'+'27777f3f4a1786f0'))), + ('53696e676c6520626c6f636b206d7367', + '4b55384fe259c9c84e7935a003cbe928', + '16af5b145fc9f579c175f93e3bfb0eed'+'863d06ccfdb78515', + 'RFC 3686 Test Vector #4: Encrypting 16 octets using AES-CTR with 192-bit key', + dict(mode='CTR', ctr_params=dict(nbits=32, prefix='00000048'+'36733c147d6d93cb'))), + ('000102030405060708090a0b0c0d0e0f'+'101112131415161718191a1b1c1d1e1f', + '453243fc609b23327edfaafa7131cd9f'+'8490701c5ad4a79cfc1fe0ff42f4fb00', + '7c5cb2401b3dc33c19e7340819e0f69c'+'678c3db8e6f6a91a', + 'RFC 3686 Test Vector #5: Encrypting 32 octets using AES-CTR with 192-bit key', + dict(mode='CTR', ctr_params=dict(nbits=32, prefix='0096b03b'+'020c6eadc2cb500d'))), + ('000102030405060708090a0b0c0d0e0f'+'101112131415161718191a1b1c1d1e1f'+'20212223', + '96893fc55e5c722f540b7dd1ddf7e758'+'d288bc95c69165884536c811662f2188'+'abee0935', + '02bf391ee8ecb159b959617b0965279b'+'f59b60a786d3e0fe', + 'RFC 3686 Test Vector #6: Encrypting 36 octets using AES-CTR with 192-bit key', + dict(mode='CTR', ctr_params=dict(nbits=32, prefix='0007bdfd'+'5cbd60278dcc0912'))), + ('53696e676c6520626c6f636b206d7367', + '145ad01dbf824ec7560863dc71e3e0c0', + '776beff2851db06f4c8a0542c8696f6c'+'6a81af1eec96b4d37fc1d689e6c1c104', + 'RFC 3686 Test Vector #7: Encrypting 16 octets using AES-CTR with 256-bit key', + dict(mode='CTR', ctr_params=dict(nbits=32, prefix='00000060'+'db5672c97aa8f0b2'))), + ('000102030405060708090a0b0c0d0e0f'+'101112131415161718191a1b1c1d1e1f', + 'f05e231b3894612c49ee000b804eb2a9'+'b8306b508f839d6a5530831d9344af1c', + 'f6d66d6bd52d59bb0796365879eff886'+'c66dd51a5b6a99744b50590c87a23884', + 'RFC 3686 Test Vector #8: Encrypting 32 octets using AES-CTR with 256-bit key', + dict(mode='CTR', ctr_params=dict(nbits=32, prefix='00faac24'+'c1585ef15a43d875'))), + ('000102030405060708090a0b0c0d0e0f'+'101112131415161718191a1b1c1d1e1f'+'20212223', + 'eb6c52821d0bbbf7ce7594462aca4faa'+'b407df866569fd07f48cc0b583d6071f'+'1ec0e6b8', + 'ff7a617ce69148e4f1726e2f43581de2'+'aa62d9f805532edff1eed687fb54153d', + 'RFC 3686 Test Vector #9: Encrypting 36 octets using AES-CTR with 256-bit key', + dict(mode='CTR', ctr_params=dict(nbits=32, prefix='001cc5b7'+'51a51d70a1c11148'))), + + # The following test vectors have been generated with gpg v1.4.0. + # The command line used was: + # + # gpg -c -z 0 --cipher-algo AES --passphrase secret_passphrase \ + # --disable-mdc --s2k-mode 0 --output ct pt + # + # As result, the content of the file 'pt' is encrypted with a key derived + # from 'secret_passphrase' and written to file 'ct'. + # Test vectors must be extracted from 'ct', which is a collection of + # TLVs (see RFC4880 for all details): + # - the encrypted data (with the encrypted IV as prefix) is the payload + # of the TLV with tag 9 (Symmetrical Encrypted Data Packet). + # This is the ciphertext in the test vector. + # - inside the encrypted part, there is a further layer of TLVs. One must + # look for tag 11 (Literal Data Packet); in its payload, after a short + # but time dependent header, there is the content of file 'pt'. + # In the test vector, the plaintext is the complete set of TLVs that gets + # encrypted. It is not just the content of 'pt'. + # - the key is the leftmost 16 bytes of the SHA1 digest of the password. + # The test vector contains such shortened digest. + # + # Note that encryption uses a clear IV, and decryption an encrypted IV + ( 'ac18620270744fb4f647426c61636b4361745768697465436174', # Plaintext, 'BlackCatWhiteCat' + 'dc6b9e1f095de609765c59983db5956ae4f63aea7405389d2ebb', # Ciphertext + '5baa61e4c9b93f3f0682250b6cf8331b', # Key (hash of 'password') + 'GPG Test Vector #1', + dict(mode='OPENPGP', iv='3d7d3e62282add7eb203eeba5c800733', encrypted_iv='fd934601ef49cb58b6d9aebca6056bdb96ef' ) ), +] + +def get_tests(config={}): + from Crypto.Cipher import AES + from common import make_block_tests + return make_block_tests(AES, "AES", test_data) + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_ARC2.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_ARC2.py new file mode 100644 index 0000000..b6bc519 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_ARC2.py @@ -0,0 +1,124 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Cipher/ARC2.py: Self-test for the Alleged-RC2 cipher +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for Crypto.Cipher.ARC2""" + +__revision__ = "$Id$" + +from common import dict # For compatibility with Python 2.1 and 2.2 + +import unittest +from Crypto.Util.py3compat import * + +# This is a list of (plaintext, ciphertext, key[, description[, extra_params]]) tuples. +test_data = [ + # Test vectors from RFC 2268 + + # 63-bit effective key length + ('0000000000000000', 'ebb773f993278eff', '0000000000000000', + 'RFC2268-1', dict(effective_keylen=63)), + + # 64-bit effective key length + ('ffffffffffffffff', '278b27e42e2f0d49', 'ffffffffffffffff', + 'RFC2268-2', dict(effective_keylen=64)), + ('1000000000000001', '30649edf9be7d2c2', '3000000000000000', + 'RFC2268-3', dict(effective_keylen=64)), + ('0000000000000000', '61a8a244adacccf0', '88', + 'RFC2268-4', dict(effective_keylen=64)), + ('0000000000000000', '6ccf4308974c267f', '88bca90e90875a', + 'RFC2268-5', dict(effective_keylen=64)), + ('0000000000000000', '1a807d272bbe5db1', '88bca90e90875a7f0f79c384627bafb2', + 'RFC2268-6', dict(effective_keylen=64)), + + # 128-bit effective key length + ('0000000000000000', '2269552ab0f85ca6', '88bca90e90875a7f0f79c384627bafb2', + "RFC2268-7", dict(effective_keylen=128)), + ('0000000000000000', '5b78d3a43dfff1f1', + '88bca90e90875a7f0f79c384627bafb216f80a6f85920584c42fceb0be255daf1e', + "RFC2268-8", dict(effective_keylen=129)), + + # Test vectors from PyCrypto 2.0.1's testdata.py + # 1024-bit effective key length + ('0000000000000000', '624fb3e887419e48', '5068696c6970476c617373', + 'PCTv201-0'), + ('ffffffffffffffff', '79cadef44c4a5a85', '5068696c6970476c617373', + 'PCTv201-1'), + ('0001020304050607', '90411525b34e4c2c', '5068696c6970476c617373', + 'PCTv201-2'), + ('0011223344556677', '078656aaba61cbfb', '5068696c6970476c617373', + 'PCTv201-3'), + ('0000000000000000', 'd7bcc5dbb4d6e56a', 'ffffffffffffffff', + 'PCTv201-4'), + ('ffffffffffffffff', '7259018ec557b357', 'ffffffffffffffff', + 'PCTv201-5'), + ('0001020304050607', '93d20a497f2ccb62', 'ffffffffffffffff', + 'PCTv201-6'), + ('0011223344556677', 'cb15a7f819c0014d', 'ffffffffffffffff', + 'PCTv201-7'), + ('0000000000000000', '63ac98cdf3843a7a', 'ffffffffffffffff5065746572477265656e6177617953e5ffe553', + 'PCTv201-8'), + ('ffffffffffffffff', '3fb49e2fa12371dd', 'ffffffffffffffff5065746572477265656e6177617953e5ffe553', + 'PCTv201-9'), + ('0001020304050607', '46414781ab387d5f', 'ffffffffffffffff5065746572477265656e6177617953e5ffe553', + 'PCTv201-10'), + ('0011223344556677', 'be09dc81feaca271', 'ffffffffffffffff5065746572477265656e6177617953e5ffe553', + 'PCTv201-11'), + ('0000000000000000', 'e64221e608be30ab', '53e5ffe553', + 'PCTv201-12'), + ('ffffffffffffffff', '862bc60fdcd4d9a9', '53e5ffe553', + 'PCTv201-13'), + ('0001020304050607', '6a34da50fa5e47de', '53e5ffe553', + 'PCTv201-14'), + ('0011223344556677', '584644c34503122c', '53e5ffe553', + 'PCTv201-15'), +] + +class BufferOverflowTest(unittest.TestCase): + # Test a buffer overflow found in older versions of PyCrypto + + def setUp(self): + global ARC2 + from Crypto.Cipher import ARC2 + + def runTest(self): + """ARC2 with keylength > 128""" + key = "x" * 16384 + mode = ARC2.MODE_ECB + self.assertRaises(ValueError, ARC2.new, key, mode) + +def get_tests(config={}): + from Crypto.Cipher import ARC2 + from common import make_block_tests + + tests = make_block_tests(ARC2, "ARC2", test_data) + tests.append(BufferOverflowTest()) + + return tests + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_ARC4.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_ARC4.py new file mode 100644 index 0000000..4e039d1 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_ARC4.py @@ -0,0 +1,81 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Cipher/ARC4.py: Self-test for the Alleged-RC4 cipher +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for Crypto.Cipher.ARC4""" + +__revision__ = "$Id$" + +from Crypto.Util.py3compat import * + +# This is a list of (plaintext, ciphertext, key[, description]) tuples. +test_data = [ + # Test vectors from Eric Rescorla's message with the subject + # "RC4 compatibility testing", sent to the cipherpunks mailing list on + # September 13, 1994. + # http://cypherpunks.venona.com/date/1994/09/msg00420.html + + ('0123456789abcdef', '75b7878099e0c596', '0123456789abcdef', + 'Test vector 0'), + + ('0000000000000000', '7494c2e7104b0879', '0123456789abcdef', + 'Test vector 1'), + + ('0000000000000000', 'de188941a3375d3a', '0000000000000000', + 'Test vector 2'), + + ('00000000000000000000', 'd6a141a7ec3c38dfbd61', 'ef012345', + 'Test vector 3'), + + ('01' * 512, + '7595c3e6114a09780c4ad452338e1ffd9a1be9498f813d76533449b6778dcad8' + + 'c78a8d2ba9ac66085d0e53d59c26c2d1c490c1ebbe0ce66d1b6b1b13b6b919b8' + + '47c25a91447a95e75e4ef16779cde8bf0a95850e32af9689444fd377108f98fd' + + 'cbd4e726567500990bcc7e0ca3c4aaa304a387d20f3b8fbbcd42a1bd311d7a43' + + '03dda5ab078896ae80c18b0af66dff319616eb784e495ad2ce90d7f772a81747' + + 'b65f62093b1e0db9e5ba532fafec47508323e671327df9444432cb7367cec82f' + + '5d44c0d00b67d650a075cd4b70dedd77eb9b10231b6b5b741347396d62897421' + + 'd43df9b42e446e358e9c11a9b2184ecbef0cd8e7a877ef968f1390ec9b3d35a5' + + '585cb009290e2fcde7b5ec66d9084be44055a619d9dd7fc3166f9487f7cb2729' + + '12426445998514c15d53a18c864ce3a2b7555793988126520eacf2e3066e230c' + + '91bee4dd5304f5fd0405b35bd99c73135d3d9bc335ee049ef69b3867bf2d7bd1' + + 'eaa595d8bfc0066ff8d31509eb0c6caa006c807a623ef84c3d33c195d23ee320' + + 'c40de0558157c822d4b8c569d849aed59d4e0fd7f379586b4b7ff684ed6a189f' + + '7486d49b9c4bad9ba24b96abf924372c8a8fffb10d55354900a77a3db5f205e1' + + 'b99fcd8660863a159ad4abe40fa48934163ddde542a6585540fd683cbfd8c00f' + + '12129a284deacc4cdefe58be7137541c047126c8d49e2755ab181ab7e940b0c0', + '0123456789abcdef', + "Test vector 4"), +] + +def get_tests(config={}): + from Crypto.Cipher import ARC4 + from common import make_stream_tests + return make_stream_tests(ARC4, "ARC4", test_data) + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_Blowfish.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_Blowfish.py new file mode 100644 index 0000000..e8f73a6 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_Blowfish.py @@ -0,0 +1,113 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Cipher/test_Blowfish.py: Self-test for the Blowfish cipher +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for Crypto.Cipher.Blowfish""" + +__revision__ = "$Id$" + +from Crypto.Util.py3compat import * + +# This is a list of (plaintext, ciphertext, key) tuples. +test_data = [ + # Test vectors from http://www.schneier.com/code/vectors.txt + ('0000000000000000', '4ef997456198dd78', '0000000000000000'), + ('ffffffffffffffff', '51866fd5b85ecb8a', 'ffffffffffffffff'), + ('1000000000000001', '7d856f9a613063f2', '3000000000000000'), + ('1111111111111111', '2466dd878b963c9d', '1111111111111111'), + ('1111111111111111', '61f9c3802281b096', '0123456789abcdef'), + ('0123456789abcdef', '7d0cc630afda1ec7', '1111111111111111'), + ('0000000000000000', '4ef997456198dd78', '0000000000000000'), + ('0123456789abcdef', '0aceab0fc6a0a28d', 'fedcba9876543210'), + ('01a1d6d039776742', '59c68245eb05282b', '7ca110454a1a6e57'), + ('5cd54ca83def57da', 'b1b8cc0b250f09a0', '0131d9619dc1376e'), + ('0248d43806f67172', '1730e5778bea1da4', '07a1133e4a0b2686'), + ('51454b582ddf440a', 'a25e7856cf2651eb', '3849674c2602319e'), + ('42fd443059577fa2', '353882b109ce8f1a', '04b915ba43feb5b6'), + ('059b5e0851cf143a', '48f4d0884c379918', '0113b970fd34f2ce'), + ('0756d8e0774761d2', '432193b78951fc98', '0170f175468fb5e6'), + ('762514b829bf486a', '13f04154d69d1ae5', '43297fad38e373fe'), + ('3bdd119049372802', '2eedda93ffd39c79', '07a7137045da2a16'), + ('26955f6835af609a', 'd887e0393c2da6e3', '04689104c2fd3b2f'), + ('164d5e404f275232', '5f99d04f5b163969', '37d06bb516cb7546'), + ('6b056e18759f5cca', '4a057a3b24d3977b', '1f08260d1ac2465e'), + ('004bd6ef09176062', '452031c1e4fada8e', '584023641aba6176'), + ('480d39006ee762f2', '7555ae39f59b87bd', '025816164629b007'), + ('437540c8698f3cfa', '53c55f9cb49fc019', '49793ebc79b3258f'), + ('072d43a077075292', '7a8e7bfa937e89a3', '4fb05e1515ab73a7'), + ('02fe55778117f12a', 'cf9c5d7a4986adb5', '49e95d6d4ca229bf'), + ('1d9d5c5018f728c2', 'd1abb290658bc778', '018310dc409b26d6'), + ('305532286d6f295a', '55cb3774d13ef201', '1c587f1c13924fef'), + ('0123456789abcdef', 'fa34ec4847b268b2', '0101010101010101'), + ('0123456789abcdef', 'a790795108ea3cae', '1f1f1f1f0e0e0e0e'), + ('0123456789abcdef', 'c39e072d9fac631d', 'e0fee0fef1fef1fe'), + ('ffffffffffffffff', '014933e0cdaff6e4', '0000000000000000'), + ('0000000000000000', 'f21e9a77b71c49bc', 'ffffffffffffffff'), + ('0000000000000000', '245946885754369a', '0123456789abcdef'), + ('ffffffffffffffff', '6b5c5a9c5d9e0a5a', 'fedcba9876543210'), + ('fedcba9876543210', 'f9ad597c49db005e', 'f0'), + ('fedcba9876543210', 'e91d21c1d961a6d6', 'f0e1'), + ('fedcba9876543210', 'e9c2b70a1bc65cf3', 'f0e1d2'), + ('fedcba9876543210', 'be1e639408640f05', 'f0e1d2c3'), + ('fedcba9876543210', 'b39e44481bdb1e6e', 'f0e1d2c3b4'), + ('fedcba9876543210', '9457aa83b1928c0d', 'f0e1d2c3b4a5'), + ('fedcba9876543210', '8bb77032f960629d', 'f0e1d2c3b4a596'), + ('fedcba9876543210', 'e87a244e2cc85e82', 'f0e1d2c3b4a59687'), + ('fedcba9876543210', '15750e7a4f4ec577', 'f0e1d2c3b4a5968778'), + ('fedcba9876543210', '122ba70b3ab64ae0', 'f0e1d2c3b4a596877869'), + ('fedcba9876543210', '3a833c9affc537f6', 'f0e1d2c3b4a5968778695a'), + ('fedcba9876543210', '9409da87a90f6bf2', 'f0e1d2c3b4a5968778695a4b'), + ('fedcba9876543210', '884f80625060b8b4', 'f0e1d2c3b4a5968778695a4b3c'), + ('fedcba9876543210', '1f85031c19e11968', 'f0e1d2c3b4a5968778695a4b3c2d'), + ('fedcba9876543210', '79d9373a714ca34f', 'f0e1d2c3b4a5968778695a4b3c2d1e'), + ('fedcba9876543210', '93142887ee3be15c', + 'f0e1d2c3b4a5968778695a4b3c2d1e0f'), + ('fedcba9876543210', '03429e838ce2d14b', + 'f0e1d2c3b4a5968778695a4b3c2d1e0f00'), + ('fedcba9876543210', 'a4299e27469ff67b', + 'f0e1d2c3b4a5968778695a4b3c2d1e0f0011'), + ('fedcba9876543210', 'afd5aed1c1bc96a8', + 'f0e1d2c3b4a5968778695a4b3c2d1e0f001122'), + ('fedcba9876543210', '10851c0e3858da9f', + 'f0e1d2c3b4a5968778695a4b3c2d1e0f00112233'), + ('fedcba9876543210', 'e6f51ed79b9db21f', + 'f0e1d2c3b4a5968778695a4b3c2d1e0f0011223344'), + ('fedcba9876543210', '64a6e14afd36b46f', + 'f0e1d2c3b4a5968778695a4b3c2d1e0f001122334455'), + ('fedcba9876543210', '80c7d7d45a5479ad', + 'f0e1d2c3b4a5968778695a4b3c2d1e0f00112233445566'), + ('fedcba9876543210', '05044b62fa52d080', + 'f0e1d2c3b4a5968778695a4b3c2d1e0f0011223344556677'), +] + +def get_tests(config={}): + from Crypto.Cipher import Blowfish + from common import make_block_tests + return make_block_tests(Blowfish, "Blowfish", test_data) + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_CAST.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_CAST.py new file mode 100644 index 0000000..1cfcec0 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_CAST.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Cipher/CAST.py: Self-test for the CAST-128 (CAST5) cipher +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for Crypto.Cipher.CAST""" + +__revision__ = "$Id$" + +from Crypto.Util.py3compat import * + +# This is a list of (plaintext, ciphertext, key) tuples. +test_data = [ + # Test vectors from RFC 2144, B.1 + ('0123456789abcdef', '238b4fe5847e44b2', + '0123456712345678234567893456789a', + '128-bit key'), + + ('0123456789abcdef', 'eb6a711a2c02271b', + '01234567123456782345', + '80-bit key'), + + ('0123456789abcdef', '7ac816d16e9b302e', + '0123456712', + '40-bit key'), +] + +def get_tests(config={}): + from Crypto.Cipher import CAST + from common import make_block_tests + return make_block_tests(CAST, "CAST", test_data) + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_DES.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_DES.py new file mode 100644 index 0000000..c5d114b --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_DES.py @@ -0,0 +1,339 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Cipher/DES.py: Self-test for the (Single) DES cipher +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for Crypto.Cipher.DES""" + +__revision__ = "$Id$" + +from common import dict # For compatibility with Python 2.1 and 2.2 +from Crypto.Util.py3compat import * +import unittest + +# This is a list of (plaintext, ciphertext, key, description) tuples. +SP800_17_B1_KEY = '01' * 8 +SP800_17_B2_PT = '00' * 8 +test_data = [ + # Test vectors from Appendix A of NIST SP 800-17 + # "Modes of Operation Validation System (MOVS): Requirements and Procedures" + # http://csrc.nist.gov/publications/nistpubs/800-17/800-17.pdf + + # Appendix A - "Sample Round Outputs for the DES" + ('0000000000000000', '82dcbafbdeab6602', '10316e028c8f3b4a', + "NIST SP800-17 A"), + + # Table B.1 - Variable Plaintext Known Answer Test + ('8000000000000000', '95f8a5e5dd31d900', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #0'), + ('4000000000000000', 'dd7f121ca5015619', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #1'), + ('2000000000000000', '2e8653104f3834ea', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #2'), + ('1000000000000000', '4bd388ff6cd81d4f', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #3'), + ('0800000000000000', '20b9e767b2fb1456', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #4'), + ('0400000000000000', '55579380d77138ef', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #5'), + ('0200000000000000', '6cc5defaaf04512f', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #6'), + ('0100000000000000', '0d9f279ba5d87260', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #7'), + ('0080000000000000', 'd9031b0271bd5a0a', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #8'), + ('0040000000000000', '424250b37c3dd951', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #9'), + ('0020000000000000', 'b8061b7ecd9a21e5', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #10'), + ('0010000000000000', 'f15d0f286b65bd28', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #11'), + ('0008000000000000', 'add0cc8d6e5deba1', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #12'), + ('0004000000000000', 'e6d5f82752ad63d1', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #13'), + ('0002000000000000', 'ecbfe3bd3f591a5e', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #14'), + ('0001000000000000', 'f356834379d165cd', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #15'), + ('0000800000000000', '2b9f982f20037fa9', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #16'), + ('0000400000000000', '889de068a16f0be6', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #17'), + ('0000200000000000', 'e19e275d846a1298', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #18'), + ('0000100000000000', '329a8ed523d71aec', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #19'), + ('0000080000000000', 'e7fce22557d23c97', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #20'), + ('0000040000000000', '12a9f5817ff2d65d', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #21'), + ('0000020000000000', 'a484c3ad38dc9c19', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #22'), + ('0000010000000000', 'fbe00a8a1ef8ad72', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #23'), + ('0000008000000000', '750d079407521363', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #24'), + ('0000004000000000', '64feed9c724c2faf', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #25'), + ('0000002000000000', 'f02b263b328e2b60', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #26'), + ('0000001000000000', '9d64555a9a10b852', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #27'), + ('0000000800000000', 'd106ff0bed5255d7', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #28'), + ('0000000400000000', 'e1652c6b138c64a5', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #29'), + ('0000000200000000', 'e428581186ec8f46', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #30'), + ('0000000100000000', 'aeb5f5ede22d1a36', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #31'), + ('0000000080000000', 'e943d7568aec0c5c', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #32'), + ('0000000040000000', 'df98c8276f54b04b', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #33'), + ('0000000020000000', 'b160e4680f6c696f', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #34'), + ('0000000010000000', 'fa0752b07d9c4ab8', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #35'), + ('0000000008000000', 'ca3a2b036dbc8502', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #36'), + ('0000000004000000', '5e0905517bb59bcf', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #37'), + ('0000000002000000', '814eeb3b91d90726', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #38'), + ('0000000001000000', '4d49db1532919c9f', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #39'), + ('0000000000800000', '25eb5fc3f8cf0621', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #40'), + ('0000000000400000', 'ab6a20c0620d1c6f', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #41'), + ('0000000000200000', '79e90dbc98f92cca', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #42'), + ('0000000000100000', '866ecedd8072bb0e', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #43'), + ('0000000000080000', '8b54536f2f3e64a8', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #44'), + ('0000000000040000', 'ea51d3975595b86b', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #45'), + ('0000000000020000', 'caffc6ac4542de31', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #46'), + ('0000000000010000', '8dd45a2ddf90796c', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #47'), + ('0000000000008000', '1029d55e880ec2d0', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #48'), + ('0000000000004000', '5d86cb23639dbea9', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #49'), + ('0000000000002000', '1d1ca853ae7c0c5f', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #50'), + ('0000000000001000', 'ce332329248f3228', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #51'), + ('0000000000000800', '8405d1abe24fb942', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #52'), + ('0000000000000400', 'e643d78090ca4207', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #53'), + ('0000000000000200', '48221b9937748a23', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #54'), + ('0000000000000100', 'dd7c0bbd61fafd54', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #55'), + ('0000000000000080', '2fbc291a570db5c4', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #56'), + ('0000000000000040', 'e07c30d7e4e26e12', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #57'), + ('0000000000000020', '0953e2258e8e90a1', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #58'), + ('0000000000000010', '5b711bc4ceebf2ee', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #59'), + ('0000000000000008', 'cc083f1e6d9e85f6', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #60'), + ('0000000000000004', 'd2fd8867d50d2dfe', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #61'), + ('0000000000000002', '06e7ea22ce92708f', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #62'), + ('0000000000000001', '166b40b44aba4bd6', SP800_17_B1_KEY, + 'NIST SP800-17 B.1 #63'), + + # Table B.2 - Variable Key Known Answer Test + (SP800_17_B2_PT, '95a8d72813daa94d', '8001010101010101', + 'NIST SP800-17 B.2 #0'), + (SP800_17_B2_PT, '0eec1487dd8c26d5', '4001010101010101', + 'NIST SP800-17 B.2 #1'), + (SP800_17_B2_PT, '7ad16ffb79c45926', '2001010101010101', + 'NIST SP800-17 B.2 #2'), + (SP800_17_B2_PT, 'd3746294ca6a6cf3', '1001010101010101', + 'NIST SP800-17 B.2 #3'), + (SP800_17_B2_PT, '809f5f873c1fd761', '0801010101010101', + 'NIST SP800-17 B.2 #4'), + (SP800_17_B2_PT, 'c02faffec989d1fc', '0401010101010101', + 'NIST SP800-17 B.2 #5'), + (SP800_17_B2_PT, '4615aa1d33e72f10', '0201010101010101', + 'NIST SP800-17 B.2 #6'), + (SP800_17_B2_PT, '2055123350c00858', '0180010101010101', + 'NIST SP800-17 B.2 #7'), + (SP800_17_B2_PT, 'df3b99d6577397c8', '0140010101010101', + 'NIST SP800-17 B.2 #8'), + (SP800_17_B2_PT, '31fe17369b5288c9', '0120010101010101', + 'NIST SP800-17 B.2 #9'), + (SP800_17_B2_PT, 'dfdd3cc64dae1642', '0110010101010101', + 'NIST SP800-17 B.2 #10'), + (SP800_17_B2_PT, '178c83ce2b399d94', '0108010101010101', + 'NIST SP800-17 B.2 #11'), + (SP800_17_B2_PT, '50f636324a9b7f80', '0104010101010101', + 'NIST SP800-17 B.2 #12'), + (SP800_17_B2_PT, 'a8468ee3bc18f06d', '0102010101010101', + 'NIST SP800-17 B.2 #13'), + (SP800_17_B2_PT, 'a2dc9e92fd3cde92', '0101800101010101', + 'NIST SP800-17 B.2 #14'), + (SP800_17_B2_PT, 'cac09f797d031287', '0101400101010101', + 'NIST SP800-17 B.2 #15'), + (SP800_17_B2_PT, '90ba680b22aeb525', '0101200101010101', + 'NIST SP800-17 B.2 #16'), + (SP800_17_B2_PT, 'ce7a24f350e280b6', '0101100101010101', + 'NIST SP800-17 B.2 #17'), + (SP800_17_B2_PT, '882bff0aa01a0b87', '0101080101010101', + 'NIST SP800-17 B.2 #18'), + (SP800_17_B2_PT, '25610288924511c2', '0101040101010101', + 'NIST SP800-17 B.2 #19'), + (SP800_17_B2_PT, 'c71516c29c75d170', '0101020101010101', + 'NIST SP800-17 B.2 #20'), + (SP800_17_B2_PT, '5199c29a52c9f059', '0101018001010101', + 'NIST SP800-17 B.2 #21'), + (SP800_17_B2_PT, 'c22f0a294a71f29f', '0101014001010101', + 'NIST SP800-17 B.2 #22'), + (SP800_17_B2_PT, 'ee371483714c02ea', '0101012001010101', + 'NIST SP800-17 B.2 #23'), + (SP800_17_B2_PT, 'a81fbd448f9e522f', '0101011001010101', + 'NIST SP800-17 B.2 #24'), + (SP800_17_B2_PT, '4f644c92e192dfed', '0101010801010101', + 'NIST SP800-17 B.2 #25'), + (SP800_17_B2_PT, '1afa9a66a6df92ae', '0101010401010101', + 'NIST SP800-17 B.2 #26'), + (SP800_17_B2_PT, 'b3c1cc715cb879d8', '0101010201010101', + 'NIST SP800-17 B.2 #27'), + (SP800_17_B2_PT, '19d032e64ab0bd8b', '0101010180010101', + 'NIST SP800-17 B.2 #28'), + (SP800_17_B2_PT, '3cfaa7a7dc8720dc', '0101010140010101', + 'NIST SP800-17 B.2 #29'), + (SP800_17_B2_PT, 'b7265f7f447ac6f3', '0101010120010101', + 'NIST SP800-17 B.2 #30'), + (SP800_17_B2_PT, '9db73b3c0d163f54', '0101010110010101', + 'NIST SP800-17 B.2 #31'), + (SP800_17_B2_PT, '8181b65babf4a975', '0101010108010101', + 'NIST SP800-17 B.2 #32'), + (SP800_17_B2_PT, '93c9b64042eaa240', '0101010104010101', + 'NIST SP800-17 B.2 #33'), + (SP800_17_B2_PT, '5570530829705592', '0101010102010101', + 'NIST SP800-17 B.2 #34'), + (SP800_17_B2_PT, '8638809e878787a0', '0101010101800101', + 'NIST SP800-17 B.2 #35'), + (SP800_17_B2_PT, '41b9a79af79ac208', '0101010101400101', + 'NIST SP800-17 B.2 #36'), + (SP800_17_B2_PT, '7a9be42f2009a892', '0101010101200101', + 'NIST SP800-17 B.2 #37'), + (SP800_17_B2_PT, '29038d56ba6d2745', '0101010101100101', + 'NIST SP800-17 B.2 #38'), + (SP800_17_B2_PT, '5495c6abf1e5df51', '0101010101080101', + 'NIST SP800-17 B.2 #39'), + (SP800_17_B2_PT, 'ae13dbd561488933', '0101010101040101', + 'NIST SP800-17 B.2 #40'), + (SP800_17_B2_PT, '024d1ffa8904e389', '0101010101020101', + 'NIST SP800-17 B.2 #41'), + (SP800_17_B2_PT, 'd1399712f99bf02e', '0101010101018001', + 'NIST SP800-17 B.2 #42'), + (SP800_17_B2_PT, '14c1d7c1cffec79e', '0101010101014001', + 'NIST SP800-17 B.2 #43'), + (SP800_17_B2_PT, '1de5279dae3bed6f', '0101010101012001', + 'NIST SP800-17 B.2 #44'), + (SP800_17_B2_PT, 'e941a33f85501303', '0101010101011001', + 'NIST SP800-17 B.2 #45'), + (SP800_17_B2_PT, 'da99dbbc9a03f379', '0101010101010801', + 'NIST SP800-17 B.2 #46'), + (SP800_17_B2_PT, 'b7fc92f91d8e92e9', '0101010101010401', + 'NIST SP800-17 B.2 #47'), + (SP800_17_B2_PT, 'ae8e5caa3ca04e85', '0101010101010201', + 'NIST SP800-17 B.2 #48'), + (SP800_17_B2_PT, '9cc62df43b6eed74', '0101010101010180', + 'NIST SP800-17 B.2 #49'), + (SP800_17_B2_PT, 'd863dbb5c59a91a0', '0101010101010140', + 'NIST SP800-17 B.2 #50'), + (SP800_17_B2_PT, 'a1ab2190545b91d7', '0101010101010120', + 'NIST SP800-17 B.2 #51'), + (SP800_17_B2_PT, '0875041e64c570f7', '0101010101010110', + 'NIST SP800-17 B.2 #52'), + (SP800_17_B2_PT, '5a594528bebef1cc', '0101010101010108', + 'NIST SP800-17 B.2 #53'), + (SP800_17_B2_PT, 'fcdb3291de21f0c0', '0101010101010104', + 'NIST SP800-17 B.2 #54'), + (SP800_17_B2_PT, '869efd7f9f265a09', '0101010101010102', + 'NIST SP800-17 B.2 #55'), +] + +class RonRivestTest(unittest.TestCase): + """ Ronald L. Rivest's DES test, see + http://people.csail.mit.edu/rivest/Destest.txt + ABSTRACT + -------- + + We present a simple way to test the correctness of a DES implementation: + Use the recurrence relation: + + X0 = 9474B8E8C73BCA7D (hexadecimal) + + X(i+1) = IF (i is even) THEN E(Xi,Xi) ELSE D(Xi,Xi) + + to compute a sequence of 64-bit values: X0, X1, X2, ..., X16. Here + E(X,K) denotes the DES encryption of X using key K, and D(X,K) denotes + the DES decryption of X using key K. If you obtain + + X16 = 1B1A2DDB4C642438 + + your implementation does not have any of the 36,568 possible single-fault + errors described herein. + """ + def runTest(self): + from Crypto.Cipher import DES + from binascii import b2a_hex + + X = [] + X[0:] = [b('\x94\x74\xB8\xE8\xC7\x3B\xCA\x7D')] + + for i in range(16): + c = DES.new(X[i],DES.MODE_ECB) + if not (i&1): # (num&1) returns 1 for odd numbers + X[i+1:] = [c.encrypt(X[i])] # even + else: + X[i+1:] = [c.decrypt(X[i])] # odd + + self.assertEqual(b2a_hex(X[16]), + b2a_hex(b('\x1B\x1A\x2D\xDB\x4C\x64\x24\x38'))) + +def get_tests(config={}): + from Crypto.Cipher import DES + from common import make_block_tests + return make_block_tests(DES, "DES", test_data) + [RonRivestTest()] + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_DES3.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_DES3.py new file mode 100644 index 0000000..50e969b --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_DES3.py @@ -0,0 +1,333 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Cipher/DES3.py: Self-test for the Triple-DES cipher +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for Crypto.Cipher.DES3""" + +__revision__ = "$Id$" + +from common import dict # For compatibility with Python 2.1 and 2.2 +from Crypto.Util.py3compat import * +from binascii import hexlify + +# This is a list of (plaintext, ciphertext, key, description) tuples. +SP800_20_A1_KEY = '01' * 24 +SP800_20_A2_PT = '00' * 8 +test_data = [ + # Test vector from Appendix B of NIST SP 800-67 + # "Recommendation for the Triple Data Encryption Algorithm (TDEA) Block + # Cipher" + # http://csrc.nist.gov/publications/nistpubs/800-67/SP800-67.pdf + ('54686520717566636b2062726f776e20666f78206a756d70', + 'a826fd8ce53b855fcce21c8112256fe668d5c05dd9b6b900', + '0123456789abcdef23456789abcdef01456789abcdef0123', + 'NIST SP800-67 B.1'), + + # Test vectors "The Multi-block Message Test (MMT) for DES and TDES" + # http://csrc.nist.gov/groups/STM/cavp/documents/des/DESMMT.pdf + ('326a494cd33fe756', 'b22b8d66de970692', + '627f460e08104a1043cd265d5840eaf1313edf97df2a8a8c', + 'DESMMT #1', dict(mode='CBC', iv='8e29f75ea77e5475')), + + ('84401f78fe6c10876d8ea23094ea5309', '7b1f7c7e3b1c948ebd04a75ffba7d2f5', + '37ae5ebf46dff2dc0754b94f31cbb3855e7fd36dc870bfae', + 'DESMMT #2', dict(mode='CBC', iv='3d1de3cc132e3b65')), + + # Test vectors from Appendix A of NIST SP 800-20 + # "Modes of Operation Validation System for the Triple Data Encryption + # Algorithm (TMOVS): Requirements and Procedures" + # http://csrc.nist.gov/publications/nistpubs/800-20/800-20.pdf + + # Table A.1 - Variable Plaintext Known Answer Test + ('8000000000000000', '95f8a5e5dd31d900', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #0'), + ('4000000000000000', 'dd7f121ca5015619', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #1'), + ('2000000000000000', '2e8653104f3834ea', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #2'), + ('1000000000000000', '4bd388ff6cd81d4f', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #3'), + ('0800000000000000', '20b9e767b2fb1456', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #4'), + ('0400000000000000', '55579380d77138ef', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #5'), + ('0200000000000000', '6cc5defaaf04512f', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #6'), + ('0100000000000000', '0d9f279ba5d87260', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #7'), + ('0080000000000000', 'd9031b0271bd5a0a', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #8'), + ('0040000000000000', '424250b37c3dd951', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #9'), + ('0020000000000000', 'b8061b7ecd9a21e5', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #10'), + ('0010000000000000', 'f15d0f286b65bd28', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #11'), + ('0008000000000000', 'add0cc8d6e5deba1', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #12'), + ('0004000000000000', 'e6d5f82752ad63d1', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #13'), + ('0002000000000000', 'ecbfe3bd3f591a5e', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #14'), + ('0001000000000000', 'f356834379d165cd', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #15'), + ('0000800000000000', '2b9f982f20037fa9', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #16'), + ('0000400000000000', '889de068a16f0be6', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #17'), + ('0000200000000000', 'e19e275d846a1298', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #18'), + ('0000100000000000', '329a8ed523d71aec', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #19'), + ('0000080000000000', 'e7fce22557d23c97', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #20'), + ('0000040000000000', '12a9f5817ff2d65d', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #21'), + ('0000020000000000', 'a484c3ad38dc9c19', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #22'), + ('0000010000000000', 'fbe00a8a1ef8ad72', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #23'), + ('0000008000000000', '750d079407521363', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #24'), + ('0000004000000000', '64feed9c724c2faf', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #25'), + ('0000002000000000', 'f02b263b328e2b60', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #26'), + ('0000001000000000', '9d64555a9a10b852', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #27'), + ('0000000800000000', 'd106ff0bed5255d7', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #28'), + ('0000000400000000', 'e1652c6b138c64a5', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #29'), + ('0000000200000000', 'e428581186ec8f46', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #30'), + ('0000000100000000', 'aeb5f5ede22d1a36', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #31'), + ('0000000080000000', 'e943d7568aec0c5c', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #32'), + ('0000000040000000', 'df98c8276f54b04b', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #33'), + ('0000000020000000', 'b160e4680f6c696f', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #34'), + ('0000000010000000', 'fa0752b07d9c4ab8', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #35'), + ('0000000008000000', 'ca3a2b036dbc8502', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #36'), + ('0000000004000000', '5e0905517bb59bcf', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #37'), + ('0000000002000000', '814eeb3b91d90726', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #38'), + ('0000000001000000', '4d49db1532919c9f', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #39'), + ('0000000000800000', '25eb5fc3f8cf0621', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #40'), + ('0000000000400000', 'ab6a20c0620d1c6f', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #41'), + ('0000000000200000', '79e90dbc98f92cca', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #42'), + ('0000000000100000', '866ecedd8072bb0e', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #43'), + ('0000000000080000', '8b54536f2f3e64a8', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #44'), + ('0000000000040000', 'ea51d3975595b86b', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #45'), + ('0000000000020000', 'caffc6ac4542de31', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #46'), + ('0000000000010000', '8dd45a2ddf90796c', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #47'), + ('0000000000008000', '1029d55e880ec2d0', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #48'), + ('0000000000004000', '5d86cb23639dbea9', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #49'), + ('0000000000002000', '1d1ca853ae7c0c5f', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #50'), + ('0000000000001000', 'ce332329248f3228', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #51'), + ('0000000000000800', '8405d1abe24fb942', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #52'), + ('0000000000000400', 'e643d78090ca4207', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #53'), + ('0000000000000200', '48221b9937748a23', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #54'), + ('0000000000000100', 'dd7c0bbd61fafd54', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #55'), + ('0000000000000080', '2fbc291a570db5c4', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #56'), + ('0000000000000040', 'e07c30d7e4e26e12', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #57'), + ('0000000000000020', '0953e2258e8e90a1', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #58'), + ('0000000000000010', '5b711bc4ceebf2ee', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #59'), + ('0000000000000008', 'cc083f1e6d9e85f6', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #60'), + ('0000000000000004', 'd2fd8867d50d2dfe', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #61'), + ('0000000000000002', '06e7ea22ce92708f', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #62'), + ('0000000000000001', '166b40b44aba4bd6', SP800_20_A1_KEY, + 'NIST SP800-20 A.1 #63'), + + # Table A.2 - Variable Key Known Answer Test + (SP800_20_A2_PT, '95a8d72813daa94d', '8001010101010101'*3, + 'NIST SP800-20 A.2 #0'), + (SP800_20_A2_PT, '0eec1487dd8c26d5', '4001010101010101'*3, + 'NIST SP800-20 A.2 #1'), + (SP800_20_A2_PT, '7ad16ffb79c45926', '2001010101010101'*3, + 'NIST SP800-20 A.2 #2'), + (SP800_20_A2_PT, 'd3746294ca6a6cf3', '1001010101010101'*3, + 'NIST SP800-20 A.2 #3'), + (SP800_20_A2_PT, '809f5f873c1fd761', '0801010101010101'*3, + 'NIST SP800-20 A.2 #4'), + (SP800_20_A2_PT, 'c02faffec989d1fc', '0401010101010101'*3, + 'NIST SP800-20 A.2 #5'), + (SP800_20_A2_PT, '4615aa1d33e72f10', '0201010101010101'*3, + 'NIST SP800-20 A.2 #6'), + (SP800_20_A2_PT, '2055123350c00858', '0180010101010101'*3, + 'NIST SP800-20 A.2 #7'), + (SP800_20_A2_PT, 'df3b99d6577397c8', '0140010101010101'*3, + 'NIST SP800-20 A.2 #8'), + (SP800_20_A2_PT, '31fe17369b5288c9', '0120010101010101'*3, + 'NIST SP800-20 A.2 #9'), + (SP800_20_A2_PT, 'dfdd3cc64dae1642', '0110010101010101'*3, + 'NIST SP800-20 A.2 #10'), + (SP800_20_A2_PT, '178c83ce2b399d94', '0108010101010101'*3, + 'NIST SP800-20 A.2 #11'), + (SP800_20_A2_PT, '50f636324a9b7f80', '0104010101010101'*3, + 'NIST SP800-20 A.2 #12'), + (SP800_20_A2_PT, 'a8468ee3bc18f06d', '0102010101010101'*3, + 'NIST SP800-20 A.2 #13'), + (SP800_20_A2_PT, 'a2dc9e92fd3cde92', '0101800101010101'*3, + 'NIST SP800-20 A.2 #14'), + (SP800_20_A2_PT, 'cac09f797d031287', '0101400101010101'*3, + 'NIST SP800-20 A.2 #15'), + (SP800_20_A2_PT, '90ba680b22aeb525', '0101200101010101'*3, + 'NIST SP800-20 A.2 #16'), + (SP800_20_A2_PT, 'ce7a24f350e280b6', '0101100101010101'*3, + 'NIST SP800-20 A.2 #17'), + (SP800_20_A2_PT, '882bff0aa01a0b87', '0101080101010101'*3, + 'NIST SP800-20 A.2 #18'), + (SP800_20_A2_PT, '25610288924511c2', '0101040101010101'*3, + 'NIST SP800-20 A.2 #19'), + (SP800_20_A2_PT, 'c71516c29c75d170', '0101020101010101'*3, + 'NIST SP800-20 A.2 #20'), + (SP800_20_A2_PT, '5199c29a52c9f059', '0101018001010101'*3, + 'NIST SP800-20 A.2 #21'), + (SP800_20_A2_PT, 'c22f0a294a71f29f', '0101014001010101'*3, + 'NIST SP800-20 A.2 #22'), + (SP800_20_A2_PT, 'ee371483714c02ea', '0101012001010101'*3, + 'NIST SP800-20 A.2 #23'), + (SP800_20_A2_PT, 'a81fbd448f9e522f', '0101011001010101'*3, + 'NIST SP800-20 A.2 #24'), + (SP800_20_A2_PT, '4f644c92e192dfed', '0101010801010101'*3, + 'NIST SP800-20 A.2 #25'), + (SP800_20_A2_PT, '1afa9a66a6df92ae', '0101010401010101'*3, + 'NIST SP800-20 A.2 #26'), + (SP800_20_A2_PT, 'b3c1cc715cb879d8', '0101010201010101'*3, + 'NIST SP800-20 A.2 #27'), + (SP800_20_A2_PT, '19d032e64ab0bd8b', '0101010180010101'*3, + 'NIST SP800-20 A.2 #28'), + (SP800_20_A2_PT, '3cfaa7a7dc8720dc', '0101010140010101'*3, + 'NIST SP800-20 A.2 #29'), + (SP800_20_A2_PT, 'b7265f7f447ac6f3', '0101010120010101'*3, + 'NIST SP800-20 A.2 #30'), + (SP800_20_A2_PT, '9db73b3c0d163f54', '0101010110010101'*3, + 'NIST SP800-20 A.2 #31'), + (SP800_20_A2_PT, '8181b65babf4a975', '0101010108010101'*3, + 'NIST SP800-20 A.2 #32'), + (SP800_20_A2_PT, '93c9b64042eaa240', '0101010104010101'*3, + 'NIST SP800-20 A.2 #33'), + (SP800_20_A2_PT, '5570530829705592', '0101010102010101'*3, + 'NIST SP800-20 A.2 #34'), + (SP800_20_A2_PT, '8638809e878787a0', '0101010101800101'*3, + 'NIST SP800-20 A.2 #35'), + (SP800_20_A2_PT, '41b9a79af79ac208', '0101010101400101'*3, + 'NIST SP800-20 A.2 #36'), + (SP800_20_A2_PT, '7a9be42f2009a892', '0101010101200101'*3, + 'NIST SP800-20 A.2 #37'), + (SP800_20_A2_PT, '29038d56ba6d2745', '0101010101100101'*3, + 'NIST SP800-20 A.2 #38'), + (SP800_20_A2_PT, '5495c6abf1e5df51', '0101010101080101'*3, + 'NIST SP800-20 A.2 #39'), + (SP800_20_A2_PT, 'ae13dbd561488933', '0101010101040101'*3, + 'NIST SP800-20 A.2 #40'), + (SP800_20_A2_PT, '024d1ffa8904e389', '0101010101020101'*3, + 'NIST SP800-20 A.2 #41'), + (SP800_20_A2_PT, 'd1399712f99bf02e', '0101010101018001'*3, + 'NIST SP800-20 A.2 #42'), + (SP800_20_A2_PT, '14c1d7c1cffec79e', '0101010101014001'*3, + 'NIST SP800-20 A.2 #43'), + (SP800_20_A2_PT, '1de5279dae3bed6f', '0101010101012001'*3, + 'NIST SP800-20 A.2 #44'), + (SP800_20_A2_PT, 'e941a33f85501303', '0101010101011001'*3, + 'NIST SP800-20 A.2 #45'), + (SP800_20_A2_PT, 'da99dbbc9a03f379', '0101010101010801'*3, + 'NIST SP800-20 A.2 #46'), + (SP800_20_A2_PT, 'b7fc92f91d8e92e9', '0101010101010401'*3, + 'NIST SP800-20 A.2 #47'), + (SP800_20_A2_PT, 'ae8e5caa3ca04e85', '0101010101010201'*3, + 'NIST SP800-20 A.2 #48'), + (SP800_20_A2_PT, '9cc62df43b6eed74', '0101010101010180'*3, + 'NIST SP800-20 A.2 #49'), + (SP800_20_A2_PT, 'd863dbb5c59a91a0', '0101010101010140'*3, + 'NIST SP800-20 A.2 #50'), + (SP800_20_A2_PT, 'a1ab2190545b91d7', '0101010101010120'*3, + 'NIST SP800-20 A.2 #51'), + (SP800_20_A2_PT, '0875041e64c570f7', '0101010101010110'*3, + 'NIST SP800-20 A.2 #52'), + (SP800_20_A2_PT, '5a594528bebef1cc', '0101010101010108'*3, + 'NIST SP800-20 A.2 #53'), + (SP800_20_A2_PT, 'fcdb3291de21f0c0', '0101010101010104'*3, + 'NIST SP800-20 A.2 #54'), + (SP800_20_A2_PT, '869efd7f9f265a09', '0101010101010102'*3, + 'NIST SP800-20 A.2 #55'), + + # "Two-key 3DES". Test vector generated using PyCrypto 2.0.1. + # This test is designed to test the DES3 API, not the correctness of the + # output. + ('21e81b7ade88a259', '5c577d4d9b20c0f8', + '9b397ebf81b1181e282f4bb8adbadc6b', 'Two-key 3DES'), + + # The following test vectors have been generated with gpg v1.4.0. + # The command line used was: + # gpg -c -z 0 --cipher-algo 3DES --passphrase secret_passphrase \ + # --disable-mdc --s2k-mode 0 --output ct pt + # For an explanation, see test_AES.py . + ( 'ac1762037074324fb53ba3596f73656d69746556616c6c6579', # Plaintext, 'YosemiteValley' + '9979238528357b90e2e0be549cb0b2d5999b9a4a447e5c5c7d', # Ciphertext + '7ade65b460f5ea9be35f9e14aa883a2048e3824aa616c0b2', # Key (hash of 'BearsAhead') + 'GPG Test Vector #1', + dict(mode='OPENPGP', iv='cd47e2afb8b7e4b0', encrypted_iv='6a7eef0b58050e8b904a' ) ), +] + +def get_tests(config={}): + from Crypto.Cipher import DES3 + from common import make_block_tests + return make_block_tests(DES3, "DES3", test_data) + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_XOR.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_XOR.py new file mode 100644 index 0000000..a4d542a --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_XOR.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Cipher/XOR.py: Self-test for the XOR "cipher" +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for Crypto.Cipher.XOR""" + +import unittest + +__revision__ = "$Id$" + +from Crypto.Util.py3compat import * + +# This is a list of (plaintext, ciphertext, key) tuples. +test_data = [ + # Test vectors written from scratch. (Nobody posts XOR test vectors on the web? How disappointing.) + ('01', '01', + '00', + 'zero key'), + + ('0102040810204080', '0003050911214181', + '01', + '1-byte key'), + + ('0102040810204080', 'cda8c8a2dc8a8c2a', + 'ccaa', + '2-byte key'), + + ('ff'*64, 'fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0'*2, + '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f', + '32-byte key'), +] + +class TruncationSelfTest(unittest.TestCase): + + def runTest(self): + """33-byte key (should raise ValueError under current implementation)""" + # Crypto.Cipher.XOR previously truncated its inputs at 32 bytes. Now + # it should raise a ValueError if the length is too long. + self.assertRaises(ValueError, XOR.new, "x"*33) + +def get_tests(config={}): + global XOR + from Crypto.Cipher import XOR + from common import make_stream_tests + return make_stream_tests(XOR, "XOR", test_data) + [TruncationSelfTest()] + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_pkcs1_15.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_pkcs1_15.py new file mode 100644 index 0000000..7aa1703 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_pkcs1_15.py @@ -0,0 +1,174 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Cipher/test_pkcs1_15.py: Self-test for PKCS#1 v1.5 encryption +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +__revision__ = "$Id$" + +import unittest +import sys + +from Crypto.PublicKey import RSA +from Crypto.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex +from Crypto import Random +from Crypto.Cipher import PKCS1_v1_5 as PKCS +from Crypto.Util.py3compat import * + +def rws(t): + """Remove white spaces, tabs, and new lines from a string""" + for c in ['\n', '\t', ' ']: + t = t.replace(c,'') + return t + +def t2b(t): + """Convert a text string with bytes in hex form to a byte string""" + clean = b(rws(t)) + if len(clean)%2 == 1: + print clean + raise ValueError("Even number of characters expected") + return a2b_hex(clean) + +class PKCS1_15_Tests(unittest.TestCase): + + def setUp(self): + self.rng = Random.new().read + self.key1024 = RSA.generate(1024, self.rng) + + # List of tuples with test data for PKCS#1 v1.5. + # Each tuple is made up by: + # Item #0: dictionary with RSA key component, or key to import + # Item #1: plaintext + # Item #2: ciphertext + # Item #3: random data + + _testData = ( + + # + # Generated with openssl 0.9.8o + # + ( + # Private key + '''-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQDAiAnvIAOvqVwJTaYzsKnefZftgtXGE2hPJppGsWl78yz9jeXY +W/FxX/gTPURArNhdnhP6n3p2ZaDIBrO2zizbgIXs0IsljTTcr4vnI8fMXzyNUOjA +zP3nzMqZDZK6757XQAobOssMkBFqRWwilT/3DsBhRpl3iMUhF+wvpTSHewIDAQAB +AoGAC4HV/inOrpgTvSab8Wj0riyZgQOZ3U3ZpSlsfR8ra9Ib9Uee3jCYnKscu6Gk +y6zI/cdt8EPJ4PuwAWSNJzbpbVaDvUq25OD+CX8/uRT08yBS4J8TzBitZJTD4lS7 +atdTnKT0Wmwk+u8tDbhvMKwnUHdJLcuIsycts9rwJVapUtkCQQDvDpx2JMun0YKG +uUttjmL8oJ3U0m3ZvMdVwBecA0eebZb1l2J5PvI3EJD97eKe91Nsw8T3lwpoN40k +IocSVDklAkEAzi1HLHE6EzVPOe5+Y0kGvrIYRRhncOb72vCvBZvD6wLZpQgqo6c4 +d3XHFBBQWA6xcvQb5w+VVEJZzw64y25sHwJBAMYReRl6SzL0qA0wIYrYWrOt8JeQ +8mthulcWHXmqTgC6FEXP9Es5GD7/fuKl4wqLKZgIbH4nqvvGay7xXLCXD/ECQH9a +1JYNMtRen5unSAbIOxRcKkWz92F0LKpm9ZW/S9vFHO+mBcClMGoKJHiuQxLBsLbT +NtEZfSJZAeS2sUtn3/0CQDb2M2zNBTF8LlM0nxmh0k9VGm5TVIyBEMcipmvOgqIs +HKukWBcq9f/UOmS0oEhai/6g+Uf7VHJdWaeO5LzuvwU= +-----END RSA PRIVATE KEY-----''', + # Plaintext + '''THIS IS PLAINTEXT\x0A''', + # Ciphertext + '''3f dc fd 3c cd 5c 9b 12 af 65 32 e3 f7 d0 da 36 + 8f 8f d9 e3 13 1c 7f c8 b3 f9 c1 08 e4 eb 79 9c + 91 89 1f 96 3b 94 77 61 99 a4 b1 ee 5d e6 17 c9 + 5d 0a b5 63 52 0a eb 00 45 38 2a fb b0 71 3d 11 + f7 a1 9e a7 69 b3 af 61 c0 bb 04 5b 5d 4b 27 44 + 1f 5b 97 89 ba 6a 08 95 ee 4f a2 eb 56 64 e5 0f + da 7c f9 9a 61 61 06 62 ed a0 bc 5f aa 6c 31 78 + 70 28 1a bb 98 3c e3 6a 60 3c d1 0b 0f 5a f4 75''', + # Random data + '''eb d7 7d 86 a4 35 23 a3 54 7e 02 0b 42 1d + 61 6c af 67 b8 4e 17 56 80 66 36 04 64 34 26 8a + 47 dd 44 b3 1a b2 17 60 f4 91 2e e2 b5 95 64 cc + f9 da c8 70 94 54 86 4c ef 5b 08 7d 18 c4 ab 8d + 04 06 33 8f ca 15 5f 52 60 8a a1 0c f5 08 b5 4c + bb 99 b8 94 25 04 9c e6 01 75 e6 f9 63 7a 65 61 + 13 8a a7 47 77 81 ae 0d b8 2c 4d 50 a5''' + ), + ) + + def testEncrypt1(self): + for test in self._testData: + # Build the key + key = RSA.importKey(test[0]) + # RNG that takes its random numbers from a pool given + # at initialization + class randGen: + def __init__(self, data): + self.data = data + self.idx = 0 + def __call__(self, N): + r = self.data[self.idx:N] + self.idx += N + return r + # The real test + key._randfunc = randGen(t2b(test[3])) + cipher = PKCS.new(key) + ct = cipher.encrypt(b(test[1])) + self.assertEqual(ct, t2b(test[2])) + + def testEncrypt2(self): + # Verify that encryption fail if plaintext is too long + pt = '\x00'*(128-11+1) + cipher = PKCS.new(self.key1024) + self.assertRaises(ValueError, cipher.encrypt, pt) + + def testVerify1(self): + for test in self._testData: + # Build the key + key = RSA.importKey(test[0]) + # The real test + cipher = PKCS.new(key) + pt = cipher.decrypt(t2b(test[2]), "---") + self.assertEqual(pt, b(test[1])) + + def testVerify2(self): + # Verify that decryption fails if ciphertext is not as long as + # RSA modulus + cipher = PKCS.new(self.key1024) + self.assertRaises(ValueError, cipher.decrypt, '\x00'*127, "---") + self.assertRaises(ValueError, cipher.decrypt, '\x00'*129, "---") + + # Verify that decryption fails if there are less then 8 non-zero padding + # bytes + pt = b('\x00\x02' + '\xFF'*7 + '\x00' + '\x45'*118) + ct = self.key1024.encrypt(pt, 0)[0] + ct = b('\x00'*(128-len(ct))) + ct + self.assertEqual("---", cipher.decrypt(ct, "---")) + + def testEncryptVerify1(self): + # Encrypt/Verify messages of length [0..RSAlen-11] + # and therefore padding [8..117] + for pt_len in xrange(0,128-11+1): + pt = self.rng(pt_len) + cipher = PKCS.new(self.key1024) + ct = cipher.encrypt(pt) + pt2 = cipher.decrypt(ct, "---") + self.assertEqual(pt,pt2) + + +def get_tests(config={}): + tests = [] + tests += list_test_cases(PKCS1_15_Tests) + return tests + +if __name__ == '__main__': + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_pkcs1_oaep.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_pkcs1_oaep.py new file mode 100644 index 0000000..accca61 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Cipher/test_pkcs1_oaep.py @@ -0,0 +1,372 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Cipher/test_pkcs1_oaep.py: Self-test for PKCS#1 OAEP encryption +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +from __future__ import nested_scopes + +__revision__ = "$Id$" + +import unittest + +from Crypto.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex + +from Crypto.Util.py3compat import * +from Crypto.PublicKey import RSA +from Crypto.Cipher import PKCS1_OAEP as PKCS +from Crypto.Hash import MD2,MD5,SHA as SHA1,SHA256,RIPEMD +from Crypto import Random + +def rws(t): + """Remove white spaces, tabs, and new lines from a string""" + for c in ['\n', '\t', ' ']: + t = t.replace(c,'') + return t + +def t2b(t): + """Convert a text string with bytes in hex form to a byte string""" + clean = rws(t) + if len(clean)%2 == 1: + raise ValueError("Even number of characters expected") + return a2b_hex(clean) + +class PKCS1_OAEP_Tests(unittest.TestCase): + + def setUp(self): + self.rng = Random.new().read + self.key1024 = RSA.generate(1024, self.rng) + + # List of tuples with test data for PKCS#1 OAEP + # Each tuple is made up by: + # Item #0: dictionary with RSA key component + # Item #1: plaintext + # Item #2: ciphertext + # Item #3: random data (=seed) + # Item #4: hash object + + _testData = ( + + # + # From in oaep-int.txt to be found in + # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip + # + ( + # Private key + { + 'n':'''bb f8 2f 09 06 82 ce 9c 23 38 ac 2b 9d a8 71 f7 + 36 8d 07 ee d4 10 43 a4 40 d6 b6 f0 74 54 f5 1f + b8 df ba af 03 5c 02 ab 61 ea 48 ce eb 6f cd 48 + 76 ed 52 0d 60 e1 ec 46 19 71 9d 8a 5b 8b 80 7f + af b8 e0 a3 df c7 37 72 3e e6 b4 b7 d9 3a 25 84 + ee 6a 64 9d 06 09 53 74 88 34 b2 45 45 98 39 4e + e0 aa b1 2d 7b 61 a5 1f 52 7a 9a 41 f6 c1 68 7f + e2 53 72 98 ca 2a 8f 59 46 f8 e5 fd 09 1d bd cb''', + # Public key + 'e':'11', + # In the test vector, only p and q were given... + # d is computed offline as e^{-1} mod (p-1)(q-1) + 'd':'''a5dafc5341faf289c4b988db30c1cdf83f31251e0 + 668b42784813801579641b29410b3c7998d6bc465745e5c3 + 92669d6870da2c082a939e37fdcb82ec93edac97ff3ad595 + 0accfbc111c76f1a9529444e56aaf68c56c092cd38dc3bef + 5d20a939926ed4f74a13eddfbe1a1cecc4894af9428c2b7b + 8883fe4463a4bc85b1cb3c1''' + } + , + # Plaintext + '''d4 36 e9 95 69 fd 32 a7 c8 a0 5b bc 90 d3 2c 49''', + # Ciphertext + '''12 53 e0 4d c0 a5 39 7b b4 4a 7a b8 7e 9b f2 a0 + 39 a3 3d 1e 99 6f c8 2a 94 cc d3 00 74 c9 5d f7 + 63 72 20 17 06 9e 52 68 da 5d 1c 0b 4f 87 2c f6 + 53 c1 1d f8 23 14 a6 79 68 df ea e2 8d ef 04 bb + 6d 84 b1 c3 1d 65 4a 19 70 e5 78 3b d6 eb 96 a0 + 24 c2 ca 2f 4a 90 fe 9f 2e f5 c9 c1 40 e5 bb 48 + da 95 36 ad 87 00 c8 4f c9 13 0a de a7 4e 55 8d + 51 a7 4d df 85 d8 b5 0d e9 68 38 d6 06 3e 09 55''', + # Random + '''aa fd 12 f6 59 ca e6 34 89 b4 79 e5 07 6d de c2 + f0 6c b5 8f''', + # Hash + SHA1, + ), + + # + # From in oaep-vect.txt to be found in Example 1.1 + # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip + # + ( + # Private key + { + 'n':'''a8 b3 b2 84 af 8e b5 0b 38 70 34 a8 60 f1 46 c4 + 91 9f 31 87 63 cd 6c 55 98 c8 ae 48 11 a1 e0 ab + c4 c7 e0 b0 82 d6 93 a5 e7 fc ed 67 5c f4 66 85 + 12 77 2c 0c bc 64 a7 42 c6 c6 30 f5 33 c8 cc 72 + f6 2a e8 33 c4 0b f2 58 42 e9 84 bb 78 bd bf 97 + c0 10 7d 55 bd b6 62 f5 c4 e0 fa b9 84 5c b5 14 + 8e f7 39 2d d3 aa ff 93 ae 1e 6b 66 7b b3 d4 24 + 76 16 d4 f5 ba 10 d4 cf d2 26 de 88 d3 9f 16 fb''', + 'e':'''01 00 01''', + 'd':'''53 33 9c fd b7 9f c8 46 6a 65 5c 73 16 ac a8 5c + 55 fd 8f 6d d8 98 fd af 11 95 17 ef 4f 52 e8 fd + 8e 25 8d f9 3f ee 18 0f a0 e4 ab 29 69 3c d8 3b + 15 2a 55 3d 4a c4 d1 81 2b 8b 9f a5 af 0e 7f 55 + fe 73 04 df 41 57 09 26 f3 31 1f 15 c4 d6 5a 73 + 2c 48 31 16 ee 3d 3d 2d 0a f3 54 9a d9 bf 7c bf + b7 8a d8 84 f8 4d 5b eb 04 72 4d c7 36 9b 31 de + f3 7d 0c f5 39 e9 cf cd d3 de 65 37 29 ea d5 d1 ''' + } + , + # Plaintext + '''66 28 19 4e 12 07 3d b0 3b a9 4c da 9e f9 53 23 + 97 d5 0d ba 79 b9 87 00 4a fe fe 34''', + # Ciphertext + '''35 4f e6 7b 4a 12 6d 5d 35 fe 36 c7 77 79 1a 3f + 7b a1 3d ef 48 4e 2d 39 08 af f7 22 fa d4 68 fb + 21 69 6d e9 5d 0b e9 11 c2 d3 17 4f 8a fc c2 01 + 03 5f 7b 6d 8e 69 40 2d e5 45 16 18 c2 1a 53 5f + a9 d7 bf c5 b8 dd 9f c2 43 f8 cf 92 7d b3 13 22 + d6 e8 81 ea a9 1a 99 61 70 e6 57 a0 5a 26 64 26 + d9 8c 88 00 3f 84 77 c1 22 70 94 a0 d9 fa 1e 8c + 40 24 30 9c e1 ec cc b5 21 00 35 d4 7a c7 2e 8a''', + # Random + '''18 b7 76 ea 21 06 9d 69 77 6a 33 e9 6b ad 48 e1 + dd a0 a5 ef''', + SHA1 + ), + + # + # From in oaep-vect.txt to be found in Example 2.1 + # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip + # + ( + # Private key + { + 'n':'''01 94 7c 7f ce 90 42 5f 47 27 9e 70 85 1f 25 d5 + e6 23 16 fe 8a 1d f1 93 71 e3 e6 28 e2 60 54 3e + 49 01 ef 60 81 f6 8c 0b 81 41 19 0d 2a e8 da ba + 7d 12 50 ec 6d b6 36 e9 44 ec 37 22 87 7c 7c 1d + 0a 67 f1 4b 16 94 c5 f0 37 94 51 a4 3e 49 a3 2d + de 83 67 0b 73 da 91 a1 c9 9b c2 3b 43 6a 60 05 + 5c 61 0f 0b af 99 c1 a0 79 56 5b 95 a3 f1 52 66 + 32 d1 d4 da 60 f2 0e da 25 e6 53 c4 f0 02 76 6f + 45''', + 'e':'''01 00 01''', + 'd':'''08 23 f2 0f ad b5 da 89 08 8a 9d 00 89 3e 21 fa + 4a 1b 11 fb c9 3c 64 a3 be 0b aa ea 97 fb 3b 93 + c3 ff 71 37 04 c1 9c 96 3c 1d 10 7a ae 99 05 47 + 39 f7 9e 02 e1 86 de 86 f8 7a 6d de fe a6 d8 cc + d1 d3 c8 1a 47 bf a7 25 5b e2 06 01 a4 a4 b2 f0 + 8a 16 7b 5e 27 9d 71 5b 1b 45 5b dd 7e ab 24 59 + 41 d9 76 8b 9a ce fb 3c cd a5 95 2d a3 ce e7 25 + 25 b4 50 16 63 a8 ee 15 c9 e9 92 d9 24 62 fe 39''' + }, + # Plaintext + '''8f f0 0c aa 60 5c 70 28 30 63 4d 9a 6c 3d 42 c6 + 52 b5 8c f1 d9 2f ec 57 0b ee e7''', + # Ciphertext + '''01 81 af 89 22 b9 fc b4 d7 9d 92 eb e1 98 15 99 + 2f c0 c1 43 9d 8b cd 49 13 98 a0 f4 ad 3a 32 9a + 5b d9 38 55 60 db 53 26 83 c8 b7 da 04 e4 b1 2a + ed 6a ac df 47 1c 34 c9 cd a8 91 ad dc c2 df 34 + 56 65 3a a6 38 2e 9a e5 9b 54 45 52 57 eb 09 9d + 56 2b be 10 45 3f 2b 6d 13 c5 9c 02 e1 0f 1f 8a + bb 5d a0 d0 57 09 32 da cf 2d 09 01 db 72 9d 0f + ef cc 05 4e 70 96 8e a5 40 c8 1b 04 bc ae fe 72 + 0e''', + # Random + '''8c 40 7b 5e c2 89 9e 50 99 c5 3e 8c e7 93 bf 94 + e7 1b 17 82''', + SHA1 + ), + + # + # From in oaep-vect.txt to be found in Example 10.1 + # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip + # + ( + # Private key + { + 'n':'''ae 45 ed 56 01 ce c6 b8 cc 05 f8 03 93 5c 67 4d + db e0 d7 5c 4c 09 fd 79 51 fc 6b 0c ae c3 13 a8 + df 39 97 0c 51 8b ff ba 5e d6 8f 3f 0d 7f 22 a4 + 02 9d 41 3f 1a e0 7e 4e be 9e 41 77 ce 23 e7 f5 + 40 4b 56 9e 4e e1 bd cf 3c 1f b0 3e f1 13 80 2d + 4f 85 5e b9 b5 13 4b 5a 7c 80 85 ad ca e6 fa 2f + a1 41 7e c3 76 3b e1 71 b0 c6 2b 76 0e de 23 c1 + 2a d9 2b 98 08 84 c6 41 f5 a8 fa c2 6b da d4 a0 + 33 81 a2 2f e1 b7 54 88 50 94 c8 25 06 d4 01 9a + 53 5a 28 6a fe b2 71 bb 9b a5 92 de 18 dc f6 00 + c2 ae ea e5 6e 02 f7 cf 79 fc 14 cf 3b dc 7c d8 + 4f eb bb f9 50 ca 90 30 4b 22 19 a7 aa 06 3a ef + a2 c3 c1 98 0e 56 0c d6 4a fe 77 95 85 b6 10 76 + 57 b9 57 85 7e fd e6 01 09 88 ab 7d e4 17 fc 88 + d8 f3 84 c4 e6 e7 2c 3f 94 3e 0c 31 c0 c4 a5 cc + 36 f8 79 d8 a3 ac 9d 7d 59 86 0e aa da 6b 83 bb''', + 'e':'''01 00 01''', + 'd':'''05 6b 04 21 6f e5 f3 54 ac 77 25 0a 4b 6b 0c 85 + 25 a8 5c 59 b0 bd 80 c5 64 50 a2 2d 5f 43 8e 59 + 6a 33 3a a8 75 e2 91 dd 43 f4 8c b8 8b 9d 5f c0 + d4 99 f9 fc d1 c3 97 f9 af c0 70 cd 9e 39 8c 8d + 19 e6 1d b7 c7 41 0a 6b 26 75 df bf 5d 34 5b 80 + 4d 20 1a dd 50 2d 5c e2 df cb 09 1c e9 99 7b be + be 57 30 6f 38 3e 4d 58 81 03 f0 36 f7 e8 5d 19 + 34 d1 52 a3 23 e4 a8 db 45 1d 6f 4a 5b 1b 0f 10 + 2c c1 50 e0 2f ee e2 b8 8d ea 4a d4 c1 ba cc b2 + 4d 84 07 2d 14 e1 d2 4a 67 71 f7 40 8e e3 05 64 + fb 86 d4 39 3a 34 bc f0 b7 88 50 1d 19 33 03 f1 + 3a 22 84 b0 01 f0 f6 49 ea f7 93 28 d4 ac 5c 43 + 0a b4 41 49 20 a9 46 0e d1 b7 bc 40 ec 65 3e 87 + 6d 09 ab c5 09 ae 45 b5 25 19 01 16 a0 c2 61 01 + 84 82 98 50 9c 1c 3b f3 a4 83 e7 27 40 54 e1 5e + 97 07 50 36 e9 89 f6 09 32 80 7b 52 57 75 1e 79''' + }, + # Plaintext + '''8b ba 6b f8 2a 6c 0f 86 d5 f1 75 6e 97 95 68 70 + b0 89 53 b0 6b 4e b2 05 bc 16 94 ee''', + # Ciphertext + '''53 ea 5d c0 8c d2 60 fb 3b 85 85 67 28 7f a9 15 + 52 c3 0b 2f eb fb a2 13 f0 ae 87 70 2d 06 8d 19 + ba b0 7f e5 74 52 3d fb 42 13 9d 68 c3 c5 af ee + e0 bf e4 cb 79 69 cb f3 82 b8 04 d6 e6 13 96 14 + 4e 2d 0e 60 74 1f 89 93 c3 01 4b 58 b9 b1 95 7a + 8b ab cd 23 af 85 4f 4c 35 6f b1 66 2a a7 2b fc + c7 e5 86 55 9d c4 28 0d 16 0c 12 67 85 a7 23 eb + ee be ff 71 f1 15 94 44 0a ae f8 7d 10 79 3a 87 + 74 a2 39 d4 a0 4c 87 fe 14 67 b9 da f8 52 08 ec + 6c 72 55 79 4a 96 cc 29 14 2f 9a 8b d4 18 e3 c1 + fd 67 34 4b 0c d0 82 9d f3 b2 be c6 02 53 19 62 + 93 c6 b3 4d 3f 75 d3 2f 21 3d d4 5c 62 73 d5 05 + ad f4 cc ed 10 57 cb 75 8f c2 6a ee fa 44 12 55 + ed 4e 64 c1 99 ee 07 5e 7f 16 64 61 82 fd b4 64 + 73 9b 68 ab 5d af f0 e6 3e 95 52 01 68 24 f0 54 + bf 4d 3c 8c 90 a9 7b b6 b6 55 32 84 eb 42 9f cc''', + # Random + '''47 e1 ab 71 19 fe e5 6c 95 ee 5e aa d8 6f 40 d0 + aa 63 bd 33''', + SHA1 + ), + ) + + def testEncrypt1(self): + # Verify encryption using all test vectors + for test in self._testData: + # Build the key + comps = [ long(rws(test[0][x]),16) for x in ('n','e') ] + key = RSA.construct(comps) + # RNG that takes its random numbers from a pool given + # at initialization + class randGen: + def __init__(self, data): + self.data = data + self.idx = 0 + def __call__(self, N): + r = self.data[self.idx:N] + self.idx += N + return r + # The real test + key._randfunc = randGen(t2b(test[3])) + cipher = PKCS.new(key, test[4]) + ct = cipher.encrypt(t2b(test[1])) + self.assertEqual(ct, t2b(test[2])) + + def testEncrypt2(self): + # Verify that encryption fails if plaintext is too long + pt = '\x00'*(128-2*20-2+1) + cipher = PKCS.new(self.key1024) + self.assertRaises(ValueError, cipher.encrypt, pt) + + def testDecrypt1(self): + # Verify decryption using all test vectors + for test in self._testData: + # Build the key + comps = [ long(rws(test[0][x]),16) for x in ('n','e','d') ] + key = RSA.construct(comps) + # The real test + cipher = PKCS.new(key, test[4]) + pt = cipher.decrypt(t2b(test[2])) + self.assertEqual(pt, t2b(test[1])) + + def testDecrypt2(self): + # Simplest possible negative tests + for ct_size in (127,128,129): + cipher = PKCS.new(self.key1024) + self.assertRaises(ValueError, cipher.decrypt, bchr(0x00)*ct_size) + + def testEncryptDecrypt1(self): + # Encrypt/Decrypt messages of length [0..128-2*20-2] + for pt_len in xrange(0,128-2*20-2): + pt = self.rng(pt_len) + ct = PKCS.encrypt(pt, self.key1024) + pt2 = PKCS.decrypt(ct, self.key1024) + self.assertEqual(pt,pt2) + + def testEncryptDecrypt1(self): + # Helper function to monitor what's requested from RNG + global asked + def localRng(N): + global asked + asked += N + return self.rng(N) + # Verify that OAEP is friendly to all hashes + for hashmod in (MD2,MD5,SHA1,SHA256,RIPEMD): + # Verify that encrypt() asks for as many random bytes + # as the hash output size + asked = 0 + pt = self.rng(40) + self.key1024._randfunc = localRng + cipher = PKCS.new(self.key1024, hashmod) + ct = cipher.encrypt(pt) + self.assertEqual(cipher.decrypt(ct), pt) + self.failUnless(asked > hashmod.digest_size) + + def testEncryptDecrypt2(self): + # Verify that OAEP supports labels + pt = self.rng(35) + xlabel = self.rng(22) + cipher = PKCS.new(self.key1024, label=xlabel) + ct = cipher.encrypt(pt) + self.assertEqual(cipher.decrypt(ct), pt) + + def testEncryptDecrypt3(self): + # Verify that encrypt() uses the custom MGF + global mgfcalls + # Helper function to monitor what's requested from MGF + def newMGF(seed,maskLen): + global mgfcalls + mgfcalls += 1 + return bchr(0x00)*maskLen + mgfcalls = 0 + pt = self.rng(32) + cipher = PKCS.new(self.key1024, mgfunc=newMGF) + ct = cipher.encrypt(pt) + self.assertEqual(mgfcalls, 2) + self.assertEqual(cipher.decrypt(ct), pt) + +def get_tests(config={}): + tests = [] + tests += list_test_cases(PKCS1_OAEP_Tests) + return tests + +if __name__ == '__main__': + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/__init__.py new file mode 100644 index 0000000..bb19f9b --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/__init__.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Hash/__init__.py: Self-test for hash modules +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test for hash modules""" + +__revision__ = "$Id$" + +def get_tests(config={}): + tests = [] + from Crypto.SelfTest.Hash import test_HMAC; tests += test_HMAC.get_tests(config=config) + from Crypto.SelfTest.Hash import test_MD2; tests += test_MD2.get_tests(config=config) + from Crypto.SelfTest.Hash import test_MD4; tests += test_MD4.get_tests(config=config) + from Crypto.SelfTest.Hash import test_MD5; tests += test_MD5.get_tests(config=config) + from Crypto.SelfTest.Hash import test_RIPEMD; tests += test_RIPEMD.get_tests(config=config) + from Crypto.SelfTest.Hash import test_SHA; tests += test_SHA.get_tests(config=config) + from Crypto.SelfTest.Hash import test_SHA256; tests += test_SHA256.get_tests(config=config) + try: + from Crypto.SelfTest.Hash import test_SHA224; tests += test_SHA224.get_tests(config=config) + from Crypto.SelfTest.Hash import test_SHA384; tests += test_SHA384.get_tests(config=config) + from Crypto.SelfTest.Hash import test_SHA512; tests += test_SHA512.get_tests(config=config) + except ImportError: + import sys + sys.stderr.write("SelfTest: warning: not testing SHA224/SHA384/SHA512 modules (not available)\n") + return tests + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/common.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/common.py new file mode 100644 index 0000000..f77fb0f --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/common.py @@ -0,0 +1,197 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Hash/common.py: Common code for Crypto.SelfTest.Hash +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-testing for PyCrypto hash modules""" + +__revision__ = "$Id$" + +import sys +import unittest +import binascii +from Crypto.Util.py3compat import * + +# For compatibility with Python 2.1 and Python 2.2 +if sys.hexversion < 0x02030000: + # Python 2.1 doesn't have a dict() function + # Python 2.2 dict() function raises TypeError if you do dict(MD5='blah') + def dict(**kwargs): + return kwargs.copy() +else: + dict = dict + + +class HashDigestSizeSelfTest(unittest.TestCase): + + def __init__(self, hashmod, description, expected): + unittest.TestCase.__init__(self) + self.hashmod = hashmod + self.expected = expected + self.description = description + + def shortDescription(self): + return self.description + + def runTest(self): + self.failUnless(hasattr(self.hashmod, "digest_size")) + self.assertEquals(self.hashmod.digest_size, self.expected) + h = self.hashmod.new() + self.failUnless(hasattr(h, "digest_size")) + self.assertEquals(h.digest_size, self.expected) + + +class HashSelfTest(unittest.TestCase): + + def __init__(self, hashmod, description, expected, input): + unittest.TestCase.__init__(self) + self.hashmod = hashmod + self.expected = expected + self.input = input + self.description = description + + def shortDescription(self): + return self.description + + def runTest(self): + h = self.hashmod.new() + h.update(self.input) + + out1 = binascii.b2a_hex(h.digest()) + out2 = h.hexdigest() + + h = self.hashmod.new(self.input) + + out3 = h.hexdigest() + out4 = binascii.b2a_hex(h.digest()) + + # PY3K: hexdigest() should return str(), and digest() bytes + self.assertEqual(self.expected, out1) # h = .new(); h.update(data); h.digest() + if sys.version_info[0] == 2: + self.assertEqual(self.expected, out2) # h = .new(); h.update(data); h.hexdigest() + self.assertEqual(self.expected, out3) # h = .new(data); h.hexdigest() + else: + self.assertEqual(self.expected.decode(), out2) # h = .new(); h.update(data); h.hexdigest() + self.assertEqual(self.expected.decode(), out3) # h = .new(data); h.hexdigest() + self.assertEqual(self.expected, out4) # h = .new(data); h.digest() + + # Verify that new() object method produces a fresh hash object + h2 = h.new() + h2.update(self.input) + out5 = binascii.b2a_hex(h2.digest()) + self.assertEqual(self.expected, out5) + +class HashTestOID(unittest.TestCase): + def __init__(self, hashmod, oid): + unittest.TestCase.__init__(self) + self.hashmod = hashmod + self.oid = oid + + def runTest(self): + h = self.hashmod.new() + if self.oid==None: + try: + raised = 0 + a = h.oid + except AttributeError: + raised = 1 + self.assertEqual(raised,1) + else: + self.assertEqual(h.oid, self.oid) + +class MACSelfTest(unittest.TestCase): + + def __init__(self, hashmod, description, expected_dict, input, key, hashmods): + unittest.TestCase.__init__(self) + self.hashmod = hashmod + self.expected_dict = expected_dict + self.input = input + self.key = key + self.hashmods = hashmods + self.description = description + + def shortDescription(self): + return self.description + + def runTest(self): + for hashname in self.expected_dict.keys(): + hashmod = self.hashmods[hashname] + key = binascii.a2b_hex(b(self.key)) + data = binascii.a2b_hex(b(self.input)) + + # Strip whitespace from the expected string (which should be in lowercase-hex) + expected = b("".join(self.expected_dict[hashname].split())) + + h = self.hashmod.new(key, digestmod=hashmod) + h.update(data) + out1 = binascii.b2a_hex(h.digest()) + out2 = h.hexdigest() + + h = self.hashmod.new(key, data, hashmod) + + out3 = h.hexdigest() + out4 = binascii.b2a_hex(h.digest()) + + # Test .copy() + h2 = h.copy() + h.update(b("blah blah blah")) # Corrupt the original hash object + out5 = binascii.b2a_hex(h2.digest()) # The copied hash object should return the correct result + + # PY3K: hexdigest() should return str(), and digest() bytes + self.assertEqual(expected, out1) + if sys.version_info[0] == 2: + self.assertEqual(expected, out2) + self.assertEqual(expected, out3) + else: + self.assertEqual(expected.decode(), out2) + self.assertEqual(expected.decode(), out3) + self.assertEqual(expected, out4) + self.assertEqual(expected, out5) + +def make_hash_tests(module, module_name, test_data, digest_size, oid=None): + tests = [] + for i in range(len(test_data)): + row = test_data[i] + (expected, input) = map(b,row[0:2]) + if len(row) < 3: + description = repr(input) + else: + description = row[2].encode('latin-1') + name = "%s #%d: %s" % (module_name, i+1, description) + tests.append(HashSelfTest(module, name, expected, input)) + if oid is not None: + oid = b(oid) + name = "%s #%d: digest_size" % (module_name, i+1) + tests.append(HashDigestSizeSelfTest(module, name, digest_size)) + tests.append(HashTestOID(module, oid)) + return tests + +def make_mac_tests(module, module_name, test_data, hashmods): + tests = [] + for i in range(len(test_data)): + row = test_data[i] + (key, data, results, description) = row + name = "%s #%d: %s" % (module_name, i+1, description) + tests.append(MACSelfTest(module, name, results, data, key, hashmods)) + return tests + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_HMAC.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_HMAC.py new file mode 100644 index 0000000..c01c97b --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_HMAC.py @@ -0,0 +1,223 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Hash/HMAC.py: Self-test for the HMAC module +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for Crypto.Hash.HMAC""" + +__revision__ = "$Id$" + +from common import dict # For compatibility with Python 2.1 and 2.2 +from Crypto.Util.py3compat import * + +# This is a list of (key, data, results, description) tuples. +test_data = [ + ## Test vectors from RFC 2202 ## + # Test that the default hashmod is MD5 + ('0b' * 16, + '4869205468657265', + dict(default='9294727a3638bb1c13f48ef8158bfc9d'), + 'default-is-MD5'), + + # Test case 1 (MD5) + ('0b' * 16, + '4869205468657265', + dict(MD5='9294727a3638bb1c13f48ef8158bfc9d'), + 'RFC 2202 #1-MD5 (HMAC-MD5)'), + + # Test case 1 (SHA1) + ('0b' * 20, + '4869205468657265', + dict(SHA1='b617318655057264e28bc0b6fb378c8ef146be00'), + 'RFC 2202 #1-SHA1 (HMAC-SHA1)'), + + # Test case 2 + ('4a656665', + '7768617420646f2079612077616e7420666f72206e6f7468696e673f', + dict(MD5='750c783e6ab0b503eaa86e310a5db738', + SHA1='effcdf6ae5eb2fa2d27416d5f184df9c259a7c79'), + 'RFC 2202 #2 (HMAC-MD5/SHA1)'), + + # Test case 3 (MD5) + ('aa' * 16, + 'dd' * 50, + dict(MD5='56be34521d144c88dbb8c733f0e8b3f6'), + 'RFC 2202 #3-MD5 (HMAC-MD5)'), + + # Test case 3 (SHA1) + ('aa' * 20, + 'dd' * 50, + dict(SHA1='125d7342b9ac11cd91a39af48aa17b4f63f175d3'), + 'RFC 2202 #3-SHA1 (HMAC-SHA1)'), + + # Test case 4 + ('0102030405060708090a0b0c0d0e0f10111213141516171819', + 'cd' * 50, + dict(MD5='697eaf0aca3a3aea3a75164746ffaa79', + SHA1='4c9007f4026250c6bc8414f9bf50c86c2d7235da'), + 'RFC 2202 #4 (HMAC-MD5/SHA1)'), + + # Test case 5 (MD5) + ('0c' * 16, + '546573742057697468205472756e636174696f6e', + dict(MD5='56461ef2342edc00f9bab995690efd4c'), + 'RFC 2202 #5-MD5 (HMAC-MD5)'), + + # Test case 5 (SHA1) + # NB: We do not implement hash truncation, so we only test the full hash here. + ('0c' * 20, + '546573742057697468205472756e636174696f6e', + dict(SHA1='4c1a03424b55e07fe7f27be1d58bb9324a9a5a04'), + 'RFC 2202 #5-SHA1 (HMAC-SHA1)'), + + # Test case 6 + ('aa' * 80, + '54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a' + + '65204b6579202d2048617368204b6579204669727374', + dict(MD5='6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd', + SHA1='aa4ae5e15272d00e95705637ce8a3b55ed402112'), + 'RFC 2202 #6 (HMAC-MD5/SHA1)'), + + # Test case 7 + ('aa' * 80, + '54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a' + + '65204b657920616e64204c6172676572205468616e204f6e6520426c6f636b2d' + + '53697a652044617461', + dict(MD5='6f630fad67cda0ee1fb1f562db3aa53e', + SHA1='e8e99d0f45237d786d6bbaa7965c7808bbff1a91'), + 'RFC 2202 #7 (HMAC-MD5/SHA1)'), + + ## Test vectors from RFC 4231 ## + # 4.2. Test Case 1 + ('0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b', + '4869205468657265', + dict(SHA256=''' + b0344c61d8db38535ca8afceaf0bf12b + 881dc200c9833da726e9376c2e32cff7 + '''), + 'RFC 4231 #1 (HMAC-SHA256)'), + + # 4.3. Test Case 2 - Test with a key shorter than the length of the HMAC + # output. + ('4a656665', + '7768617420646f2079612077616e7420666f72206e6f7468696e673f', + dict(SHA256=''' + 5bdcc146bf60754e6a042426089575c7 + 5a003f089d2739839dec58b964ec3843 + '''), + 'RFC 4231 #2 (HMAC-SHA256)'), + + # 4.4. Test Case 3 - Test with a combined length of key and data that is + # larger than 64 bytes (= block-size of SHA-224 and SHA-256). + ('aa' * 20, + 'dd' * 50, + dict(SHA256=''' + 773ea91e36800e46854db8ebd09181a7 + 2959098b3ef8c122d9635514ced565fe + '''), + 'RFC 4231 #3 (HMAC-SHA256)'), + + # 4.5. Test Case 4 - Test with a combined length of key and data that is + # larger than 64 bytes (= block-size of SHA-224 and SHA-256). + ('0102030405060708090a0b0c0d0e0f10111213141516171819', + 'cd' * 50, + dict(SHA256=''' + 82558a389a443c0ea4cc819899f2083a + 85f0faa3e578f8077a2e3ff46729665b + '''), + 'RFC 4231 #4 (HMAC-SHA256)'), + + # 4.6. Test Case 5 - Test with a truncation of output to 128 bits. + # + # Not included because we do not implement hash truncation. + # + + # 4.7. Test Case 6 - Test with a key larger than 128 bytes (= block-size of + # SHA-384 and SHA-512). + ('aa' * 131, + '54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a' + + '65204b6579202d2048617368204b6579204669727374', + dict(SHA256=''' + 60e431591ee0b67f0d8a26aacbf5b77f + 8e0bc6213728c5140546040f0ee37f54 + '''), + 'RFC 4231 #6 (HMAC-SHA256)'), + + # 4.8. Test Case 7 - Test with a key and data that is larger than 128 bytes + # (= block-size of SHA-384 and SHA-512). + ('aa' * 131, + '5468697320697320612074657374207573696e672061206c6172676572207468' + + '616e20626c6f636b2d73697a65206b657920616e642061206c61726765722074' + + '68616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565' + + '647320746f20626520686173686564206265666f7265206265696e6720757365' + + '642062792074686520484d414320616c676f726974686d2e', + dict(SHA256=''' + 9b09ffa71b942fcb27635fbcd5b0e944 + bfdc63644f0713938a7f51535c3a35e2 + '''), + 'RFC 4231 #7 (HMAC-SHA256)'), +] + +hashlib_test_data = [ + # Test case 8 (SHA224) + ('4a656665', + '7768617420646f2079612077616e74' + + '20666f72206e6f7468696e673f', + dict(SHA224='a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44'), + 'RFC 4634 8.4 SHA224 (HMAC-SHA224)'), + + # Test case 9 (SHA384) + ('4a656665', + '7768617420646f2079612077616e74' + + '20666f72206e6f7468696e673f', + dict(SHA384='af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec3736322445e8e2240ca5e69e2c78b3239ecfab21649'), + 'RFC 4634 8.4 SHA384 (HMAC-SHA384)'), + + # Test case 10 (SHA512) + ('4a656665', + '7768617420646f2079612077616e74' + + '20666f72206e6f7468696e673f', + dict(SHA512='164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737'), + 'RFC 4634 8.4 SHA512 (HMAC-SHA512)'), + +] + +def get_tests(config={}): + global test_data + from Crypto.Hash import HMAC, MD5, SHA as SHA1, SHA256 + from common import make_mac_tests + hashmods = dict(MD5=MD5, SHA1=SHA1, SHA256=SHA256, default=None) + try: + from Crypto.Hash import SHA224, SHA384, SHA512 + hashmods.update(dict(SHA224=SHA224, SHA384=SHA384, SHA512=SHA512)) + test_data += hashlib_test_data + except ImportError: + import sys + sys.stderr.write("SelfTest: warning: not testing HMAC-SHA224/384/512 (not available)\n") + return make_mac_tests(HMAC, "HMAC", test_data, hashmods) + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_MD2.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_MD2.py new file mode 100644 index 0000000..db636d4 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_MD2.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Hash/MD2.py: Self-test for the MD2 hash function +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for Crypto.Hash.MD2""" + +__revision__ = "$Id$" + +from Crypto.Util.py3compat import * + +# This is a list of (expected_result, input[, description]) tuples. +test_data = [ + # Test vectors from RFC 1319 + ('8350e5a3e24c153df2275c9f80692773', '', "'' (empty string)"), + ('32ec01ec4a6dac72c0ab96fb34c0b5d1', 'a'), + ('da853b0d3f88d99b30283a69e6ded6bb', 'abc'), + ('ab4f496bfb2a530b219ff33031fe06b0', 'message digest'), + + ('4e8ddff3650292ab5a4108c3aa47940b', 'abcdefghijklmnopqrstuvwxyz', + 'a-z'), + + ('da33def2a42df13975352846c30338cd', + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', + 'A-Z, a-z, 0-9'), + + ('d5976f79d83d3a0dc9806c3c66f3efd8', + '1234567890123456789012345678901234567890123456' + + '7890123456789012345678901234567890', + "'1234567890' * 8"), +] + +def get_tests(config={}): + from Crypto.Hash import MD2 + from common import make_hash_tests + return make_hash_tests(MD2, "MD2", test_data, + digest_size=16, + oid="\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x02") + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_MD4.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_MD4.py new file mode 100644 index 0000000..1727bb6 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_MD4.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Hash/MD4.py: Self-test for the MD4 hash function +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for Crypto.Hash.MD4""" + +__revision__ = "$Id$" + +from Crypto.Util.py3compat import * + +# This is a list of (expected_result, input[, description]) tuples. +test_data = [ + # Test vectors from RFC 1320 + ('31d6cfe0d16ae931b73c59d7e0c089c0', '', "'' (empty string)"), + ('bde52cb31de33e46245e05fbdbd6fb24', 'a'), + ('a448017aaf21d8525fc10ae87aa6729d', 'abc'), + ('d9130a8164549fe818874806e1c7014b', 'message digest'), + + ('d79e1c308aa5bbcdeea8ed63df412da9', 'abcdefghijklmnopqrstuvwxyz', + 'a-z'), + + ('043f8582f241db351ce627e153e7f0e4', + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', + 'A-Z, a-z, 0-9'), + + ('e33b4ddc9c38f2199c3e7b164fcc0536', + '1234567890123456789012345678901234567890123456' + + '7890123456789012345678901234567890', + "'1234567890' * 8"), +] + +def get_tests(config={}): + from Crypto.Hash import MD4 + from common import make_hash_tests + return make_hash_tests(MD4, "MD4", test_data, + digest_size=16, + oid="\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x04") + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_MD5.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_MD5.py new file mode 100644 index 0000000..2e293fc --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_MD5.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Hash/MD5.py: Self-test for the MD5 hash function +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for Crypto.Hash.MD5""" + +__revision__ = "$Id$" + +from Crypto.Util.py3compat import * + +# This is a list of (expected_result, input[, description]) tuples. +test_data = [ + # Test vectors from RFC 1321 + ('d41d8cd98f00b204e9800998ecf8427e', '', "'' (empty string)"), + ('0cc175b9c0f1b6a831c399e269772661', 'a'), + ('900150983cd24fb0d6963f7d28e17f72', 'abc'), + ('f96b697d7cb7938d525a2f31aaf161d0', 'message digest'), + + ('c3fcd3d76192e4007dfb496cca67e13b', 'abcdefghijklmnopqrstuvwxyz', + 'a-z'), + + ('d174ab98d277d9f5a5611c2c9f419d9f', + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', + 'A-Z, a-z, 0-9'), + + ('57edf4a22be3c955ac49da2e2107b67a', + '1234567890123456789012345678901234567890123456' + + '7890123456789012345678901234567890', + "'1234567890' * 8"), +] + +def get_tests(config={}): + from Crypto.Hash import MD5 + from common import make_hash_tests + return make_hash_tests(MD5, "MD5", test_data, + digest_size=16, + oid="\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05") + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_RIPEMD.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_RIPEMD.py new file mode 100644 index 0000000..6673a93 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_RIPEMD.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Hash/test_RIPEMD.py: Self-test for the RIPEMD-160 hash function +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +#"""Self-test suite for Crypto.Hash.RIPEMD""" + +__revision__ = "$Id$" + +from Crypto.Util.py3compat import * + +# This is a list of (expected_result, input[, description]) tuples. +test_data = [ + # Test vectors downloaded 2008-09-12 from + # http://homes.esat.kuleuven.be/~bosselae/ripemd160.html + ('9c1185a5c5e9fc54612808977ee8f548b2258d31', '', "'' (empty string)"), + ('0bdc9d2d256b3ee9daae347be6f4dc835a467ffe', 'a'), + ('8eb208f7e05d987a9b044a8e98c6b087f15a0bfc', 'abc'), + ('5d0689ef49d2fae572b881b123a85ffa21595f36', 'message digest'), + + ('f71c27109c692c1b56bbdceb5b9d2865b3708dbc', + 'abcdefghijklmnopqrstuvwxyz', + 'a-z'), + + ('12a053384a9c0c88e405a06c27dcf49ada62eb2b', + 'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', + 'abcdbcd...pnopq'), + + ('b0e20b6e3116640286ed3a87a5713079b21f5189', + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', + 'A-Z, a-z, 0-9'), + + ('9b752e45573d4b39f4dbd3323cab82bf63326bfb', + '1234567890' * 8, + "'1234567890' * 8"), + + ('52783243c1697bdbe16d37f97f68f08325dc1528', + 'a' * 10**6, + '"a" * 10**6'), +] + +def get_tests(config={}): + from Crypto.Hash import RIPEMD + from common import make_hash_tests + return make_hash_tests(RIPEMD, "RIPEMD", test_data, + digest_size=20, + oid="\x06\x05\x2b\x24\x03\02\x01") + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_SHA.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_SHA.py new file mode 100644 index 0000000..7d72e77 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_SHA.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Hash/SHA.py: Self-test for the SHA-1 hash function +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for Crypto.Hash.SHA""" + +__revision__ = "$Id$" + +from Crypto.Util.py3compat import * + +# Test vectors from various sources +# This is a list of (expected_result, input[, description]) tuples. +test_data = [ + # FIPS PUB 180-2, A.1 - "One-Block Message" + ('a9993e364706816aba3e25717850c26c9cd0d89d', 'abc'), + + # FIPS PUB 180-2, A.2 - "Multi-Block Message" + ('84983e441c3bd26ebaae4aa1f95129e5e54670f1', + 'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'), + + # FIPS PUB 180-2, A.3 - "Long Message" +# ('34aa973cd4c4daa4f61eeb2bdbad27316534016f', +# 'a' * 10**6, +# '"a" * 10**6'), + + # RFC 3174: Section 7.3, "TEST4" (multiple of 512 bits) + ('dea356a2cddd90c7a7ecedc5ebb563934f460452', + '01234567' * 80, + '"01234567" * 80'), +] + +def get_tests(config={}): + from Crypto.Hash import SHA + from common import make_hash_tests + return make_hash_tests(SHA, "SHA", test_data, + digest_size=20, + oid="\x06\x05\x2B\x0E\x03\x02\x1A") + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_SHA224.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_SHA224.py new file mode 100644 index 0000000..a60f35a --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_SHA224.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Hash/test_SHA224.py: Self-test for the SHA-224 hash function +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for Crypto.Hash.SHA224""" + +__revision__ = "$Id$" + +# Test vectors from various sources +# This is a list of (expected_result, input[, description]) tuples. +test_data = [ + + # RFC 3874: Section 3.1, "Test Vector #1 + ('23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7', 'abc'), + + # RFC 3874: Section 3.2, "Test Vector #2 + ('75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525', 'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'), + + # RFC 3874: Section 3.3, "Test Vector #3 + ('20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67', 'a' * 10**6, "'a' * 10**6"), + + # Examples from http://de.wikipedia.org/wiki/Secure_Hash_Algorithm + ('d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f', ''), + + ('49b08defa65e644cbf8a2dd9270bdededabc741997d1dadd42026d7b', + 'Franz jagt im komplett verwahrlosten Taxi quer durch Bayern'), + + ('58911e7fccf2971a7d07f93162d8bd13568e71aa8fc86fc1fe9043d1', + 'Frank jagt im komplett verwahrlosten Taxi quer durch Bayern'), + +] + +def get_tests(config={}): + from Crypto.Hash import SHA224 + from common import make_hash_tests + return make_hash_tests(SHA224, "SHA224", test_data, + digest_size=28, + oid='\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04') + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_SHA256.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_SHA256.py new file mode 100644 index 0000000..4b45110 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_SHA256.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Hash/test_SHA256.py: Self-test for the SHA-256 hash function +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for Crypto.Hash.SHA256""" + +__revision__ = "$Id$" + +import unittest +from Crypto.Util.py3compat import * + +class LargeSHA256Test(unittest.TestCase): + def runTest(self): + """SHA256: 512/520 MiB test""" + from Crypto.Hash import SHA256 + zeros = bchr(0x00) * (1024*1024) + + h = SHA256.new(zeros) + for i in xrange(511): + h.update(zeros) + + # This test vector is from PyCrypto's old testdata.py file. + self.assertEqual('9acca8e8c22201155389f65abbf6bc9723edc7384ead80503839f49dcc56d767', h.hexdigest()) # 512 MiB + + for i in xrange(8): + h.update(zeros) + + # This test vector is from PyCrypto's old testdata.py file. + self.assertEqual('abf51ad954b246009dfe5a50ecd582fd5b8f1b8b27f30393853c3ef721e7fa6e', h.hexdigest()) # 520 MiB + +def get_tests(config={}): + # Test vectors from FIPS PUB 180-2 + # This is a list of (expected_result, input[, description]) tuples. + test_data = [ + # FIPS PUB 180-2, B.1 - "One-Block Message" + ('ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad', + 'abc'), + + # FIPS PUB 180-2, B.2 - "Multi-Block Message" + ('248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1', + 'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'), + + # FIPS PUB 180-2, B.3 - "Long Message" + ('cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0', + 'a' * 10**6, + '"a" * 10**6'), + + # Test for an old PyCrypto bug. + ('f7fd017a3c721ce7ff03f3552c0813adcc48b7f33f07e5e2ba71e23ea393d103', + 'This message is precisely 55 bytes long, to test a bug.', + 'Length = 55 (mod 64)'), + + # Example from http://de.wikipedia.org/wiki/Secure_Hash_Algorithm + ('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', ''), + + ('d32b568cd1b96d459e7291ebf4b25d007f275c9f13149beeb782fac0716613f8', + 'Franz jagt im komplett verwahrlosten Taxi quer durch Bayern'), + ] + + from Crypto.Hash import SHA256 + from common import make_hash_tests + tests = make_hash_tests(SHA256, "SHA256", test_data, + digest_size=32, + oid="\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01") + + if config.get('slow_tests'): + tests += [LargeSHA256Test()] + + return tests + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_SHA384.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_SHA384.py new file mode 100644 index 0000000..b7a72c0 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_SHA384.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Hash/test_SHA.py: Self-test for the SHA-384 hash function +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for Crypto.Hash.SHA384""" + +__revision__ = "$Id$" + +# Test vectors from various sources +# This is a list of (expected_result, input[, description]) tuples. +test_data = [ + + # RFC 4634: Section Page 8.4, "Test 1" + ('cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7', 'abc'), + + # RFC 4634: Section Page 8.4, "Test 2.2" + ('09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039', 'abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu'), + + # RFC 4634: Section Page 8.4, "Test 3" + ('9d0e1809716474cb086e834e310a4a1ced149e9c00f248527972cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985', 'a' * 10**6, "'a' * 10**6"), + + # Taken from http://de.wikipedia.org/wiki/Secure_Hash_Algorithm + ('38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b', ''), + + # Example from http://de.wikipedia.org/wiki/Secure_Hash_Algorithm + ('71e8383a4cea32d6fd6877495db2ee353542f46fa44bc23100bca48f3366b84e809f0708e81041f427c6d5219a286677', + 'Franz jagt im komplett verwahrlosten Taxi quer durch Bayern'), + +] + +def get_tests(config={}): + from Crypto.Hash import SHA384 + from common import make_hash_tests + return make_hash_tests(SHA384, "SHA384", test_data, + digest_size=48, + oid='\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02') + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_SHA512.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_SHA512.py new file mode 100644 index 0000000..cb86177 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Hash/test_SHA512.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Hash/test_SHA512.py: Self-test for the SHA-512 hash function +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for Crypto.Hash.SHA512""" + +__revision__ = "$Id$" + +# Test vectors from various sources +# This is a list of (expected_result, input[, description]) tuples. +test_data = [ + + # RFC 4634: Section Page 8.4, "Test 1" + ('ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f', 'abc'), + + # RFC 4634: Section Page 8.4, "Test 2.1" + ('8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909', 'abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu'), + + # RFC 4634: Section Page 8.4, "Test 3" + ('e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b', 'a' * 10**6, "'a' * 10**6"), + + # Taken from http://de.wikipedia.org/wiki/Secure_Hash_Algorithm + ('cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e', ''), + + ('af9ed2de700433b803240a552b41b5a472a6ef3fe1431a722b2063c75e9f07451f67a28e37d09cde769424c96aea6f8971389db9e1993d6c565c3c71b855723c', 'Franz jagt im komplett verwahrlosten Taxi quer durch Bayern'), +] + +def get_tests(config={}): + from Crypto.Hash import SHA512 + from common import make_hash_tests + return make_hash_tests(SHA512, "SHA512", test_data, + digest_size=64, + oid="\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03") + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Protocol/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Protocol/__init__.py new file mode 100644 index 0000000..a62c670 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Protocol/__init__.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Protocol/__init__.py: Self-tests for Crypto.Protocol +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test for Crypto.Protocol""" + +__revision__ = "$Id$" + +def get_tests(config={}): + tests = [] + from Crypto.SelfTest.Protocol import test_chaffing; tests += test_chaffing.get_tests(config=config) + from Crypto.SelfTest.Protocol import test_rfc1751; tests += test_rfc1751.get_tests(config=config) + from Crypto.SelfTest.Protocol import test_AllOrNothing; tests += test_AllOrNothing.get_tests(config=config) + return tests + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Protocol/test_AllOrNothing.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Protocol/test_AllOrNothing.py new file mode 100644 index 0000000..a211eab --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Protocol/test_AllOrNothing.py @@ -0,0 +1,76 @@ +# +# Test script for Crypto.Protocol.AllOrNothing +# +# Part of the Python Cryptography Toolkit +# +# Written by Andrew Kuchling and others +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +__revision__ = "$Id$" + +import unittest +from Crypto.Protocol import AllOrNothing +from Crypto.Util.py3compat import * + +text = b("""\ +When in the Course of human events, it becomes necessary for one people to +dissolve the political bands which have connected them with another, and to +assume among the powers of the earth, the separate and equal station to which +the Laws of Nature and of Nature's God entitle them, a decent respect to the +opinions of mankind requires that they should declare the causes which impel +them to the separation. + +We hold these truths to be self-evident, that all men are created equal, that +they are endowed by their Creator with certain unalienable Rights, that among +these are Life, Liberty, and the pursuit of Happiness. That to secure these +rights, Governments are instituted among Men, deriving their just powers from +the consent of the governed. That whenever any Form of Government becomes +destructive of these ends, it is the Right of the People to alter or to +abolish it, and to institute new Government, laying its foundation on such +principles and organizing its powers in such form, as to them shall seem most +likely to effect their Safety and Happiness. +""") + +class AllOrNothingTest (unittest.TestCase): + + def runTest(self): + "Simple test of AllOrNothing" + + from Crypto.Cipher import AES + import base64 + + # The current AllOrNothing will fail + # every so often. Repeat the test + # several times to force this. + for i in range(50): + x = AllOrNothing.AllOrNothing(AES) + + msgblocks = x.digest(text) + + # get a new undigest-only object so there's no leakage + y = AllOrNothing.AllOrNothing(AES) + text2 = y.undigest(msgblocks) + self.assertEqual(text, text2) + +def get_tests(config={}): + return [AllOrNothingTest()] + +if __name__ == "__main__": + unittest.main() diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Protocol/test_KDF.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Protocol/test_KDF.py new file mode 100644 index 0000000..119836b --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Protocol/test_KDF.py @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Protocol/test_KDF.py: Self-test for key derivation functions +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +__revision__ = "$Id$" + +import unittest +from binascii import unhexlify + +from Crypto.SelfTest.st_common import list_test_cases +from Crypto.Hash import SHA as SHA1,HMAC + +from Crypto.Protocol.KDF import * + +def t2b(t): return unhexlify(b(t)) + +class PBKDF1_Tests(unittest.TestCase): + + # List of tuples with test data. + # Each tuple is made up by: + # Item #0: a pass phrase + # Item #1: salt (8 bytes encoded in hex) + # Item #2: output key length + # Item #3: iterations to use + # Item #4: expected result (encoded in hex) + _testData = ( + # From http://www.di-mgt.com.au/cryptoKDFs.html#examplespbkdf + ("password","78578E5A5D63CB06",16,1000,"DC19847E05C64D2FAF10EBFB4A3D2A20"), + ) + + def test1(self): + v = self._testData[0] + res = PBKDF1(v[0], t2b(v[1]), v[2], v[3], SHA1) + self.assertEqual(res, t2b(v[4])) + +class PBKDF2_Tests(unittest.TestCase): + + # List of tuples with test data. + # Each tuple is made up by: + # Item #0: a pass phrase + # Item #1: salt (encoded in hex) + # Item #2: output key length + # Item #3: iterations to use + # Item #4: expected result (encoded in hex) + _testData = ( + # From http://www.di-mgt.com.au/cryptoKDFs.html#examplespbkdf + ("password","78578E5A5D63CB06",24,2048,"BFDE6BE94DF7E11DD409BCE20A0255EC327CB936FFE93643"), + # From RFC 6050 + ("password","73616c74", 20, 1, "0c60c80f961f0e71f3a9b524af6012062fe037a6"), + ("password","73616c74", 20, 2, "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957"), + ("password","73616c74", 20, 4096, "4b007901b765489abead49d926f721d065a429c1"), + ("passwordPASSWORDpassword","73616c7453414c5473616c7453414c5473616c7453414c5473616c7453414c5473616c74", + 25, 4096, "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038"), + ( 'pass\x00word',"7361006c74",16,4096, "56fa6aa75548099dcc37d7f03425e0c3"), + ) + + def test1(self): + # Test only for HMAC-SHA1 as PRF + + def prf(p,s): + return HMAC.new(p,s,SHA1).digest() + + for i in xrange(len(self._testData)): + v = self._testData[i] + res = PBKDF2(v[0], t2b(v[1]), v[2], v[3]) + res2 = PBKDF2(v[0], t2b(v[1]), v[2], v[3], prf) + self.assertEqual(res, t2b(v[4])) + self.assertEqual(res, res2) + +def get_tests(config={}): + tests = [] + tests += list_test_cases(PBKDF1_Tests) + tests += list_test_cases(PBKDF2_Tests) + return tests + +if __name__ == '__main__': + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Protocol/test_chaffing.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Protocol/test_chaffing.py new file mode 100644 index 0000000..5fa0120 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Protocol/test_chaffing.py @@ -0,0 +1,74 @@ +# +# Test script for Crypto.Protocol.Chaffing +# +# Part of the Python Cryptography Toolkit +# +# Written by Andrew Kuchling and others +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +__revision__ = "$Id$" + +import unittest +from Crypto.Protocol import Chaffing + +text = """\ +When in the Course of human events, it becomes necessary for one people to +dissolve the political bands which have connected them with another, and to +assume among the powers of the earth, the separate and equal station to which +the Laws of Nature and of Nature's God entitle them, a decent respect to the +opinions of mankind requires that they should declare the causes which impel +them to the separation. + +We hold these truths to be self-evident, that all men are created equal, that +they are endowed by their Creator with certain unalienable Rights, that among +these are Life, Liberty, and the pursuit of Happiness. That to secure these +rights, Governments are instituted among Men, deriving their just powers from +the consent of the governed. That whenever any Form of Government becomes +destructive of these ends, it is the Right of the People to alter or to +abolish it, and to institute new Government, laying its foundation on such +principles and organizing its powers in such form, as to them shall seem most +likely to effect their Safety and Happiness. +""" + +class ChaffingTest (unittest.TestCase): + + def runTest(self): + "Simple tests of chaffing and winnowing" + # Test constructors + Chaffing.Chaff() + Chaffing.Chaff(0.5, 1) + self.assertRaises(ValueError, Chaffing.Chaff, factor=-1) + self.assertRaises(ValueError, Chaffing.Chaff, blocksper=-1) + + data = [(1, 'data1', 'data1'), (2, 'data2', 'data2')] + c = Chaffing.Chaff(1.0, 1) + c.chaff(data) + chaff = c.chaff(data) + self.assertEqual(len(chaff), 4) + + c = Chaffing.Chaff(0.0, 1) + chaff = c.chaff(data) + self.assertEqual(len(chaff), 2) + +def get_tests(config={}): + return [ChaffingTest()] + +if __name__ == "__main__": + unittest.main() diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Protocol/test_rfc1751.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Protocol/test_rfc1751.py new file mode 100644 index 0000000..0878cc5 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Protocol/test_rfc1751.py @@ -0,0 +1,62 @@ +# +# Test script for Crypto.Util.RFC1751. +# +# Part of the Python Cryptography Toolkit +# +# Written by Andrew Kuchling and others +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +__revision__ = "$Id$" + +import binascii +import unittest +from Crypto.Util import RFC1751 +from Crypto.Util.py3compat import * + +test_data = [('EB33F77EE73D4053', 'TIDE ITCH SLOW REIN RULE MOT'), + ('CCAC2AED591056BE4F90FD441C534766', + 'RASH BUSH MILK LOOK BAD BRIM AVID GAFF BAIT ROT POD LOVE'), + ('EFF81F9BFBC65350920CDD7416DE8009', + 'TROD MUTE TAIL WARM CHAR KONG HAAG CITY BORE O TEAL AWL') + ] + +class RFC1751Test_k2e (unittest.TestCase): + + def runTest (self): + "Check converting keys to English" + for key, words in test_data: + key=binascii.a2b_hex(b(key)) + self.assertEqual(RFC1751.key_to_english(key), words) + +class RFC1751Test_e2k (unittest.TestCase): + + def runTest (self): + "Check converting English strings to keys" + for key, words in test_data: + key=binascii.a2b_hex(b(key)) + self.assertEqual(RFC1751.english_to_key(words), key) + +# class RFC1751Test + +def get_tests(config={}): + return [RFC1751Test_k2e(), RFC1751Test_e2k()] + +if __name__ == "__main__": + unittest.main() diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/PublicKey/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/PublicKey/__init__.py new file mode 100644 index 0000000..61ba53f --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/PublicKey/__init__.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/PublicKey/__init__.py: Self-test for public key crypto +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test for public-key crypto""" + +__revision__ = "$Id$" + +import os + +def get_tests(config={}): + tests = [] + from Crypto.SelfTest.PublicKey import test_DSA; tests += test_DSA.get_tests(config=config) + from Crypto.SelfTest.PublicKey import test_RSA; tests += test_RSA.get_tests(config=config) + from Crypto.SelfTest.PublicKey import test_importKey; tests += test_importKey.get_tests(config=config) + from Crypto.SelfTest.PublicKey import test_ElGamal; tests += test_ElGamal.get_tests(config=config) + return tests + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/PublicKey/test_DSA.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/PublicKey/test_DSA.py new file mode 100644 index 0000000..b05f69a --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/PublicKey/test_DSA.py @@ -0,0 +1,244 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/PublicKey/test_DSA.py: Self-test for the DSA primitive +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for Crypto.PublicKey.DSA""" + +__revision__ = "$Id$" + +import sys +import os +if sys.version_info[0] == 2 and sys.version_info[1] == 1: + from Crypto.Util.py21compat import * +from Crypto.Util.py3compat import * + +import unittest +from Crypto.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex + +def _sws(s): + """Remove whitespace from a text or byte string""" + if isinstance(s,str): + return "".join(s.split()) + else: + return b("").join(s.split()) + +class DSATest(unittest.TestCase): + # Test vector from "Appendix 5. Example of the DSA" of + # "Digital Signature Standard (DSS)", + # U.S. Department of Commerce/National Institute of Standards and Technology + # FIPS 186-2 (+Change Notice), 2000 January 27. + # http://csrc.nist.gov/publications/fips/fips186-2/fips186-2-change1.pdf + + y = _sws("""19131871 d75b1612 a819f29d 78d1b0d7 346f7aa7 7bb62a85 + 9bfd6c56 75da9d21 2d3a36ef 1672ef66 0b8c7c25 5cc0ec74 + 858fba33 f44c0669 9630a76b 030ee333""") + + g = _sws("""626d0278 39ea0a13 413163a5 5b4cb500 299d5522 956cefcb + 3bff10f3 99ce2c2e 71cb9de5 fa24babf 58e5b795 21925c9c + c42e9f6f 464b088c c572af53 e6d78802""") + + p = _sws("""8df2a494 492276aa 3d25759b b06869cb eac0d83a fb8d0cf7 + cbb8324f 0d7882e5 d0762fc5 b7210eaf c2e9adac 32ab7aac + 49693dfb f83724c2 ec0736ee 31c80291""") + + q = _sws("""c773218c 737ec8ee 993b4f2d ed30f48e dace915f""") + + x = _sws("""2070b322 3dba372f de1c0ffc 7b2e3b49 8b260614""") + + k = _sws("""358dad57 1462710f 50e254cf 1a376b2b deaadfbf""") + k_inverse = _sws("""0d516729 8202e49b 4116ac10 4fc3f415 ae52f917""") + m = b2a_hex(b("abc")) + m_hash = _sws("""a9993e36 4706816a ba3e2571 7850c26c 9cd0d89d""") + r = _sws("""8bac1ab6 6410435c b7181f95 b16ab97c 92b341c0""") + s = _sws("""41e2345f 1f56df24 58f426d1 55b4ba2d b6dcd8c8""") + + def setUp(self): + global DSA, Random, bytes_to_long, size + from Crypto.PublicKey import DSA + from Crypto import Random + from Crypto.Util.number import bytes_to_long, inverse, size + + self.dsa = DSA + + def test_generate_1arg(self): + """DSA (default implementation) generated key (1 argument)""" + dsaObj = self.dsa.generate(1024) + self._check_private_key(dsaObj) + pub = dsaObj.publickey() + self._check_public_key(pub) + + def test_generate_2arg(self): + """DSA (default implementation) generated key (2 arguments)""" + dsaObj = self.dsa.generate(1024, Random.new().read) + self._check_private_key(dsaObj) + pub = dsaObj.publickey() + self._check_public_key(pub) + + def test_construct_4tuple(self): + """DSA (default implementation) constructed key (4-tuple)""" + (y, g, p, q) = [bytes_to_long(a2b_hex(param)) for param in (self.y, self.g, self.p, self.q)] + dsaObj = self.dsa.construct((y, g, p, q)) + self._test_verification(dsaObj) + + def test_construct_5tuple(self): + """DSA (default implementation) constructed key (5-tuple)""" + (y, g, p, q, x) = [bytes_to_long(a2b_hex(param)) for param in (self.y, self.g, self.p, self.q, self.x)] + dsaObj = self.dsa.construct((y, g, p, q, x)) + self._test_signing(dsaObj) + self._test_verification(dsaObj) + + def _check_private_key(self, dsaObj): + # Check capabilities + self.assertEqual(1, dsaObj.has_private()) + self.assertEqual(1, dsaObj.can_sign()) + self.assertEqual(0, dsaObj.can_encrypt()) + self.assertEqual(0, dsaObj.can_blind()) + + # Check dsaObj.[ygpqx] -> dsaObj.key.[ygpqx] mapping + self.assertEqual(dsaObj.y, dsaObj.key.y) + self.assertEqual(dsaObj.g, dsaObj.key.g) + self.assertEqual(dsaObj.p, dsaObj.key.p) + self.assertEqual(dsaObj.q, dsaObj.key.q) + self.assertEqual(dsaObj.x, dsaObj.key.x) + + # Sanity check key data + self.assertEqual(1, dsaObj.p > dsaObj.q) # p > q + self.assertEqual(160, size(dsaObj.q)) # size(q) == 160 bits + self.assertEqual(0, (dsaObj.p - 1) % dsaObj.q) # q is a divisor of p-1 + self.assertEqual(dsaObj.y, pow(dsaObj.g, dsaObj.x, dsaObj.p)) # y == g**x mod p + self.assertEqual(1, 0 < dsaObj.x < dsaObj.q) # 0 < x < q + + def _check_public_key(self, dsaObj): + k = a2b_hex(self.k) + m_hash = a2b_hex(self.m_hash) + + # Check capabilities + self.assertEqual(0, dsaObj.has_private()) + self.assertEqual(1, dsaObj.can_sign()) + self.assertEqual(0, dsaObj.can_encrypt()) + self.assertEqual(0, dsaObj.can_blind()) + + # Check dsaObj.[ygpq] -> dsaObj.key.[ygpq] mapping + self.assertEqual(dsaObj.y, dsaObj.key.y) + self.assertEqual(dsaObj.g, dsaObj.key.g) + self.assertEqual(dsaObj.p, dsaObj.key.p) + self.assertEqual(dsaObj.q, dsaObj.key.q) + + # Check that private parameters are all missing + self.assertEqual(0, hasattr(dsaObj, 'x')) + self.assertEqual(0, hasattr(dsaObj.key, 'x')) + + # Sanity check key data + self.assertEqual(1, dsaObj.p > dsaObj.q) # p > q + self.assertEqual(160, size(dsaObj.q)) # size(q) == 160 bits + self.assertEqual(0, (dsaObj.p - 1) % dsaObj.q) # q is a divisor of p-1 + + # Public-only key objects should raise an error when .sign() is called + self.assertRaises(TypeError, dsaObj.sign, m_hash, k) + + # Check __eq__ and __ne__ + self.assertEqual(dsaObj.publickey() == dsaObj.publickey(),True) # assert_ + self.assertEqual(dsaObj.publickey() != dsaObj.publickey(),False) # failIf + + def _test_signing(self, dsaObj): + k = a2b_hex(self.k) + m_hash = a2b_hex(self.m_hash) + r = bytes_to_long(a2b_hex(self.r)) + s = bytes_to_long(a2b_hex(self.s)) + (r_out, s_out) = dsaObj.sign(m_hash, k) + self.assertEqual((r, s), (r_out, s_out)) + + def _test_verification(self, dsaObj): + m_hash = a2b_hex(self.m_hash) + r = bytes_to_long(a2b_hex(self.r)) + s = bytes_to_long(a2b_hex(self.s)) + self.assertEqual(1, dsaObj.verify(m_hash, (r, s))) + self.assertEqual(0, dsaObj.verify(m_hash + b("\0"), (r, s))) + +class DSAFastMathTest(DSATest): + def setUp(self): + DSATest.setUp(self) + self.dsa = DSA.DSAImplementation(use_fast_math=True) + + def test_generate_1arg(self): + """DSA (_fastmath implementation) generated key (1 argument)""" + DSATest.test_generate_1arg(self) + + def test_generate_2arg(self): + """DSA (_fastmath implementation) generated key (2 arguments)""" + DSATest.test_generate_2arg(self) + + def test_construct_4tuple(self): + """DSA (_fastmath implementation) constructed key (4-tuple)""" + DSATest.test_construct_4tuple(self) + + def test_construct_5tuple(self): + """DSA (_fastmath implementation) constructed key (5-tuple)""" + DSATest.test_construct_5tuple(self) + +class DSASlowMathTest(DSATest): + def setUp(self): + DSATest.setUp(self) + self.dsa = DSA.DSAImplementation(use_fast_math=False) + + def test_generate_1arg(self): + """DSA (_slowmath implementation) generated key (1 argument)""" + DSATest.test_generate_1arg(self) + + def test_generate_2arg(self): + """DSA (_slowmath implementation) generated key (2 arguments)""" + DSATest.test_generate_2arg(self) + + def test_construct_4tuple(self): + """DSA (_slowmath implementation) constructed key (4-tuple)""" + DSATest.test_construct_4tuple(self) + + def test_construct_5tuple(self): + """DSA (_slowmath implementation) constructed key (5-tuple)""" + DSATest.test_construct_5tuple(self) + + +def get_tests(config={}): + tests = [] + tests += list_test_cases(DSATest) + try: + from Crypto.PublicKey import _fastmath + tests += list_test_cases(DSAFastMathTest) + except ImportError: + from distutils.sysconfig import get_config_var + import inspect + _fm_path = os.path.normpath(os.path.dirname(os.path.abspath( + inspect.getfile(inspect.currentframe()))) + +"/../../PublicKey/_fastmath"+get_config_var("SO")) + if os.path.exists(_fm_path): + raise ImportError("While the _fastmath module exists, importing "+ + "it failed. This may point to the gmp or mpir shared library "+ + "not being in the path. _fastmath was found at "+_fm_path) + tests += list_test_cases(DSASlowMathTest) + return tests + +if __name__ == '__main__': + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/PublicKey/test_ElGamal.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/PublicKey/test_ElGamal.py new file mode 100644 index 0000000..cdee8cf --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/PublicKey/test_ElGamal.py @@ -0,0 +1,210 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/PublicKey/test_ElGamal.py: Self-test for the ElGamal primitive +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for Crypto.PublicKey.ElGamal""" + +__revision__ = "$Id$" + +import unittest +from Crypto.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex +from Crypto import Random +from Crypto.PublicKey import ElGamal +from Crypto.Util.number import * +from Crypto.Util.py3compat import * + +class ElGamalTest(unittest.TestCase): + + # + # Test vectors + # + # There seem to be no real ElGamal test vectors available in the + # public domain. The following test vectors have been generated + # with libgcrypt 1.5.0. + # + # Encryption + tve=[ + { + # 256 bits + 'p' :'BA4CAEAAED8CBE952AFD2126C63EB3B345D65C2A0A73D2A3AD4138B6D09BD933', + 'g' :'05', + 'y' :'60D063600ECED7C7C55146020E7A31C4476E9793BEAED420FEC9E77604CAE4EF', + 'x' :'1D391BA2EE3C37FE1BA175A69B2C73A11238AD77675932', + 'k' :'F5893C5BAB4131264066F57AB3D8AD89E391A0B68A68A1', + 'pt' :'48656C6C6F207468657265', + 'ct1':'32BFD5F487966CEA9E9356715788C491EC515E4ED48B58F0F00971E93AAA5EC7', + 'ct2':'7BE8FBFF317C93E82FCEF9BD515284BA506603FEA25D01C0CB874A31F315EE68' + }, + + { + # 512 bits + 'p' :'F1B18AE9F7B4E08FDA9A04832F4E919D89462FD31BF12F92791A93519F75076D6CE3942689CDFF2F344CAFF0F82D01864F69F3AECF566C774CBACF728B81A227', + 'g' :'07', + 'y' :'688628C676E4F05D630E1BE39D0066178CA7AA83836B645DE5ADD359B4825A12B02EF4252E4E6FA9BEC1DB0BE90F6D7C8629CABB6E531F472B2664868156E20C', + 'x' :'14E60B1BDFD33436C0DA8A22FDC14A2CCDBBED0627CE68', + 'k' :'38DBF14E1F319BDA9BAB33EEEADCAF6B2EA5250577ACE7', + 'pt' :'48656C6C6F207468657265', + 'ct1':'290F8530C2CC312EC46178724F196F308AD4C523CEABB001FACB0506BFED676083FE0F27AC688B5C749AB3CB8A80CD6F7094DBA421FB19442F5A413E06A9772B', + 'ct2':'1D69AAAD1DC50493FB1B8E8721D621D683F3BF1321BE21BC4A43E11B40C9D4D9C80DE3AAC2AB60D31782B16B61112E68220889D53C4C3136EE6F6CE61F8A23A0' + } + ] + + # Signature + tvs=[ + { + # 256 bits + 'p' :'D2F3C41EA66530838A704A48FFAC9334F4701ECE3A97CEE4C69DD01AE7129DD7', + 'g' :'05', + 'y' :'C3F9417DC0DAFEA6A05C1D2333B7A95E63B3F4F28CC962254B3256984D1012E7', + 'x' :'165E4A39BE44D5A2D8B1332D416BC559616F536BC735BB', + 'k' :'C7F0C794A7EAD726E25A47FF8928013680E73C51DD3D7D99BFDA8F492585928F', + 'h' :'48656C6C6F207468657265', + 'sig1':'35CA98133779E2073EF31165AFCDEB764DD54E96ADE851715495F9C635E1E7C2', + 'sig2':'0135B88B1151279FE5D8078D4FC685EE81177EE9802AB123A73925FC1CB059A7', + }, + { + # 512 bits + 'p' :'E24CF3A4B8A6AF749DCA6D714282FE4AABEEE44A53BB6ED15FBE32B5D3C3EF9CC4124A2ECA331F3C1C1B667ACA3766825217E7B5F9856648D95F05330C6A19CF', + 'g' :'0B', + 'y' :'2AD3A1049CA5D4ED207B2431C79A8719BB4073D4A94E450EA6CEE8A760EB07ADB67C0D52C275EE85D7B52789061EE45F2F37D9B2AE522A51C28329766BFE68AC', + 'x' :'16CBB4F46D9ECCF24FF9F7E63CAA3BD8936341555062AB', + 'k' :'8A3D89A4E429FD2476D7D717251FB79BF900FFE77444E6BB8299DC3F84D0DD57ABAB50732AE158EA52F5B9E7D8813E81FD9F79470AE22F8F1CF9AEC820A78C69', + 'h' :'48656C6C6F207468657265', + 'sig1':'BE001AABAFFF976EC9016198FBFEA14CBEF96B000CCC0063D3324016F9E91FE80D8F9325812ED24DDB2B4D4CF4430B169880B3CE88313B53255BD4EC0378586F', + 'sig2':'5E266F3F837BA204E3BBB6DBECC0611429D96F8C7CE8F4EFDF9D4CB681C2A954468A357BF4242CEC7418B51DFC081BCD21299EF5B5A0DDEF3A139A1817503DDE', + } + ] + + def test_generate_128(self): + self._test_random_key(128) + + def test_generate_512(self): + self._test_random_key(512) + + def test_encryption(self): + for tv in self.tve: + for as_longs in (0,1): + d = self.convert_tv(tv, as_longs) + key = ElGamal.construct(d['key']) + ct = key.encrypt(d['pt'], d['k']) + self.assertEquals(ct[0], d['ct1']) + self.assertEquals(ct[1], d['ct2']) + + def test_decryption(self): + for tv in self.tve: + for as_longs in (0,1): + d = self.convert_tv(tv, as_longs) + key = ElGamal.construct(d['key']) + pt = key.decrypt((d['ct1'], d['ct2'])) + self.assertEquals(pt, d['pt']) + + def test_signing(self): + for tv in self.tvs: + for as_longs in (0,1): + d = self.convert_tv(tv, as_longs) + key = ElGamal.construct(d['key']) + sig1, sig2 = key.sign(d['h'], d['k']) + self.assertEquals(sig1, d['sig1']) + self.assertEquals(sig2, d['sig2']) + + def test_verification(self): + for tv in self.tvs: + for as_longs in (0,1): + d = self.convert_tv(tv, as_longs) + key = ElGamal.construct(d['key']) + # Positive test + res = key.verify( d['h'], (d['sig1'],d['sig2']) ) + self.failUnless(res) + # Negative test + res = key.verify( d['h'], (d['sig1']+1,d['sig2']) ) + self.failIf(res) + + def convert_tv(self, tv, as_longs=0): + """Convert a test vector from textual form (hexadecimal ascii + to either integers or byte strings.""" + key_comps = 'p','g','y','x' + tv2 = {} + for c in tv.keys(): + tv2[c] = a2b_hex(tv[c]) + if as_longs or c in key_comps or c in ('sig1','sig2'): + tv2[c] = bytes_to_long(tv2[c]) + tv2['key']=[] + for c in key_comps: + tv2['key'] += [tv2[c]] + del tv2[c] + return tv2 + + def _test_random_key(self, bits): + elgObj = ElGamal.generate(bits, Random.new().read) + self._check_private_key(elgObj) + self._exercise_primitive(elgObj) + pub = elgObj.publickey() + self._check_public_key(pub) + self._exercise_public_primitive(elgObj) + + def _check_private_key(self, elgObj): + + # Check capabilities + self.failUnless(elgObj.has_private()) + self.failUnless(elgObj.can_sign()) + self.failUnless(elgObj.can_encrypt()) + + # Sanity check key data + self.failUnless(1 +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for Crypto.PublicKey.RSA""" + +__revision__ = "$Id$" + +import sys +import os +if sys.version_info[0] == 2 and sys.version_info[1] == 1: + from Crypto.Util.py21compat import * +from Crypto.Util.py3compat import * + +import unittest +from Crypto.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex + +class RSATest(unittest.TestCase): + # Test vectors from "RSA-OAEP and RSA-PSS test vectors (.zip file)" + # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip + # See RSADSI's PKCS#1 page at + # http://www.rsa.com/rsalabs/node.asp?id=2125 + + # from oaep-int.txt + + # TODO: PyCrypto treats the message as starting *after* the leading "00" + # TODO: That behaviour should probably be changed in the future. + plaintext = """ + eb 7a 19 ac e9 e3 00 63 50 e3 29 50 4b 45 e2 + ca 82 31 0b 26 dc d8 7d 5c 68 f1 ee a8 f5 52 67 + c3 1b 2e 8b b4 25 1f 84 d7 e0 b2 c0 46 26 f5 af + f9 3e dc fb 25 c9 c2 b3 ff 8a e1 0e 83 9a 2d db + 4c dc fe 4f f4 77 28 b4 a1 b7 c1 36 2b aa d2 9a + b4 8d 28 69 d5 02 41 21 43 58 11 59 1b e3 92 f9 + 82 fb 3e 87 d0 95 ae b4 04 48 db 97 2f 3a c1 4f + 7b c2 75 19 52 81 ce 32 d2 f1 b7 6d 4d 35 3e 2d + """ + + ciphertext = """ + 12 53 e0 4d c0 a5 39 7b b4 4a 7a b8 7e 9b f2 a0 + 39 a3 3d 1e 99 6f c8 2a 94 cc d3 00 74 c9 5d f7 + 63 72 20 17 06 9e 52 68 da 5d 1c 0b 4f 87 2c f6 + 53 c1 1d f8 23 14 a6 79 68 df ea e2 8d ef 04 bb + 6d 84 b1 c3 1d 65 4a 19 70 e5 78 3b d6 eb 96 a0 + 24 c2 ca 2f 4a 90 fe 9f 2e f5 c9 c1 40 e5 bb 48 + da 95 36 ad 87 00 c8 4f c9 13 0a de a7 4e 55 8d + 51 a7 4d df 85 d8 b5 0d e9 68 38 d6 06 3e 09 55 + """ + + modulus = """ + bb f8 2f 09 06 82 ce 9c 23 38 ac 2b 9d a8 71 f7 + 36 8d 07 ee d4 10 43 a4 40 d6 b6 f0 74 54 f5 1f + b8 df ba af 03 5c 02 ab 61 ea 48 ce eb 6f cd 48 + 76 ed 52 0d 60 e1 ec 46 19 71 9d 8a 5b 8b 80 7f + af b8 e0 a3 df c7 37 72 3e e6 b4 b7 d9 3a 25 84 + ee 6a 64 9d 06 09 53 74 88 34 b2 45 45 98 39 4e + e0 aa b1 2d 7b 61 a5 1f 52 7a 9a 41 f6 c1 68 7f + e2 53 72 98 ca 2a 8f 59 46 f8 e5 fd 09 1d bd cb + """ + + e = 0x11L # public exponent + + prime_factor = """ + c9 7f b1 f0 27 f4 53 f6 34 12 33 ea aa d1 d9 35 + 3f 6c 42 d0 88 66 b1 d0 5a 0f 20 35 02 8b 9d 86 + 98 40 b4 16 66 b4 2e 92 ea 0d a3 b4 32 04 b5 cf + ce 33 52 52 4d 04 16 a5 a4 41 e7 00 af 46 15 03 + """ + + def setUp(self): + global RSA, Random, bytes_to_long + from Crypto.PublicKey import RSA + from Crypto import Random + from Crypto.Util.number import bytes_to_long, inverse + self.n = bytes_to_long(a2b_hex(self.modulus)) + self.p = bytes_to_long(a2b_hex(self.prime_factor)) + + # Compute q, d, and u from n, e, and p + self.q = divmod(self.n, self.p)[0] + self.d = inverse(self.e, (self.p-1)*(self.q-1)) + self.u = inverse(self.p, self.q) # u = e**-1 (mod q) + + self.rsa = RSA + + def test_generate_1arg(self): + """RSA (default implementation) generated key (1 argument)""" + rsaObj = self.rsa.generate(1024) + self._check_private_key(rsaObj) + self._exercise_primitive(rsaObj) + pub = rsaObj.publickey() + self._check_public_key(pub) + self._exercise_public_primitive(rsaObj) + + def test_generate_2arg(self): + """RSA (default implementation) generated key (2 arguments)""" + rsaObj = self.rsa.generate(1024, Random.new().read) + self._check_private_key(rsaObj) + self._exercise_primitive(rsaObj) + pub = rsaObj.publickey() + self._check_public_key(pub) + self._exercise_public_primitive(rsaObj) + + def test_generate_3args(self): + rsaObj = self.rsa.generate(1024, Random.new().read,e=65537) + self._check_private_key(rsaObj) + self._exercise_primitive(rsaObj) + pub = rsaObj.publickey() + self._check_public_key(pub) + self._exercise_public_primitive(rsaObj) + self.assertEqual(65537,rsaObj.e) + + def test_construct_2tuple(self): + """RSA (default implementation) constructed key (2-tuple)""" + pub = self.rsa.construct((self.n, self.e)) + self._check_public_key(pub) + self._check_encryption(pub) + self._check_verification(pub) + + def test_construct_3tuple(self): + """RSA (default implementation) constructed key (3-tuple)""" + rsaObj = self.rsa.construct((self.n, self.e, self.d)) + self._check_encryption(rsaObj) + self._check_decryption(rsaObj) + self._check_signing(rsaObj) + self._check_verification(rsaObj) + + def test_construct_4tuple(self): + """RSA (default implementation) constructed key (4-tuple)""" + rsaObj = self.rsa.construct((self.n, self.e, self.d, self.p)) + self._check_encryption(rsaObj) + self._check_decryption(rsaObj) + self._check_signing(rsaObj) + self._check_verification(rsaObj) + + def test_construct_5tuple(self): + """RSA (default implementation) constructed key (5-tuple)""" + rsaObj = self.rsa.construct((self.n, self.e, self.d, self.p, self.q)) + self._check_private_key(rsaObj) + self._check_encryption(rsaObj) + self._check_decryption(rsaObj) + self._check_signing(rsaObj) + self._check_verification(rsaObj) + + def test_construct_6tuple(self): + """RSA (default implementation) constructed key (6-tuple)""" + rsaObj = self.rsa.construct((self.n, self.e, self.d, self.p, self.q, self.u)) + self._check_private_key(rsaObj) + self._check_encryption(rsaObj) + self._check_decryption(rsaObj) + self._check_signing(rsaObj) + self._check_verification(rsaObj) + + def test_factoring(self): + rsaObj = self.rsa.construct([self.n, self.e, self.d]) + self.failUnless(rsaObj.p==self.p or rsaObj.p==self.q) + self.failUnless(rsaObj.q==self.p or rsaObj.q==self.q) + self.failUnless(rsaObj.q*rsaObj.p == self.n) + + self.assertRaises(ValueError, self.rsa.construct, [self.n, self.e, self.n-1]) + + def _check_private_key(self, rsaObj): + # Check capabilities + self.assertEqual(1, rsaObj.has_private()) + self.assertEqual(1, rsaObj.can_sign()) + self.assertEqual(1, rsaObj.can_encrypt()) + self.assertEqual(1, rsaObj.can_blind()) + + # Check rsaObj.[nedpqu] -> rsaObj.key.[nedpqu] mapping + self.assertEqual(rsaObj.n, rsaObj.key.n) + self.assertEqual(rsaObj.e, rsaObj.key.e) + self.assertEqual(rsaObj.d, rsaObj.key.d) + self.assertEqual(rsaObj.p, rsaObj.key.p) + self.assertEqual(rsaObj.q, rsaObj.key.q) + self.assertEqual(rsaObj.u, rsaObj.key.u) + + # Sanity check key data + self.assertEqual(rsaObj.n, rsaObj.p * rsaObj.q) # n = pq + self.assertEqual(1, rsaObj.d * rsaObj.e % ((rsaObj.p-1) * (rsaObj.q-1))) # ed = 1 (mod (p-1)(q-1)) + self.assertEqual(1, rsaObj.p * rsaObj.u % rsaObj.q) # pu = 1 (mod q) + self.assertEqual(1, rsaObj.p > 1) # p > 1 + self.assertEqual(1, rsaObj.q > 1) # q > 1 + self.assertEqual(1, rsaObj.e > 1) # e > 1 + self.assertEqual(1, rsaObj.d > 1) # d > 1 + + def _check_public_key(self, rsaObj): + ciphertext = a2b_hex(self.ciphertext) + + # Check capabilities + self.assertEqual(0, rsaObj.has_private()) + self.assertEqual(1, rsaObj.can_sign()) + self.assertEqual(1, rsaObj.can_encrypt()) + self.assertEqual(1, rsaObj.can_blind()) + + # Check rsaObj.[ne] -> rsaObj.key.[ne] mapping + self.assertEqual(rsaObj.n, rsaObj.key.n) + self.assertEqual(rsaObj.e, rsaObj.key.e) + + # Check that private parameters are all missing + self.assertEqual(0, hasattr(rsaObj, 'd')) + self.assertEqual(0, hasattr(rsaObj, 'p')) + self.assertEqual(0, hasattr(rsaObj, 'q')) + self.assertEqual(0, hasattr(rsaObj, 'u')) + self.assertEqual(0, hasattr(rsaObj.key, 'd')) + self.assertEqual(0, hasattr(rsaObj.key, 'p')) + self.assertEqual(0, hasattr(rsaObj.key, 'q')) + self.assertEqual(0, hasattr(rsaObj.key, 'u')) + + # Sanity check key data + self.assertEqual(1, rsaObj.e > 1) # e > 1 + + # Public keys should not be able to sign or decrypt + self.assertRaises(TypeError, rsaObj.sign, ciphertext, b("")) + self.assertRaises(TypeError, rsaObj.decrypt, ciphertext) + + # Check __eq__ and __ne__ + self.assertEqual(rsaObj.publickey() == rsaObj.publickey(),True) # assert_ + self.assertEqual(rsaObj.publickey() != rsaObj.publickey(),False) # failIf + + def _exercise_primitive(self, rsaObj): + # Since we're using a randomly-generated key, we can't check the test + # vector, but we can make sure encryption and decryption are inverse + # operations. + ciphertext = a2b_hex(self.ciphertext) + + # Test decryption + plaintext = rsaObj.decrypt((ciphertext,)) + + # Test encryption (2 arguments) + (new_ciphertext2,) = rsaObj.encrypt(plaintext, b("")) + self.assertEqual(b2a_hex(ciphertext), b2a_hex(new_ciphertext2)) + + # Test blinded decryption + blinding_factor = Random.new().read(len(ciphertext)-1) + blinded_ctext = rsaObj.blind(ciphertext, blinding_factor) + blinded_ptext = rsaObj.decrypt((blinded_ctext,)) + unblinded_plaintext = rsaObj.unblind(blinded_ptext, blinding_factor) + self.assertEqual(b2a_hex(plaintext), b2a_hex(unblinded_plaintext)) + + # Test signing (2 arguments) + signature2 = rsaObj.sign(ciphertext, b("")) + self.assertEqual((bytes_to_long(plaintext),), signature2) + + # Test verification + self.assertEqual(1, rsaObj.verify(ciphertext, (bytes_to_long(plaintext),))) + + def _exercise_public_primitive(self, rsaObj): + plaintext = a2b_hex(self.plaintext) + + # Test encryption (2 arguments) + (new_ciphertext2,) = rsaObj.encrypt(plaintext, b("")) + + # Exercise verification + rsaObj.verify(new_ciphertext2, (bytes_to_long(plaintext),)) + + def _check_encryption(self, rsaObj): + plaintext = a2b_hex(self.plaintext) + ciphertext = a2b_hex(self.ciphertext) + + # Test encryption (2 arguments) + (new_ciphertext2,) = rsaObj.encrypt(plaintext, b("")) + self.assertEqual(b2a_hex(ciphertext), b2a_hex(new_ciphertext2)) + + def _check_decryption(self, rsaObj): + plaintext = a2b_hex(self.plaintext) + ciphertext = a2b_hex(self.ciphertext) + + # Test plain decryption + new_plaintext = rsaObj.decrypt((ciphertext,)) + self.assertEqual(b2a_hex(plaintext), b2a_hex(new_plaintext)) + + # Test blinded decryption + blinding_factor = Random.new().read(len(ciphertext)-1) + blinded_ctext = rsaObj.blind(ciphertext, blinding_factor) + blinded_ptext = rsaObj.decrypt((blinded_ctext,)) + unblinded_plaintext = rsaObj.unblind(blinded_ptext, blinding_factor) + self.assertEqual(b2a_hex(plaintext), b2a_hex(unblinded_plaintext)) + + def _check_verification(self, rsaObj): + signature = bytes_to_long(a2b_hex(self.plaintext)) + message = a2b_hex(self.ciphertext) + + # Test verification + t = (signature,) # rsaObj.verify expects a tuple + self.assertEqual(1, rsaObj.verify(message, t)) + + # Test verification with overlong tuple (this is a + # backward-compatibility hack to support some harmless misuse of the + # API) + t2 = (signature, '') + self.assertEqual(1, rsaObj.verify(message, t2)) # extra garbage at end of tuple + + def _check_signing(self, rsaObj): + signature = bytes_to_long(a2b_hex(self.plaintext)) + message = a2b_hex(self.ciphertext) + + # Test signing (2 argument) + self.assertEqual((signature,), rsaObj.sign(message, b(""))) + +class RSAFastMathTest(RSATest): + def setUp(self): + RSATest.setUp(self) + self.rsa = RSA.RSAImplementation(use_fast_math=True) + + def test_generate_1arg(self): + """RSA (_fastmath implementation) generated key (1 argument)""" + RSATest.test_generate_1arg(self) + + def test_generate_2arg(self): + """RSA (_fastmath implementation) generated key (2 arguments)""" + RSATest.test_generate_2arg(self) + + def test_construct_2tuple(self): + """RSA (_fastmath implementation) constructed key (2-tuple)""" + RSATest.test_construct_2tuple(self) + + def test_construct_3tuple(self): + """RSA (_fastmath implementation) constructed key (3-tuple)""" + RSATest.test_construct_3tuple(self) + + def test_construct_4tuple(self): + """RSA (_fastmath implementation) constructed key (4-tuple)""" + RSATest.test_construct_4tuple(self) + + def test_construct_5tuple(self): + """RSA (_fastmath implementation) constructed key (5-tuple)""" + RSATest.test_construct_5tuple(self) + + def test_construct_6tuple(self): + """RSA (_fastmath implementation) constructed key (6-tuple)""" + RSATest.test_construct_6tuple(self) + + def test_factoring(self): + RSATest.test_factoring(self) + +class RSASlowMathTest(RSATest): + def setUp(self): + RSATest.setUp(self) + self.rsa = RSA.RSAImplementation(use_fast_math=False) + + def test_generate_1arg(self): + """RSA (_slowmath implementation) generated key (1 argument)""" + RSATest.test_generate_1arg(self) + + def test_generate_2arg(self): + """RSA (_slowmath implementation) generated key (2 arguments)""" + RSATest.test_generate_2arg(self) + + def test_construct_2tuple(self): + """RSA (_slowmath implementation) constructed key (2-tuple)""" + RSATest.test_construct_2tuple(self) + + def test_construct_3tuple(self): + """RSA (_slowmath implementation) constructed key (3-tuple)""" + RSATest.test_construct_3tuple(self) + + def test_construct_4tuple(self): + """RSA (_slowmath implementation) constructed key (4-tuple)""" + RSATest.test_construct_4tuple(self) + + def test_construct_5tuple(self): + """RSA (_slowmath implementation) constructed key (5-tuple)""" + RSATest.test_construct_5tuple(self) + + def test_construct_6tuple(self): + """RSA (_slowmath implementation) constructed key (6-tuple)""" + RSATest.test_construct_6tuple(self) + + def test_factoring(self): + RSATest.test_factoring(self) + +def get_tests(config={}): + tests = [] + tests += list_test_cases(RSATest) + try: + from Crypto.PublicKey import _fastmath + tests += list_test_cases(RSAFastMathTest) + except ImportError: + from distutils.sysconfig import get_config_var + import inspect + _fm_path = os.path.normpath(os.path.dirname(os.path.abspath( + inspect.getfile(inspect.currentframe()))) + +"/../../PublicKey/_fastmath"+get_config_var("SO")) + if os.path.exists(_fm_path): + raise ImportError("While the _fastmath module exists, importing "+ + "it failed. This may point to the gmp or mpir shared library "+ + "not being in the path. _fastmath was found at "+_fm_path) + if config.get('slow_tests',1): + tests += list_test_cases(RSASlowMathTest) + return tests + +if __name__ == '__main__': + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/PublicKey/test_importKey.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/PublicKey/test_importKey.py new file mode 100644 index 0000000..28a7eee --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/PublicKey/test_importKey.py @@ -0,0 +1,345 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/PublicKey/test_importKey.py: Self-test for importing RSA keys +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +from __future__ import nested_scopes + +__revision__ = "$Id$" + +import unittest + +from Crypto.PublicKey import RSA +from Crypto.SelfTest.st_common import * +from Crypto.Util.py3compat import * +from Crypto.Util.number import inverse +from Crypto.Util import asn1 + +def der2pem(der, text='PUBLIC'): + import binascii + chunks = [ binascii.b2a_base64(der[i:i+48]) for i in range(0, len(der), 48) ] + pem = b('-----BEGIN %s KEY-----\n' % text) + pem += b('').join(chunks) + pem += b('-----END %s KEY-----' % text) + return pem + +class ImportKeyTests(unittest.TestCase): + # 512-bit RSA key generated with openssl + rsaKeyPEM = u'''-----BEGIN RSA PRIVATE KEY----- +MIIBOwIBAAJBAL8eJ5AKoIsjURpcEoGubZMxLD7+kT+TLr7UkvEtFrRhDDKMtuII +q19FrL4pUIMymPMSLBn3hJLe30Dw48GQM4UCAwEAAQJACUSDEp8RTe32ftq8IwG8 +Wojl5mAd1wFiIOrZ/Uv8b963WJOJiuQcVN29vxU5+My9GPZ7RA3hrDBEAoHUDPrI +OQIhAPIPLz4dphiD9imAkivY31Rc5AfHJiQRA7XixTcjEkojAiEAyh/pJHks/Mlr ++rdPNEpotBjfV4M4BkgGAA/ipcmaAjcCIQCHvhwwKVBLzzTscT2HeUdEeBMoiXXK +JACAr3sJQJGxIQIgarRp+m1WSKV1MciwMaTOnbU7wxFs9DP1pva76lYBzgUCIQC9 +n0CnZCJ6IZYqSt0H5N7+Q+2Ro64nuwV/OSQfM6sBwQ== +-----END RSA PRIVATE KEY-----''' + + # As above, but this is actually an unencrypted PKCS#8 key + rsaKeyPEM8 = u'''-----BEGIN PRIVATE KEY----- +MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAvx4nkAqgiyNRGlwS +ga5tkzEsPv6RP5MuvtSS8S0WtGEMMoy24girX0WsvilQgzKY8xIsGfeEkt7fQPDj +wZAzhQIDAQABAkAJRIMSnxFN7fZ+2rwjAbxaiOXmYB3XAWIg6tn9S/xv3rdYk4mK +5BxU3b2/FTn4zL0Y9ntEDeGsMEQCgdQM+sg5AiEA8g8vPh2mGIP2KYCSK9jfVFzk +B8cmJBEDteLFNyMSSiMCIQDKH+kkeSz8yWv6t080Smi0GN9XgzgGSAYAD+KlyZoC +NwIhAIe+HDApUEvPNOxxPYd5R0R4EyiJdcokAICvewlAkbEhAiBqtGn6bVZIpXUx +yLAxpM6dtTvDEWz0M/Wm9rvqVgHOBQIhAL2fQKdkInohlipK3Qfk3v5D7ZGjrie7 +BX85JB8zqwHB +-----END PRIVATE KEY-----''' + + # The same RSA private key as in rsaKeyPEM, but now encrypted + rsaKeyEncryptedPEM=( + + # With DES and passphrase 'test' + ('test', u'''-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-CBC,AF8F9A40BD2FA2FC + +Ckl9ex1kaVEWhYC2QBmfaF+YPiR4NFkRXA7nj3dcnuFEzBnY5XULupqQpQI3qbfA +u8GYS7+b3toWWiHZivHbAAUBPDIZG9hKDyB9Sq2VMARGsX1yW1zhNvZLIiVJzUHs +C6NxQ1IJWOXzTew/xM2I26kPwHIvadq+/VaT8gLQdjdH0jOiVNaevjWnLgrn1mLP +BCNRMdcexozWtAFNNqSzfW58MJL2OdMi21ED184EFytIc1BlB+FZiGZduwKGuaKy +9bMbdb/1PSvsSzPsqW7KSSrTw6MgJAFJg6lzIYvR5F4poTVBxwBX3+EyEmShiaNY +IRX3TgQI0IjrVuLmvlZKbGWP18FXj7I7k9tSsNOOzllTTdq3ny5vgM3A+ynfAaxp +dysKznQ6P+IoqML1WxAID4aGRMWka+uArOJ148Rbj9s= +-----END RSA PRIVATE KEY-----''', + "\xAF\x8F\x9A\x40\xBD\x2F\xA2\xFC"), + + # With Triple-DES and passphrase 'rocking' + ('rocking', u'''-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,C05D6C07F7FC02F6 + +w4lwQrXaVoTTJ0GgwY566htTA2/t1YlimhxkxYt9AEeCcidS5M0Wq9ClPiPz9O7F +m6K5QpM1rxo1RUE/ZyI85gglRNPdNwkeTOqit+kum7nN73AToX17+irVmOA4Z9E+ +4O07t91GxGMcjUSIFk0ucwEU4jgxRvYscbvOMvNbuZszGdVNzBTVddnShKCsy9i7 +nJbPlXeEKYi/OkRgO4PtfqqWQu5GIEFVUf9ev1QV7AvC+kyWTR1wWYnHX265jU5c +sopxQQtP8XEHIJEdd5/p1oieRcWTCNyY8EkslxDSsrf0OtZp6mZH9N+KU47cgQtt +9qGORmlWnsIoFFKcDohbtOaWBTKhkj5h6OkLjFjfU/sBeV1c+7wDT3dAy5tawXjG +YSxC7qDQIT/RECvV3+oQKEcmpEujn45wAnkTi12BH30= +-----END RSA PRIVATE KEY-----''', + "\xC0\x5D\x6C\x07\xF7\xFC\x02\xF6"), + ) + + rsaPublicKeyPEM = u'''-----BEGIN PUBLIC KEY----- +MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL8eJ5AKoIsjURpcEoGubZMxLD7+kT+T +Lr7UkvEtFrRhDDKMtuIIq19FrL4pUIMymPMSLBn3hJLe30Dw48GQM4UCAwEAAQ== +-----END PUBLIC KEY-----''' + + # Obtained using 'ssh-keygen -i -m PKCS8 -f rsaPublicKeyPEM' + rsaPublicKeyOpenSSH = '''ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAQQC/HieQCqCLI1EaXBKBrm2TMSw+/pE/ky6+1JLxLRa0YQwyjLbiCKtfRay+KVCDMpjzEiwZ94SS3t9A8OPBkDOF comment\n''' + + # The private key, in PKCS#1 format encoded with DER + rsaKeyDER = a2b_hex( + '''3082013b020100024100bf1e27900aa08b23511a5c1281ae6d93312c3efe + 913f932ebed492f12d16b4610c328cb6e208ab5f45acbe2950833298f312 + 2c19f78492dedf40f0e3c190338502030100010240094483129f114dedf6 + 7edabc2301bc5a88e5e6601dd7016220ead9fd4bfc6fdeb75893898ae41c + 54ddbdbf1539f8ccbd18f67b440de1ac30440281d40cfac839022100f20f + 2f3e1da61883f62980922bd8df545ce407c726241103b5e2c53723124a23 + 022100ca1fe924792cfcc96bfab74f344a68b418df578338064806000fe2 + a5c99a023702210087be1c3029504bcf34ec713d877947447813288975ca + 240080af7b094091b12102206ab469fa6d5648a57531c8b031a4ce9db53b + c3116cf433f5a6f6bbea5601ce05022100bd9f40a764227a21962a4add07 + e4defe43ed91a3ae27bb057f39241f33ab01c1 + '''.replace(" ","")) + + # The private key, in unencrypted PKCS#8 format encoded with DER + rsaKeyDER8 = a2b_hex( + '''30820155020100300d06092a864886f70d01010105000482013f3082013 + b020100024100bf1e27900aa08b23511a5c1281ae6d93312c3efe913f932 + ebed492f12d16b4610c328cb6e208ab5f45acbe2950833298f3122c19f78 + 492dedf40f0e3c190338502030100010240094483129f114dedf67edabc2 + 301bc5a88e5e6601dd7016220ead9fd4bfc6fdeb75893898ae41c54ddbdb + f1539f8ccbd18f67b440de1ac30440281d40cfac839022100f20f2f3e1da + 61883f62980922bd8df545ce407c726241103b5e2c53723124a23022100c + a1fe924792cfcc96bfab74f344a68b418df578338064806000fe2a5c99a0 + 23702210087be1c3029504bcf34ec713d877947447813288975ca240080a + f7b094091b12102206ab469fa6d5648a57531c8b031a4ce9db53bc3116cf + 433f5a6f6bbea5601ce05022100bd9f40a764227a21962a4add07e4defe4 + 3ed91a3ae27bb057f39241f33ab01c1 + '''.replace(" ","")) + + rsaPublicKeyDER = a2b_hex( + '''305c300d06092a864886f70d0101010500034b003048024100bf1e27900a + a08b23511a5c1281ae6d93312c3efe913f932ebed492f12d16b4610c328c + b6e208ab5f45acbe2950833298f3122c19f78492dedf40f0e3c190338502 + 03010001 + '''.replace(" ","")) + + n = long('BF 1E 27 90 0A A0 8B 23 51 1A 5C 12 81 AE 6D 93 31 2C 3E FE 91 3F 93 2E BE D4 92 F1 2D 16 B4 61 0C 32 8C B6 E2 08 AB 5F 45 AC BE 29 50 83 32 98 F3 12 2C 19 F7 84 92 DE DF 40 F0 E3 C1 90 33 85'.replace(" ",""),16) + e = 65537L + d = long('09 44 83 12 9F 11 4D ED F6 7E DA BC 23 01 BC 5A 88 E5 E6 60 1D D7 01 62 20 EA D9 FD 4B FC 6F DE B7 58 93 89 8A E4 1C 54 DD BD BF 15 39 F8 CC BD 18 F6 7B 44 0D E1 AC 30 44 02 81 D4 0C FA C8 39'.replace(" ",""),16) + p = long('00 F2 0F 2F 3E 1D A6 18 83 F6 29 80 92 2B D8 DF 54 5C E4 07 C7 26 24 11 03 B5 E2 C5 37 23 12 4A 23'.replace(" ",""),16) + q = long('00 CA 1F E9 24 79 2C FC C9 6B FA B7 4F 34 4A 68 B4 18 DF 57 83 38 06 48 06 00 0F E2 A5 C9 9A 02 37'.replace(" ",""),16) + + # This is q^{-1} mod p). fastmath and slowmath use pInv (p^{-1} + # mod q) instead! + qInv = long('00 BD 9F 40 A7 64 22 7A 21 96 2A 4A DD 07 E4 DE FE 43 ED 91 A3 AE 27 BB 05 7F 39 24 1F 33 AB 01 C1'.replace(" ",""),16) + pInv = inverse(p,q) + + def testImportKey1(self): + """Verify import of RSAPrivateKey DER SEQUENCE""" + key = self.rsa.importKey(self.rsaKeyDER) + self.failUnless(key.has_private()) + self.assertEqual(key.n, self.n) + self.assertEqual(key.e, self.e) + self.assertEqual(key.d, self.d) + self.assertEqual(key.p, self.p) + self.assertEqual(key.q, self.q) + + def testImportKey2(self): + """Verify import of SubjectPublicKeyInfo DER SEQUENCE""" + key = self.rsa.importKey(self.rsaPublicKeyDER) + self.failIf(key.has_private()) + self.assertEqual(key.n, self.n) + self.assertEqual(key.e, self.e) + + def testImportKey3unicode(self): + """Verify import of RSAPrivateKey DER SEQUENCE, encoded with PEM as unicode""" + key = RSA.importKey(self.rsaKeyPEM) + self.assertEqual(key.has_private(),True) # assert_ + self.assertEqual(key.n, self.n) + self.assertEqual(key.e, self.e) + self.assertEqual(key.d, self.d) + self.assertEqual(key.p, self.p) + self.assertEqual(key.q, self.q) + + def testImportKey3bytes(self): + """Verify import of RSAPrivateKey DER SEQUENCE, encoded with PEM as byte string""" + key = RSA.importKey(b(self.rsaKeyPEM)) + self.assertEqual(key.has_private(),True) # assert_ + self.assertEqual(key.n, self.n) + self.assertEqual(key.e, self.e) + self.assertEqual(key.d, self.d) + self.assertEqual(key.p, self.p) + self.assertEqual(key.q, self.q) + + def testImportKey4unicode(self): + """Verify import of RSAPrivateKey DER SEQUENCE, encoded with PEM as unicode""" + key = RSA.importKey(self.rsaPublicKeyPEM) + self.assertEqual(key.has_private(),False) # failIf + self.assertEqual(key.n, self.n) + self.assertEqual(key.e, self.e) + + def testImportKey4bytes(self): + """Verify import of SubjectPublicKeyInfo DER SEQUENCE, encoded with PEM as byte string""" + key = RSA.importKey(b(self.rsaPublicKeyPEM)) + self.assertEqual(key.has_private(),False) # failIf + self.assertEqual(key.n, self.n) + self.assertEqual(key.e, self.e) + + def testImportKey5(self): + """Verifies that the imported key is still a valid RSA pair""" + key = RSA.importKey(self.rsaKeyPEM) + idem = key.encrypt(key.decrypt(b("Test")),0) + self.assertEqual(idem[0],b("Test")) + + def testImportKey6(self): + """Verifies that the imported key is still a valid RSA pair""" + key = RSA.importKey(self.rsaKeyDER) + idem = key.encrypt(key.decrypt(b("Test")),0) + self.assertEqual(idem[0],b("Test")) + + def testImportKey7(self): + """Verify import of OpenSSH public key""" + key = self.rsa.importKey(self.rsaPublicKeyOpenSSH) + self.assertEqual(key.n, self.n) + self.assertEqual(key.e, self.e) + + def testImportKey8(self): + """Verify import of encrypted PrivateKeyInfo DER SEQUENCE""" + for t in self.rsaKeyEncryptedPEM: + key = self.rsa.importKey(t[1], t[0]) + self.failUnless(key.has_private()) + self.assertEqual(key.n, self.n) + self.assertEqual(key.e, self.e) + self.assertEqual(key.d, self.d) + self.assertEqual(key.p, self.p) + self.assertEqual(key.q, self.q) + + def testImportKey9(self): + """Verify import of unencrypted PrivateKeyInfo DER SEQUENCE""" + key = self.rsa.importKey(self.rsaKeyDER8) + self.failUnless(key.has_private()) + self.assertEqual(key.n, self.n) + self.assertEqual(key.e, self.e) + self.assertEqual(key.d, self.d) + self.assertEqual(key.p, self.p) + self.assertEqual(key.q, self.q) + + def testImportKey10(self): + """Verify import of unencrypted PrivateKeyInfo DER SEQUENCE, encoded with PEM""" + key = self.rsa.importKey(self.rsaKeyPEM8) + self.failUnless(key.has_private()) + self.assertEqual(key.n, self.n) + self.assertEqual(key.e, self.e) + self.assertEqual(key.d, self.d) + self.assertEqual(key.p, self.p) + self.assertEqual(key.q, self.q) + + def testImportKey11(self): + """Verify import of RSAPublicKey DER SEQUENCE""" + der = asn1.DerSequence([17, 3]).encode() + key = self.rsa.importKey(der) + self.assertEqual(key.n, 17) + self.assertEqual(key.e, 3) + + def testImportKey12(self): + """Verify import of RSAPublicKey DER SEQUENCE, encoded with PEM""" + der = asn1.DerSequence([17, 3]).encode() + pem = der2pem(der) + key = self.rsa.importKey(pem) + self.assertEqual(key.n, 17) + self.assertEqual(key.e, 3) + + ### + def testExportKey1(self): + key = self.rsa.construct([self.n, self.e, self.d, self.p, self.q, self.pInv]) + derKey = key.exportKey("DER") + self.assertEqual(derKey, self.rsaKeyDER) + + def testExportKey2(self): + key = self.rsa.construct([self.n, self.e]) + derKey = key.exportKey("DER") + self.assertEqual(derKey, self.rsaPublicKeyDER) + + def testExportKey3(self): + key = self.rsa.construct([self.n, self.e, self.d, self.p, self.q, self.pInv]) + pemKey = key.exportKey("PEM") + self.assertEqual(pemKey, b(self.rsaKeyPEM)) + + def testExportKey4(self): + key = self.rsa.construct([self.n, self.e]) + pemKey = key.exportKey("PEM") + self.assertEqual(pemKey, b(self.rsaPublicKeyPEM)) + + def testExportKey5(self): + key = self.rsa.construct([self.n, self.e]) + openssh_1 = key.exportKey("OpenSSH").split() + openssh_2 = self.rsaPublicKeyOpenSSH.split() + self.assertEqual(openssh_1[0], openssh_2[0]) + self.assertEqual(openssh_1[1], openssh_2[1]) + + def testExportKey4(self): + key = self.rsa.construct([self.n, self.e, self.d, self.p, self.q, self.pInv]) + # Tuple with index #1 is encrypted with 3DES + t = map(b,self.rsaKeyEncryptedPEM[1]) + # Force the salt being used when exporting + key._randfunc = lambda N: (t[2]*divmod(N+len(t[2]),len(t[2]))[0])[:N] + pemKey = key.exportKey("PEM", t[0]) + self.assertEqual(pemKey, t[1]) + + def testExportKey5(self): + key = self.rsa.construct([self.n, self.e, self.d, self.p, self.q, self.pInv]) + derKey = key.exportKey("DER", pkcs=8) + self.assertEqual(derKey, self.rsaKeyDER8) + + def testExportKey6(self): + key = self.rsa.construct([self.n, self.e, self.d, self.p, self.q, self.pInv]) + pemKey = key.exportKey("PEM", pkcs=8) + self.assertEqual(pemKey, b(self.rsaKeyPEM8)) + +class ImportKeyTestsSlow(ImportKeyTests): + def setUp(self): + self.rsa = RSA.RSAImplementation(use_fast_math=0) + +class ImportKeyTestsFast(ImportKeyTests): + def setUp(self): + self.rsa = RSA.RSAImplementation(use_fast_math=1) + +if __name__ == '__main__': + unittest.main() + +def get_tests(config={}): + tests = [] + try: + from Crypto.PublicKey import _fastmath + tests += list_test_cases(ImportKeyTestsFast) + except ImportError: + pass + tests += list_test_cases(ImportKeyTestsSlow) + return tests + +if __name__ == '__main__': + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/Fortuna/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/Fortuna/__init__.py new file mode 100644 index 0000000..81a0e13 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/Fortuna/__init__.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Random/Fortuna/__init__.py: Self-test for Fortuna modules +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test for the Crypto.Random.Fortuna package""" + +__revision__ = "$Id$" + +import os + +def get_tests(config={}): + tests = [] + from Crypto.SelfTest.Random.Fortuna import test_FortunaAccumulator; tests += test_FortunaAccumulator.get_tests(config=config) + from Crypto.SelfTest.Random.Fortuna import test_FortunaGenerator; tests += test_FortunaGenerator.get_tests(config=config) + from Crypto.SelfTest.Random.Fortuna import test_SHAd256; tests += test_SHAd256.get_tests(config=config) + return tests + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/Fortuna/test_FortunaAccumulator.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/Fortuna/test_FortunaAccumulator.py new file mode 100644 index 0000000..c4e6ccf --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/Fortuna/test_FortunaAccumulator.py @@ -0,0 +1,189 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Random/Fortuna/test_FortunaAccumulator.py: Self-test for the FortunaAccumulator module +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-tests for Crypto.Random.Fortuna.FortunaAccumulator""" + +__revision__ = "$Id$" + +import sys +if sys.version_info[0] == 2 and sys.version_info[1] == 1: + from Crypto.Util.py21compat import * +from Crypto.Util.py3compat import * + +import unittest +from binascii import b2a_hex + +class FortunaAccumulatorTests(unittest.TestCase): + def setUp(self): + global FortunaAccumulator + from Crypto.Random.Fortuna import FortunaAccumulator + + def test_FortunaPool(self): + """FortunaAccumulator.FortunaPool""" + pool = FortunaAccumulator.FortunaPool() + self.assertEqual(0, pool.length) + self.assertEqual("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456", pool.hexdigest()) + + pool.append(b('abc')) + + self.assertEqual(3, pool.length) + self.assertEqual("4f8b42c22dd3729b519ba6f68d2da7cc5b2d606d05daed5ad5128cc03e6c6358", pool.hexdigest()) + + pool.append(b("dbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")) + + self.assertEqual(56, pool.length) + self.assertEqual(b('0cffe17f68954dac3a84fb1458bd5ec99209449749b2b308b7cb55812f9563af'), b2a_hex(pool.digest())) + + pool.reset() + + self.assertEqual(0, pool.length) + + pool.append(b('a') * 10**6) + + self.assertEqual(10**6, pool.length) + self.assertEqual(b('80d1189477563e1b5206b2749f1afe4807e5705e8bd77887a60187a712156688'), b2a_hex(pool.digest())) + + def test_which_pools(self): + """FortunaAccumulator.which_pools""" + + # which_pools(0) should fail + self.assertRaises(AssertionError, FortunaAccumulator.which_pools, 0) + + self.assertEqual(FortunaAccumulator.which_pools(1), [0]) + self.assertEqual(FortunaAccumulator.which_pools(2), [0, 1]) + self.assertEqual(FortunaAccumulator.which_pools(3), [0]) + self.assertEqual(FortunaAccumulator.which_pools(4), [0, 1, 2]) + self.assertEqual(FortunaAccumulator.which_pools(5), [0]) + self.assertEqual(FortunaAccumulator.which_pools(6), [0, 1]) + self.assertEqual(FortunaAccumulator.which_pools(7), [0]) + self.assertEqual(FortunaAccumulator.which_pools(8), [0, 1, 2, 3]) + for i in range(1, 32): + self.assertEqual(FortunaAccumulator.which_pools(2L**i-1), [0]) + self.assertEqual(FortunaAccumulator.which_pools(2L**i), range(i+1)) + self.assertEqual(FortunaAccumulator.which_pools(2L**i+1), [0]) + self.assertEqual(FortunaAccumulator.which_pools(2L**31), range(32)) + self.assertEqual(FortunaAccumulator.which_pools(2L**32), range(32)) + self.assertEqual(FortunaAccumulator.which_pools(2L**33), range(32)) + self.assertEqual(FortunaAccumulator.which_pools(2L**34), range(32)) + self.assertEqual(FortunaAccumulator.which_pools(2L**35), range(32)) + self.assertEqual(FortunaAccumulator.which_pools(2L**36), range(32)) + self.assertEqual(FortunaAccumulator.which_pools(2L**64), range(32)) + self.assertEqual(FortunaAccumulator.which_pools(2L**128), range(32)) + + def test_accumulator(self): + """FortunaAccumulator.FortunaAccumulator""" + fa = FortunaAccumulator.FortunaAccumulator() + + # This should fail, because we haven't seeded the PRNG yet + self.assertRaises(AssertionError, fa.random_data, 1) + + # Spread some test data across the pools (source number 42) + # This would be horribly insecure in a real system. + for p in range(32): + fa.add_random_event(42, p, b("X") * 32) + self.assertEqual(32+2, fa.pools[p].length) + + # This should still fail, because we haven't seeded the PRNG with 64 bytes yet + self.assertRaises(AssertionError, fa.random_data, 1) + + # Add more data + for p in range(32): + fa.add_random_event(42, p, b("X") * 32) + self.assertEqual((32+2)*2, fa.pools[p].length) + + # The underlying RandomGenerator should get seeded with Pool 0 + # s = SHAd256(chr(42) + chr(32) + "X"*32 + chr(42) + chr(32) + "X"*32) + # = SHA256(h'edd546f057b389155a31c32e3975e736c1dec030ddebb137014ecbfb32ed8c6f') + # = h'aef42a5dcbddab67e8efa118e1b47fde5d697f89beb971b99e6e8e5e89fbf064' + # The counter and the key before reseeding is: + # C_0 = 0 + # K_0 = "\x00" * 32 + # The counter after reseeding is 1, and the new key after reseeding is + # C_1 = 1 + # K_1 = SHAd256(K_0 || s) + # = SHA256(h'0eae3e401389fab86640327ac919ecfcb067359d95469e18995ca889abc119a6') + # = h'aafe9d0409fbaaafeb0a1f2ef2014a20953349d3c1c6e6e3b962953bea6184dd' + # The first block of random data, therefore, is + # r_1 = AES-256(K_1, 1) + # = AES-256(K_1, h'01000000000000000000000000000000') + # = h'b7b86bd9a27d96d7bb4add1b6b10d157' + # The second block of random data is + # r_2 = AES-256(K_1, 2) + # = AES-256(K_1, h'02000000000000000000000000000000') + # = h'2350b1c61253db2f8da233be726dc15f' + # The third and fourth blocks of random data (which become the new key) are + # r_3 = AES-256(K_1, 3) + # = AES-256(K_1, h'03000000000000000000000000000000') + # = h'f23ad749f33066ff53d307914fbf5b21' + # r_4 = AES-256(K_1, 4) + # = AES-256(K_1, h'04000000000000000000000000000000') + # = h'da9667c7e86ba247655c9490e9d94a7c' + # K_2 = r_3 || r_4 + # = h'f23ad749f33066ff53d307914fbf5b21da9667c7e86ba247655c9490e9d94a7c' + # The final counter value is 5. + self.assertEqual("aef42a5dcbddab67e8efa118e1b47fde5d697f89beb971b99e6e8e5e89fbf064", + fa.pools[0].hexdigest()) + self.assertEqual(None, fa.generator.key) + self.assertEqual(0, fa.generator.counter.next_value()) + + result = fa.random_data(32) + + self.assertEqual(b("b7b86bd9a27d96d7bb4add1b6b10d157" "2350b1c61253db2f8da233be726dc15f"), b2a_hex(result)) + self.assertEqual(b("f23ad749f33066ff53d307914fbf5b21da9667c7e86ba247655c9490e9d94a7c"), b2a_hex(fa.generator.key)) + self.assertEqual(5, fa.generator.counter.next_value()) + + def test_accumulator_pool_length(self): + """FortunaAccumulator.FortunaAccumulator minimum pool length""" + fa = FortunaAccumulator.FortunaAccumulator() + + # This test case is hard-coded to assume that FortunaAccumulator.min_pool_size is 64. + self.assertEqual(fa.min_pool_size, 64) + + # The PRNG should not allow us to get random data from it yet + self.assertRaises(AssertionError, fa.random_data, 1) + + # Add 60 bytes, 4 at a time (2 header + 2 payload) to each of the 32 pools + for i in range(15): + for p in range(32): + # Add the bytes to the pool + fa.add_random_event(2, p, b("XX")) + + # The PRNG should not allow us to get random data from it yet + self.assertRaises(AssertionError, fa.random_data, 1) + + # Add 4 more bytes to pool 0 + fa.add_random_event(2, 0, b("XX")) + + # We should now be able to get data from the accumulator + fa.random_data(1) + +def get_tests(config={}): + from Crypto.SelfTest.st_common import list_test_cases + return list_test_cases(FortunaAccumulatorTests) + +if __name__ == '__main__': + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/Fortuna/test_FortunaGenerator.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/Fortuna/test_FortunaGenerator.py new file mode 100644 index 0000000..d41bb02 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/Fortuna/test_FortunaGenerator.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Random/Fortuna/test_FortunaGenerator.py: Self-test for the FortunaGenerator module +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-tests for Crypto.Random.Fortuna.FortunaGenerator""" + +__revision__ = "$Id$" + +import sys +if sys.version_info[0] == 2 and sys.version_info[1] == 1: + from Crypto.Util.py21compat import * +from Crypto.Util.py3compat import * + +import unittest +from binascii import b2a_hex + +class FortunaGeneratorTests(unittest.TestCase): + def setUp(self): + global FortunaGenerator + from Crypto.Random.Fortuna import FortunaGenerator + + def test_generator(self): + """FortunaGenerator.AESGenerator""" + fg = FortunaGenerator.AESGenerator() + + # We shouldn't be able to read data until we've seeded the generator + self.assertRaises(Exception, fg.pseudo_random_data, 1) + self.assertEqual(0, fg.counter.next_value()) + + # Seed the generator, which should set the key and increment the counter. + fg.reseed(b("Hello")) + self.assertEqual(b("0ea6919d4361551364242a4ba890f8f073676e82cf1a52bb880f7e496648b565"), b2a_hex(fg.key)) + self.assertEqual(1, fg.counter.next_value()) + + # Read 2 full blocks from the generator + self.assertEqual(b("7cbe2c17684ac223d08969ee8b565616") + # counter=1 + b("717661c0d2f4758bd6ba140bf3791abd"), # counter=2 + b2a_hex(fg.pseudo_random_data(32))) + + # Meanwhile, the generator will have re-keyed itself and incremented its counter + self.assertEqual(b("33a1bb21987859caf2bbfc5615bef56d") + # counter=3 + b("e6b71ff9f37112d0c193a135160862b7"), # counter=4 + b2a_hex(fg.key)) + self.assertEqual(5, fg.counter.next_value()) + + # Read another 2 blocks from the generator + self.assertEqual(b("fd6648ba3086e919cee34904ef09a7ff") + # counter=5 + b("021f77580558b8c3e9248275f23042bf"), # counter=6 + b2a_hex(fg.pseudo_random_data(32))) + + + # Try to read more than 2**20 bytes using the internal function. This should fail. + self.assertRaises(AssertionError, fg._pseudo_random_data, 2**20+1) + +def get_tests(config={}): + from Crypto.SelfTest.st_common import list_test_cases + return list_test_cases(FortunaGeneratorTests) + +if __name__ == '__main__': + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/Fortuna/test_SHAd256.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/Fortuna/test_SHAd256.py new file mode 100644 index 0000000..f94db8a --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/Fortuna/test_SHAd256.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Random/Fortuna/test_SHAd256.py: Self-test for the SHAd256 hash function +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for Crypto.Random.Fortuna.SHAd256""" + +__revision__ = "$Id$" +from Crypto.Util.py3compat import * + +# This is a list of (expected_result, input[, description]) tuples. +test_data = [ + # I could not find any test vectors for SHAd256, so I made these vectors by + # feeding some sample data into several plain SHA256 implementations + # (including OpenSSL, the "sha256sum" tool, and this implementation). + # This is a subset of the resulting test vectors. The complete list can be + # found at: http://www.dlitz.net/crypto/shad256-test-vectors/ + ('5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456', + '', "'' (empty string)"), + ('4f8b42c22dd3729b519ba6f68d2da7cc5b2d606d05daed5ad5128cc03e6c6358', + 'abc'), + ('0cffe17f68954dac3a84fb1458bd5ec99209449749b2b308b7cb55812f9563af', + 'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq') +] + +def get_tests(config={}): + from Crypto.Random.Fortuna import SHAd256 + from Crypto.SelfTest.Hash.common import make_hash_tests + return make_hash_tests(SHAd256, "SHAd256", test_data, 32) + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/OSRNG/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/OSRNG/__init__.py new file mode 100644 index 0000000..44b3fa1 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/OSRNG/__init__.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Random/OSRNG/__init__.py: Self-test for OSRNG modules +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test for Crypto.Random.OSRNG package""" + +__revision__ = "$Id$" + +import os + +def get_tests(config={}): + tests = [] + if os.name == 'nt': + from Crypto.SelfTest.Random.OSRNG import test_nt; tests += test_nt.get_tests(config=config) + from Crypto.SelfTest.Random.OSRNG import test_winrandom; tests += test_winrandom.get_tests(config=config) + elif os.name == 'posix': + from Crypto.SelfTest.Random.OSRNG import test_posix; tests += test_posix.get_tests(config=config) + if hasattr(os, 'urandom'): + from Crypto.SelfTest.Random.OSRNG import test_fallback; tests += test_fallback.get_tests(config=config) + from Crypto.SelfTest.Random.OSRNG import test_generic; tests += test_generic.get_tests(config=config) + return tests + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/OSRNG/test_fallback.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/OSRNG/test_fallback.py new file mode 100644 index 0000000..41909b0 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/OSRNG/test_fallback.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Util/test_fallback.py: Self-test for the OSRNG.fallback.new() function +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for Crypto.Random.OSRNG.fallback""" + +__revision__ = "$Id$" + +import unittest + +class SimpleTest(unittest.TestCase): + def runTest(self): + """Crypto.Random.OSRNG.fallback.new()""" + # Import the OSRNG.nt module and try to use it + import Crypto.Random.OSRNG.fallback + randobj = Crypto.Random.OSRNG.fallback.new() + x = randobj.read(16) + y = randobj.read(16) + self.assertNotEqual(x, y) + +def get_tests(config={}): + return [SimpleTest()] + +if __name__ == '__main__': + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/OSRNG/test_generic.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/OSRNG/test_generic.py new file mode 100644 index 0000000..2a40974 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/OSRNG/test_generic.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Util/test_generic.py: Self-test for the OSRNG.new() function +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for Crypto.Random.OSRNG""" + +__revision__ = "$Id$" + +import unittest + +class SimpleTest(unittest.TestCase): + def runTest(self): + """Crypto.Random.OSRNG.new()""" + # Import the OSRNG module and try to use it + import Crypto.Random.OSRNG + randobj = Crypto.Random.OSRNG.new() + x = randobj.read(16) + y = randobj.read(16) + self.assertNotEqual(x, y) + +def get_tests(config={}): + return [SimpleTest()] + +if __name__ == '__main__': + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/OSRNG/test_nt.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/OSRNG/test_nt.py new file mode 100644 index 0000000..a7a8338 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/OSRNG/test_nt.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Util/test_generic.py: Self-test for the OSRNG.nt.new() function +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for Crypto.Random.OSRNG.nt""" + +__revision__ = "$Id$" + +import unittest + +class SimpleTest(unittest.TestCase): + def runTest(self): + """Crypto.Random.OSRNG.nt.new()""" + # Import the OSRNG.nt module and try to use it + import Crypto.Random.OSRNG.nt + randobj = Crypto.Random.OSRNG.nt.new() + x = randobj.read(16) + y = randobj.read(16) + self.assertNotEqual(x, y) + +def get_tests(config={}): + return [SimpleTest()] + +if __name__ == '__main__': + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/OSRNG/test_posix.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/OSRNG/test_posix.py new file mode 100644 index 0000000..2224afe --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/OSRNG/test_posix.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Util/test_posix.py: Self-test for the OSRNG.posix.new() function +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for Crypto.Random.OSRNG.posix""" + +__revision__ = "$Id$" + +import unittest + +class SimpleTest(unittest.TestCase): + def runTest(self): + """Crypto.Random.OSRNG.posix.new()""" + # Import the OSRNG.nt module and try to use it + import Crypto.Random.OSRNG.posix + randobj = Crypto.Random.OSRNG.posix.new() + x = randobj.read(16) + y = randobj.read(16) + self.assertNotEqual(x, y) + +def get_tests(config={}): + return [SimpleTest()] + +if __name__ == '__main__': + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/OSRNG/test_winrandom.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/OSRNG/test_winrandom.py new file mode 100644 index 0000000..3010eb7 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/OSRNG/test_winrandom.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Util/test_winrandom.py: Self-test for the winrandom module +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for Crypto.Random.OSRNG.winrandom""" + +__revision__ = "$Id$" + +import unittest + +class SimpleTest(unittest.TestCase): + def runTest(self): + """Crypto.Random.OSRNG.winrandom""" + # Import the winrandom module and try to use it + from Crypto.Random.OSRNG import winrandom + randobj = winrandom.new() + x = randobj.get_bytes(16) + y = randobj.get_bytes(16) + self.assertNotEqual(x, y) + +def get_tests(config={}): + return [SimpleTest()] + +if __name__ == '__main__': + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/__init__.py new file mode 100644 index 0000000..f972bf0 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/__init__.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Random/__init__.py: Self-test for random number generation modules +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test for random number generators""" + +__revision__ = "$Id$" + +def get_tests(config={}): + tests = [] + from Crypto.SelfTest.Random import Fortuna; tests += Fortuna.get_tests(config=config) + from Crypto.SelfTest.Random import OSRNG; tests += OSRNG.get_tests(config=config) + from Crypto.SelfTest.Random import test_random; tests += test_random.get_tests(config=config) + from Crypto.SelfTest.Random import test_rpoolcompat; tests += test_rpoolcompat.get_tests(config=config) + from Crypto.SelfTest.Random import test__UserFriendlyRNG; tests += test__UserFriendlyRNG.get_tests(config=config) + return tests + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/test__UserFriendlyRNG.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/test__UserFriendlyRNG.py new file mode 100644 index 0000000..771a663 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/test__UserFriendlyRNG.py @@ -0,0 +1,171 @@ +# -*- coding: utf-8 -*- +# Self-tests for the user-friendly Crypto.Random interface +# +# Written in 2013 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for generic Crypto.Random stuff """ + +from __future__ import nested_scopes + +__revision__ = "$Id$" + +import binascii +import pprint +import unittest +import os +import time +import sys +if sys.version_info[0] == 2 and sys.version_info[1] == 1: + from Crypto.Util.py21compat import * +from Crypto.Util.py3compat import * + +try: + import multiprocessing +except ImportError: + multiprocessing = None + +import Crypto.Random._UserFriendlyRNG +import Crypto.Random.random + +class RNGForkTest(unittest.TestCase): + + def _get_reseed_count(self): + """ + Get `FortunaAccumulator.reseed_count`, the global count of the + number of times that the PRNG has been reseeded. + """ + rng_singleton = Crypto.Random._UserFriendlyRNG._get_singleton() + rng_singleton._lock.acquire() + try: + return rng_singleton._fa.reseed_count + finally: + rng_singleton._lock.release() + + def runTest(self): + # Regression test for CVE-2013-1445. We had a bug where, under the + # right conditions, two processes might see the same random sequence. + + if sys.platform.startswith('win'): # windows can't fork + assert not hasattr(os, 'fork') # ... right? + return + + # Wait 150 ms so that we don't trigger the rate-limit prematurely. + time.sleep(0.15) + + reseed_count_before = self._get_reseed_count() + + # One or both of these calls together should trigger a reseed right here. + Crypto.Random._UserFriendlyRNG._get_singleton().reinit() + Crypto.Random.get_random_bytes(1) + + reseed_count_after = self._get_reseed_count() + self.assertNotEqual(reseed_count_before, reseed_count_after) # sanity check: test should reseed parent before forking + + rfiles = [] + for i in range(10): + rfd, wfd = os.pipe() + if os.fork() == 0: + # child + os.close(rfd) + f = os.fdopen(wfd, "wb") + + Crypto.Random.atfork() + + data = Crypto.Random.get_random_bytes(16) + + f.write(data) + f.close() + os._exit(0) + # parent + os.close(wfd) + rfiles.append(os.fdopen(rfd, "rb")) + + results = [] + results_dict = {} + for f in rfiles: + data = binascii.hexlify(f.read()) + results.append(data) + results_dict[data] = 1 + f.close() + + if len(results) != len(results_dict.keys()): + raise AssertionError("RNG output duplicated across fork():\n%s" % + (pprint.pformat(results))) + + +# For RNGMultiprocessingForkTest +def _task_main(q): + a = Crypto.Random.get_random_bytes(16) + time.sleep(0.1) # wait 100 ms + b = Crypto.Random.get_random_bytes(16) + q.put(binascii.b2a_hex(a)) + q.put(binascii.b2a_hex(b)) + q.put(None) # Wait for acknowledgment + + +class RNGMultiprocessingForkTest(unittest.TestCase): + + def runTest(self): + # Another regression test for CVE-2013-1445. This is basically the + # same as RNGForkTest, but less compatible with old versions of Python, + # and a little easier to read. + + n_procs = 5 + manager = multiprocessing.Manager() + queues = [manager.Queue(1) for i in range(n_procs)] + + # Reseed the pool + time.sleep(0.15) + Crypto.Random._UserFriendlyRNG._get_singleton().reinit() + Crypto.Random.get_random_bytes(1) + + # Start the child processes + pool = multiprocessing.Pool(processes=n_procs, initializer=Crypto.Random.atfork) + map_result = pool.map_async(_task_main, queues) + + # Get the results, ensuring that no pool processes are reused. + aa = [queues[i].get(30) for i in range(n_procs)] + bb = [queues[i].get(30) for i in range(n_procs)] + res = list(zip(aa, bb)) + + # Shut down the pool + map_result.get(30) + pool.close() + pool.join() + + # Check that the results are unique + if len(set(aa)) != len(aa) or len(set(res)) != len(res): + raise AssertionError("RNG output duplicated across fork():\n%s" % + (pprint.pformat(res),)) + + +def get_tests(config={}): + tests = [] + tests += [RNGForkTest()] + if multiprocessing is not None: + tests += [RNGMultiprocessingForkTest()] + return tests + +if __name__ == '__main__': + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/test_random.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/test_random.py new file mode 100644 index 0000000..f9ffc66 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/test_random.py @@ -0,0 +1,171 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Util/test_generic.py: Self-test for the Crypto.Random.new() function +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for Crypto.Random.new()""" + +__revision__ = "$Id$" + +import unittest +import sys +if sys.version_info[0] == 2 and sys.version_info[1] == 1: + from Crypto.Util.py21compat import * +from Crypto.Util.py3compat import * + +class SimpleTest(unittest.TestCase): + def runTest(self): + """Crypto.Random.new()""" + # Import the Random module and try to use it + from Crypto import Random + randobj = Random.new() + x = randobj.read(16) + y = randobj.read(16) + self.assertNotEqual(x, y) + z = Random.get_random_bytes(16) + self.assertNotEqual(x, z) + self.assertNotEqual(y, z) + # Test the Random.random module, which + # implements a subset of Python's random API + # Not implemented: + # seed(), getstate(), setstate(), jumpahead() + # random(), uniform(), triangular(), betavariate() + # expovariate(), gammavariate(), gauss(), + # longnormvariate(), normalvariate(), + # vonmisesvariate(), paretovariate() + # weibullvariate() + # WichmannHill(), whseed(), SystemRandom() + from Crypto.Random import random + x = random.getrandbits(16*8) + y = random.getrandbits(16*8) + self.assertNotEqual(x, y) + # Test randrange + if x>y: + start = y + stop = x + else: + start = x + stop = y + for step in range(1,10): + x = random.randrange(start,stop,step) + y = random.randrange(start,stop,step) + self.assertNotEqual(x, y) + self.assertEqual(start <= x < stop, True) + self.assertEqual(start <= y < stop, True) + self.assertEqual((x - start) % step, 0) + self.assertEqual((y - start) % step, 0) + for i in range(10): + self.assertEqual(random.randrange(1,2), 1) + self.assertRaises(ValueError, random.randrange, start, start) + self.assertRaises(ValueError, random.randrange, stop, start, step) + self.assertRaises(TypeError, random.randrange, start, stop, step, step) + self.assertRaises(TypeError, random.randrange, start, stop, "1") + self.assertRaises(TypeError, random.randrange, "1", stop, step) + self.assertRaises(TypeError, random.randrange, 1, "2", step) + self.assertRaises(ValueError, random.randrange, start, stop, 0) + # Test randint + x = random.randint(start,stop) + y = random.randint(start,stop) + self.assertNotEqual(x, y) + self.assertEqual(start <= x <= stop, True) + self.assertEqual(start <= y <= stop, True) + for i in range(10): + self.assertEqual(random.randint(1,1), 1) + self.assertRaises(ValueError, random.randint, stop, start) + self.assertRaises(TypeError, random.randint, start, stop, step) + self.assertRaises(TypeError, random.randint, "1", stop) + self.assertRaises(TypeError, random.randint, 1, "2") + # Test choice + seq = range(10000) + x = random.choice(seq) + y = random.choice(seq) + self.assertNotEqual(x, y) + self.assertEqual(x in seq, True) + self.assertEqual(y in seq, True) + for i in range(10): + self.assertEqual(random.choice((1,2,3)) in (1,2,3), True) + self.assertEqual(random.choice([1,2,3]) in [1,2,3], True) + if sys.version_info[0] is 3: + self.assertEqual(random.choice(bytearray(b('123'))) in bytearray(b('123')), True) + self.assertEqual(1, random.choice([1])) + self.assertRaises(IndexError, random.choice, []) + self.assertRaises(TypeError, random.choice, 1) + # Test shuffle. Lacks random parameter to specify function. + # Make copies of seq + seq = range(500) + x = list(seq) + y = list(seq) + random.shuffle(x) + random.shuffle(y) + self.assertNotEqual(x, y) + self.assertEqual(len(seq), len(x)) + self.assertEqual(len(seq), len(y)) + for i in range(len(seq)): + self.assertEqual(x[i] in seq, True) + self.assertEqual(y[i] in seq, True) + self.assertEqual(seq[i] in x, True) + self.assertEqual(seq[i] in y, True) + z = [1] + random.shuffle(z) + self.assertEqual(z, [1]) + if sys.version_info[0] == 3: + z = bytearray(b('12')) + random.shuffle(z) + self.assertEqual(b('1') in z, True) + self.assertRaises(TypeError, random.shuffle, b('12')) + self.assertRaises(TypeError, random.shuffle, 1) + self.assertRaises(TypeError, random.shuffle, "1") + self.assertRaises(TypeError, random.shuffle, (1,2)) + # 2to3 wraps a list() around it, alas - but I want to shoot + # myself in the foot here! :D + # if sys.version_info[0] == 3: + # self.assertRaises(TypeError, random.shuffle, range(3)) + # Test sample + x = random.sample(seq, 20) + y = random.sample(seq, 20) + self.assertNotEqual(x, y) + for i in range(20): + self.assertEqual(x[i] in seq, True) + self.assertEqual(y[i] in seq, True) + z = random.sample([1], 1) + self.assertEqual(z, [1]) + z = random.sample((1,2,3), 1) + self.assertEqual(z[0] in (1,2,3), True) + z = random.sample("123", 1) + self.assertEqual(z[0] in "123", True) + z = random.sample(range(3), 1) + self.assertEqual(z[0] in range(3), True) + if sys.version_info[0] == 3: + z = random.sample(b("123"), 1) + self.assertEqual(z[0] in b("123"), True) + z = random.sample(bytearray(b("123")), 1) + self.assertEqual(z[0] in bytearray(b("123")), True) + self.assertRaises(TypeError, random.sample, 1) + +def get_tests(config={}): + return [SimpleTest()] + +if __name__ == '__main__': + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/test_rpoolcompat.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/test_rpoolcompat.py new file mode 100644 index 0000000..be538da --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Random/test_rpoolcompat.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Util/test_winrandom.py: Self-test for the winrandom module +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test for the Crypto.Util.randpool.RandomPool wrapper class""" + +__revision__ = "$Id$" + +import sys +import unittest + +class SimpleTest(unittest.TestCase): + def runTest(self): + """Crypto.Util.randpool.RandomPool""" + # Import the winrandom module and try to use it + from Crypto.Util.randpool import RandomPool + sys.stderr.write("SelfTest: You can ignore the RandomPool_DeprecationWarning that follows.\n") + rpool = RandomPool() + x = rpool.get_bytes(16) + y = rpool.get_bytes(16) + self.assertNotEqual(x, y) + self.assertNotEqual(rpool.entropy, 0) + + rpool.randomize() + rpool.stir('foo') + rpool.add_event('foo') + +def get_tests(config={}): + return [SimpleTest()] + +if __name__ == '__main__': + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Signature/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Signature/__init__.py new file mode 100644 index 0000000..653b66c --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Signature/__init__.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Signature/__init__.py: Self-test for signature modules +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test for signature modules""" + +__revision__ = "$Id$" + +import os + +def get_tests(config={}): + tests = [] + import test_pkcs1_15; tests += test_pkcs1_15.get_tests(config=config) + import test_pkcs1_pss; tests += test_pkcs1_pss.get_tests(config=config) + return tests + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Signature/test_pkcs1_15.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Signature/test_pkcs1_15.py new file mode 100644 index 0000000..bc36696 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Signature/test_pkcs1_15.py @@ -0,0 +1,219 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Signature/test_pkcs1_15.py: Self-test for PKCS#1 v1.5 signatures +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +__revision__ = "$Id$" + +import unittest + +from Crypto.PublicKey import RSA +from Crypto.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex +from Crypto.Hash import * +from Crypto import Random +from Crypto.Signature import PKCS1_v1_5 as PKCS +from Crypto.Util.py3compat import * + +def isStr(s): + t = '' + try: + t += s + except TypeError: + return 0 + return 1 + +def rws(t): + """Remove white spaces, tabs, and new lines from a string""" + for c in ['\n', '\t', ' ']: + t = t.replace(c,'') + return t + +def t2b(t): + """Convert a text string with bytes in hex form to a byte string""" + clean = b(rws(t)) + if len(clean)%2 == 1: + raise ValueError("Even number of characters expected") + return a2b_hex(clean) + +class PKCS1_15_Tests(unittest.TestCase): + + # List of tuples with test data for PKCS#1 v1.5. + # Each tuple is made up by: + # Item #0: dictionary with RSA key component, or key to import + # Item #1: data to hash and sign + # Item #2: signature of the data #1, done with the key #0, after + # hashing it with #3 + # Item #3: hash object generator + + _testData = ( + + # + # Taken from ftp://ftp.rsa.com/pub/pkcs/ascii/examples.asc + # "Some Examples of the PKCS Standards", 1999 + # + ( + + # Private key, from 2.1 + { + 'n':'''0a 66 79 1d c6 98 81 68 de 7a b7 74 19 bb 7f b0 c0 01 c6 + 27 10 27 00 75 14 29 42 e1 9a 8d 8c 51 d0 53 b3 e3 78 2a 1d + e5 dc 5a f4 eb e9 94 68 17 01 14 a1 df e6 7c dc 9a 9a f5 5d + 65 56 20 bb ab''', + 'e':'''01 00 + 01''', + 'd':'''01 23 c5 b6 1b a3 6e db 1d 36 79 90 41 99 a8 9e a8 0c 09 + b9 12 2e 14 00 c0 9a dc f7 78 46 76 d0 1d 23 35 6a 7d 44 d6 + bd 8b d5 0e 94 bf c7 23 fa 87 d8 86 2b 75 17 76 91 c1 1d 75 + 76 92 df 88 81''' + }, + # Data to sign, from 3.1 + '''30 81 a4 02 01 00 30 42 31 0b 30 09 06 + 03 55 04 06 13 02 55 53 31 1d 30 1b 06 03 55 04 0a 13 14 + 45 78 61 6d 70 6c 65 20 4f 72 67 61 6e 69 7a 61 74 69 6f + 6e 31 14 30 12 06 03 55 04 03 13 0b 54 65 73 74 20 55 73 + 65 72 20 31 30 5b 30 0d 06 09 2a 86 48 86 f7 0d 01 01 01 + 05 00 03 4a 00 30 47 02 40 + 0a 66 79 1d c6 98 81 68 de 7a b7 74 19 bb 7f b0 + c0 01 c6 27 10 27 00 75 14 29 42 e1 9a 8d 8c 51 + d0 53 b3 e3 78 2a 1d e5 dc 5a f4 eb e9 94 68 17 + 01 14 a1 df e6 7c dc 9a 9a f5 5d 65 56 20 bb ab + 02 03 01 00 01''', + # Signature, from 3.2 (at the very end) + '''06 db 36 cb 18 d3 47 5b 9c 01 db 3c 78 95 28 08 + 02 79 bb ae ff 2b 7d 55 8e d6 61 59 87 c8 51 86 + 3f 8a 6c 2c ff bc 89 c3 f7 5a 18 d9 6b 12 7c 71 + 7d 54 d0 d8 04 8d a8 a0 54 46 26 d1 7a 2a 8f be''', + MD2 + ), + + # + # RSA keypair generated with openssl + # + ( + """-----BEGIN RSA PRIVATE KEY----- + MIIBOwIBAAJBAL8eJ5AKoIsjURpcEoGubZMxLD7+kT+TLr7UkvEtFrRhDDKMtuII + q19FrL4pUIMymPMSLBn3hJLe30Dw48GQM4UCAwEAAQJACUSDEp8RTe32ftq8IwG8 + Wojl5mAd1wFiIOrZ/Uv8b963WJOJiuQcVN29vxU5+My9GPZ7RA3hrDBEAoHUDPrI + OQIhAPIPLz4dphiD9imAkivY31Rc5AfHJiQRA7XixTcjEkojAiEAyh/pJHks/Mlr + +rdPNEpotBjfV4M4BkgGAA/ipcmaAjcCIQCHvhwwKVBLzzTscT2HeUdEeBMoiXXK + JACAr3sJQJGxIQIgarRp+m1WSKV1MciwMaTOnbU7wxFs9DP1pva76lYBzgUCIQC9 + n0CnZCJ6IZYqSt0H5N7+Q+2Ro64nuwV/OSQfM6sBwQ== + -----END RSA PRIVATE KEY-----""", + "This is a test\x0a", + # + # PKCS#1 signature computed with openssl + # + '''4a700a16432a291a3194646952687d5316458b8b86fb0a25aa30e0dcecdb + 442676759ac63d56ec1499c3ae4c0013c2053cabd5b5804848994541ac16 + fa243a4d''', + SHA + ), + + # + # Test vector from http://www.di-mgt.com.au/rsa_alg.html#signpkcs1 + # + ( + { + 'n':'''E08973398DD8F5F5E88776397F4EB005BB5383DE0FB7ABDC7DC775290D052E6D + 12DFA68626D4D26FAA5829FC97ECFA82510F3080BEB1509E4644F12CBBD832CF + C6686F07D9B060ACBEEE34096A13F5F7050593DF5EBA3556D961FF197FC981E6 + F86CEA874070EFAC6D2C749F2DFA553AB9997702A648528C4EF357385774575F''', + 'e':'''010001''', + 'd':'''00A403C327477634346CA686B57949014B2E8AD2C862B2C7D748096A8B91F736 + F275D6E8CD15906027314735644D95CD6763CEB49F56AC2F376E1CEE0EBF282D + F439906F34D86E085BD5656AD841F313D72D395EFE33CBFF29E4030B3D05A28F + B7F18EA27637B07957D32F2BDE8706227D04665EC91BAF8B1AC3EC9144AB7F21''' + }, + "abc", + '''60AD5A78FB4A4030EC542C8974CD15F55384E836554CEDD9A322D5F4135C6267 + A9D20970C54E6651070B0144D43844C899320DD8FA7819F7EBC6A7715287332E + C8675C136183B3F8A1F81EF969418267130A756FDBB2C71D9A667446E34E0EAD + 9CF31BFB66F816F319D0B7E430A5F2891553986E003720261C7E9022C0D9F11F''', + SHA + ) + + ) + + def testSign1(self): + for i in range(len(self._testData)): + row = self._testData[i] + # Build the key + if isStr(row[0]): + key = RSA.importKey(row[0]) + else: + comps = [ long(rws(row[0][x]),16) for x in ('n','e','d') ] + key = RSA.construct(comps) + h = row[3].new() + # Data to sign can either be in hex form or not + try: + h.update(t2b(row[1])) + except: + h.update(b(row[1])) + # The real test + signer = PKCS.new(key) + self.failUnless(signer.can_sign()) + s = signer.sign(h) + self.assertEqual(s, t2b(row[2])) + + def testVerify1(self): + for i in range(len(self._testData)): + row = self._testData[i] + # Build the key + if isStr(row[0]): + key = RSA.importKey(row[0]).publickey() + else: + comps = [ long(rws(row[0][x]),16) for x in ('n','e') ] + key = RSA.construct(comps) + h = row[3].new() + # Data to sign can either be in hex form or not + try: + h.update(t2b(row[1])) + except: + h.update(b(row[1])) + # The real test + verifier = PKCS.new(key) + self.failIf(verifier.can_sign()) + result = verifier.verify(h, t2b(row[2])) + self.failUnless(result) + + def testSignVerify(self): + rng = Random.new().read + key = RSA.generate(1024, rng) + + for hashmod in (MD2,MD5,SHA,SHA224,SHA256,SHA384,SHA512,RIPEMD): + h = hashmod.new() + h.update(b('blah blah blah')) + + signer = PKCS.new(key) + s = signer.sign(h) + result = signer.verify(h, s) + self.failUnless(result) + + +def get_tests(config={}): + tests = [] + tests += list_test_cases(PKCS1_15_Tests) + return tests + +if __name__ == '__main__': + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Signature/test_pkcs1_pss.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Signature/test_pkcs1_pss.py new file mode 100644 index 0000000..f5256a5 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Signature/test_pkcs1_pss.py @@ -0,0 +1,446 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Signature/test_pkcs1_pss.py: Self-test for PKCS#1 PSS signatures +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +from __future__ import nested_scopes + +__revision__ = "$Id$" + +import unittest + +from Crypto.PublicKey import RSA +from Crypto import Random +from Crypto.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex +from Crypto.Hash import * +from Crypto.Signature import PKCS1_PSS as PKCS +from Crypto.Util.py3compat import * + +def isStr(s): + t = '' + try: + t += s + except TypeError: + return 0 + return 1 + +def rws(t): + """Remove white spaces, tabs, and new lines from a string""" + for c in ['\t', '\n', ' ']: + t = t.replace(c,'') + return t + +def t2b(t): + """Convert a text string with bytes in hex form to a byte string""" + clean = b(rws(t)) + if len(clean)%2 == 1: + raise ValueError("Even number of characters expected") + return a2b_hex(clean) + +# Helper class to count how many bytes have been requested +# from the key's private RNG, w/o counting those used for blinding +class MyKey: + def __init__(self, key): + self._key = key + self.n = key.n + self.asked = 0 + def _randfunc(self, N): + self.asked += N + return self._key._randfunc(N) + def sign(self, m): + return self._key.sign(m) + def has_private(self): + return self._key.has_private() + def decrypt(self, m): + return self._key.decrypt(m) + def verify(self, m, p): + return self._key.verify(m, p) + def encrypt(self, m, p): + return self._key.encrypt(m, p) + +class PKCS1_PSS_Tests(unittest.TestCase): + + # List of tuples with test data for PKCS#1 PSS + # Each tuple is made up by: + # Item #0: dictionary with RSA key component, or key to import + # Item #1: data to hash and sign + # Item #2: signature of the data #1, done with the key #0, + # and salt #3 after hashing it with #4 + # Item #3: salt + # Item #4: hash object generator + + _testData = ( + + # + # From in pss-vect.txt to be found in + # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip + # + ( + # Private key + { + 'n':'''a2 ba 40 ee 07 e3 b2 bd 2f 02 ce 22 7f 36 a1 95 + 02 44 86 e4 9c 19 cb 41 bb bd fb ba 98 b2 2b 0e + 57 7c 2e ea ff a2 0d 88 3a 76 e6 5e 39 4c 69 d4 + b3 c0 5a 1e 8f ad da 27 ed b2 a4 2b c0 00 fe 88 + 8b 9b 32 c2 2d 15 ad d0 cd 76 b3 e7 93 6e 19 95 + 5b 22 0d d1 7d 4e a9 04 b1 ec 10 2b 2e 4d e7 75 + 12 22 aa 99 15 10 24 c7 cb 41 cc 5e a2 1d 00 ee + b4 1f 7c 80 08 34 d2 c6 e0 6b ce 3b ce 7e a9 a5''', + 'e':'''01 00 01''', + # In the test vector, only p and q were given... + # d is computed offline as e^{-1} mod (p-1)(q-1) + 'd':'''50e2c3e38d886110288dfc68a9533e7e12e27d2aa56 + d2cdb3fb6efa990bcff29e1d2987fb711962860e7391b1ce01 + ebadb9e812d2fbdfaf25df4ae26110a6d7a26f0b810f54875e + 17dd5c9fb6d641761245b81e79f8c88f0e55a6dcd5f133abd3 + 5f8f4ec80adf1bf86277a582894cb6ebcd2162f1c7534f1f49 + 47b129151b71''' + }, + + # Data to sign + '''85 9e ef 2f d7 8a ca 00 30 8b dc 47 11 93 bf 55 + bf 9d 78 db 8f 8a 67 2b 48 46 34 f3 c9 c2 6e 64 + 78 ae 10 26 0f e0 dd 8c 08 2e 53 a5 29 3a f2 17 + 3c d5 0c 6d 5d 35 4f eb f7 8b 26 02 1c 25 c0 27 + 12 e7 8c d4 69 4c 9f 46 97 77 e4 51 e7 f8 e9 e0 + 4c d3 73 9c 6b bf ed ae 48 7f b5 56 44 e9 ca 74 + ff 77 a5 3c b7 29 80 2f 6e d4 a5 ff a8 ba 15 98 + 90 fc''', + # Signature + '''8d aa 62 7d 3d e7 59 5d 63 05 6c 7e c6 59 e5 44 + 06 f1 06 10 12 8b aa e8 21 c8 b2 a0 f3 93 6d 54 + dc 3b dc e4 66 89 f6 b7 95 1b b1 8e 84 05 42 76 + 97 18 d5 71 5d 21 0d 85 ef bb 59 61 92 03 2c 42 + be 4c 29 97 2c 85 62 75 eb 6d 5a 45 f0 5f 51 87 + 6f c6 74 3d ed dd 28 ca ec 9b b3 0e a9 9e 02 c3 + 48 82 69 60 4f e4 97 f7 4c cd 7c 7f ca 16 71 89 + 71 23 cb d3 0d ef 5d 54 a2 b5 53 6a d9 0a 74 7e''', + # Salt + '''e3 b5 d5 d0 02 c1 bc e5 0c 2b 65 ef 88 a1 88 d8 + 3b ce 7e 61''', + # Hash algorithm + SHA + ), + + # + # Example 1.1 to be found in + # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip + # + ( + # Private key + { + 'n':'''a5 6e 4a 0e 70 10 17 58 9a 51 87 dc 7e a8 41 d1 + 56 f2 ec 0e 36 ad 52 a4 4d fe b1 e6 1f 7a d9 91 + d8 c5 10 56 ff ed b1 62 b4 c0 f2 83 a1 2a 88 a3 + 94 df f5 26 ab 72 91 cb b3 07 ce ab fc e0 b1 df + d5 cd 95 08 09 6d 5b 2b 8b 6d f5 d6 71 ef 63 77 + c0 92 1c b2 3c 27 0a 70 e2 59 8e 6f f8 9d 19 f1 + 05 ac c2 d3 f0 cb 35 f2 92 80 e1 38 6b 6f 64 c4 + ef 22 e1 e1 f2 0d 0c e8 cf fb 22 49 bd 9a 21 37''', + 'e':'''01 00 01''', + 'd':'''33 a5 04 2a 90 b2 7d 4f 54 51 ca 9b bb d0 b4 47 + 71 a1 01 af 88 43 40 ae f9 88 5f 2a 4b be 92 e8 + 94 a7 24 ac 3c 56 8c 8f 97 85 3a d0 7c 02 66 c8 + c6 a3 ca 09 29 f1 e8 f1 12 31 88 44 29 fc 4d 9a + e5 5f ee 89 6a 10 ce 70 7c 3e d7 e7 34 e4 47 27 + a3 95 74 50 1a 53 26 83 10 9c 2a ba ca ba 28 3c + 31 b4 bd 2f 53 c3 ee 37 e3 52 ce e3 4f 9e 50 3b + d8 0c 06 22 ad 79 c6 dc ee 88 35 47 c6 a3 b3 25''' + }, + # Message + '''cd c8 7d a2 23 d7 86 df 3b 45 e0 bb bc 72 13 26 + d1 ee 2a f8 06 cc 31 54 75 cc 6f 0d 9c 66 e1 b6 + 23 71 d4 5c e2 39 2e 1a c9 28 44 c3 10 10 2f 15 + 6a 0d 8d 52 c1 f4 c4 0b a3 aa 65 09 57 86 cb 76 + 97 57 a6 56 3b a9 58 fe d0 bc c9 84 e8 b5 17 a3 + d5 f5 15 b2 3b 8a 41 e7 4a a8 67 69 3f 90 df b0 + 61 a6 e8 6d fa ae e6 44 72 c0 0e 5f 20 94 57 29 + cb eb e7 7f 06 ce 78 e0 8f 40 98 fb a4 1f 9d 61 + 93 c0 31 7e 8b 60 d4 b6 08 4a cb 42 d2 9e 38 08 + a3 bc 37 2d 85 e3 31 17 0f cb f7 cc 72 d0 b7 1c + 29 66 48 b3 a4 d1 0f 41 62 95 d0 80 7a a6 25 ca + b2 74 4f d9 ea 8f d2 23 c4 25 37 02 98 28 bd 16 + be 02 54 6f 13 0f d2 e3 3b 93 6d 26 76 e0 8a ed + 1b 73 31 8b 75 0a 01 67 d0''', + # Signature + '''90 74 30 8f b5 98 e9 70 1b 22 94 38 8e 52 f9 71 + fa ac 2b 60 a5 14 5a f1 85 df 52 87 b5 ed 28 87 + e5 7c e7 fd 44 dc 86 34 e4 07 c8 e0 e4 36 0b c2 + 26 f3 ec 22 7f 9d 9e 54 63 8e 8d 31 f5 05 12 15 + df 6e bb 9c 2f 95 79 aa 77 59 8a 38 f9 14 b5 b9 + c1 bd 83 c4 e2 f9 f3 82 a0 d0 aa 35 42 ff ee 65 + 98 4a 60 1b c6 9e b2 8d eb 27 dc a1 2c 82 c2 d4 + c3 f6 6c d5 00 f1 ff 2b 99 4d 8a 4e 30 cb b3 3c''', + # Salt + '''de e9 59 c7 e0 64 11 36 14 20 ff 80 18 5e d5 7f + 3e 67 76 af''', + # Hash + SHA + ), + + # + # Example 1.2 to be found in + # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip + # + ( + # Private key + { + 'n':'''a5 6e 4a 0e 70 10 17 58 9a 51 87 dc 7e a8 41 d1 + 56 f2 ec 0e 36 ad 52 a4 4d fe b1 e6 1f 7a d9 91 + d8 c5 10 56 ff ed b1 62 b4 c0 f2 83 a1 2a 88 a3 + 94 df f5 26 ab 72 91 cb b3 07 ce ab fc e0 b1 df + d5 cd 95 08 09 6d 5b 2b 8b 6d f5 d6 71 ef 63 77 + c0 92 1c b2 3c 27 0a 70 e2 59 8e 6f f8 9d 19 f1 + 05 ac c2 d3 f0 cb 35 f2 92 80 e1 38 6b 6f 64 c4 + ef 22 e1 e1 f2 0d 0c e8 cf fb 22 49 bd 9a 21 37''', + 'e':'''01 00 01''', + 'd':'''33 a5 04 2a 90 b2 7d 4f 54 51 ca 9b bb d0 b4 47 + 71 a1 01 af 88 43 40 ae f9 88 5f 2a 4b be 92 e8 + 94 a7 24 ac 3c 56 8c 8f 97 85 3a d0 7c 02 66 c8 + c6 a3 ca 09 29 f1 e8 f1 12 31 88 44 29 fc 4d 9a + e5 5f ee 89 6a 10 ce 70 7c 3e d7 e7 34 e4 47 27 + a3 95 74 50 1a 53 26 83 10 9c 2a ba ca ba 28 3c + 31 b4 bd 2f 53 c3 ee 37 e3 52 ce e3 4f 9e 50 3b + d8 0c 06 22 ad 79 c6 dc ee 88 35 47 c6 a3 b3 25''' + }, + # Message + '''85 13 84 cd fe 81 9c 22 ed 6c 4c cb 30 da eb 5c + f0 59 bc 8e 11 66 b7 e3 53 0c 4c 23 3e 2b 5f 8f + 71 a1 cc a5 82 d4 3e cc 72 b1 bc a1 6d fc 70 13 + 22 6b 9e''', + # Signature + '''3e f7 f4 6e 83 1b f9 2b 32 27 41 42 a5 85 ff ce + fb dc a7 b3 2a e9 0d 10 fb 0f 0c 72 99 84 f0 4e + f2 9a 9d f0 78 07 75 ce 43 73 9b 97 83 83 90 db + 0a 55 05 e6 3d e9 27 02 8d 9d 29 b2 19 ca 2c 45 + 17 83 25 58 a5 5d 69 4a 6d 25 b9 da b6 60 03 c4 + cc cd 90 78 02 19 3b e5 17 0d 26 14 7d 37 b9 35 + 90 24 1b e5 1c 25 05 5f 47 ef 62 75 2c fb e2 14 + 18 fa fe 98 c2 2c 4d 4d 47 72 4f db 56 69 e8 43''', + # Salt + '''ef 28 69 fa 40 c3 46 cb 18 3d ab 3d 7b ff c9 8f + d5 6d f4 2d''', + # Hash + SHA + ), + + # + # Example 2.1 to be found in + # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip + # + ( + # Private key + { + 'n':'''01 d4 0c 1b cf 97 a6 8a e7 cd bd 8a 7b f3 e3 4f + a1 9d cc a4 ef 75 a4 74 54 37 5f 94 51 4d 88 fe + d0 06 fb 82 9f 84 19 ff 87 d6 31 5d a6 8a 1f f3 + a0 93 8e 9a bb 34 64 01 1c 30 3a d9 91 99 cf 0c + 7c 7a 8b 47 7d ce 82 9e 88 44 f6 25 b1 15 e5 e9 + c4 a5 9c f8 f8 11 3b 68 34 33 6a 2f d2 68 9b 47 + 2c bb 5e 5c ab e6 74 35 0c 59 b6 c1 7e 17 68 74 + fb 42 f8 fc 3d 17 6a 01 7e dc 61 fd 32 6c 4b 33 + c9''', + 'e':'''01 00 01''', + 'd':'''02 7d 14 7e 46 73 05 73 77 fd 1e a2 01 56 57 72 + 17 6a 7d c3 83 58 d3 76 04 56 85 a2 e7 87 c2 3c + 15 57 6b c1 6b 9f 44 44 02 d6 bf c5 d9 8a 3e 88 + ea 13 ef 67 c3 53 ec a0 c0 dd ba 92 55 bd 7b 8b + b5 0a 64 4a fd fd 1d d5 16 95 b2 52 d2 2e 73 18 + d1 b6 68 7a 1c 10 ff 75 54 5f 3d b0 fe 60 2d 5f + 2b 7f 29 4e 36 01 ea b7 b9 d1 ce cd 76 7f 64 69 + 2e 3e 53 6c a2 84 6c b0 c2 dd 48 6a 39 fa 75 b1''' + }, + # Message + '''da ba 03 20 66 26 3f ae db 65 98 48 11 52 78 a5 + 2c 44 fa a3 a7 6f 37 51 5e d3 36 32 10 72 c4 0a + 9d 9b 53 bc 05 01 40 78 ad f5 20 87 51 46 aa e7 + 0f f0 60 22 6d cb 7b 1f 1f c2 7e 93 60''', + # Signature + '''01 4c 5b a5 33 83 28 cc c6 e7 a9 0b f1 c0 ab 3f + d6 06 ff 47 96 d3 c1 2e 4b 63 9e d9 13 6a 5f ec + 6c 16 d8 88 4b dd 99 cf dc 52 14 56 b0 74 2b 73 + 68 68 cf 90 de 09 9a db 8d 5f fd 1d ef f3 9b a4 + 00 7a b7 46 ce fd b2 2d 7d f0 e2 25 f5 46 27 dc + 65 46 61 31 72 1b 90 af 44 53 63 a8 35 8b 9f 60 + 76 42 f7 8f ab 0a b0 f4 3b 71 68 d6 4b ae 70 d8 + 82 78 48 d8 ef 1e 42 1c 57 54 dd f4 2c 25 89 b5 + b3''', + # Salt + '''57 bf 16 0b cb 02 bb 1d c7 28 0c f0 45 85 30 b7 + d2 83 2f f7''', + SHA + ), + + # + # Example 8.1 to be found in + # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip + # + ( + # Private key + { + 'n':'''49 53 70 a1 fb 18 54 3c 16 d3 63 1e 31 63 25 5d + f6 2b e6 ee e8 90 d5 f2 55 09 e4 f7 78 a8 ea 6f + bb bc df 85 df f6 4e 0d 97 20 03 ab 36 81 fb ba + 6d d4 1f d5 41 82 9b 2e 58 2d e9 f2 a4 a4 e0 a2 + d0 90 0b ef 47 53 db 3c ee 0e e0 6c 7d fa e8 b1 + d5 3b 59 53 21 8f 9c ce ea 69 5b 08 66 8e de aa + dc ed 94 63 b1 d7 90 d5 eb f2 7e 91 15 b4 6c ad + 4d 9a 2b 8e fa b0 56 1b 08 10 34 47 39 ad a0 73 + 3f''', + 'e':'''01 00 01''', + 'd':'''6c 66 ff e9 89 80 c3 8f cd ea b5 15 98 98 83 61 + 65 f4 b4 b8 17 c4 f6 a8 d4 86 ee 4e a9 13 0f e9 + b9 09 2b d1 36 d1 84 f9 5f 50 4a 60 7e ac 56 58 + 46 d2 fd d6 59 7a 89 67 c7 39 6e f9 5a 6e ee bb + 45 78 a6 43 96 6d ca 4d 8e e3 de 84 2d e6 32 79 + c6 18 15 9c 1a b5 4a 89 43 7b 6a 61 20 e4 93 0a + fb 52 a4 ba 6c ed 8a 49 47 ac 64 b3 0a 34 97 cb + e7 01 c2 d6 26 6d 51 72 19 ad 0e c6 d3 47 db e9''' + }, + # Message + '''81 33 2f 4b e6 29 48 41 5e a1 d8 99 79 2e ea cf + 6c 6e 1d b1 da 8b e1 3b 5c ea 41 db 2f ed 46 70 + 92 e1 ff 39 89 14 c7 14 25 97 75 f5 95 f8 54 7f + 73 56 92 a5 75 e6 92 3a f7 8f 22 c6 99 7d db 90 + fb 6f 72 d7 bb 0d d5 74 4a 31 de cd 3d c3 68 58 + 49 83 6e d3 4a ec 59 63 04 ad 11 84 3c 4f 88 48 + 9f 20 97 35 f5 fb 7f da f7 ce c8 ad dc 58 18 16 + 8f 88 0a cb f4 90 d5 10 05 b7 a8 e8 4e 43 e5 42 + 87 97 75 71 dd 99 ee a4 b1 61 eb 2d f1 f5 10 8f + 12 a4 14 2a 83 32 2e db 05 a7 54 87 a3 43 5c 9a + 78 ce 53 ed 93 bc 55 08 57 d7 a9 fb''', + # Signature + '''02 62 ac 25 4b fa 77 f3 c1 ac a2 2c 51 79 f8 f0 + 40 42 2b 3c 5b af d4 0a 8f 21 cf 0f a5 a6 67 cc + d5 99 3d 42 db af b4 09 c5 20 e2 5f ce 2b 1e e1 + e7 16 57 7f 1e fa 17 f3 da 28 05 2f 40 f0 41 9b + 23 10 6d 78 45 aa f0 11 25 b6 98 e7 a4 df e9 2d + 39 67 bb 00 c4 d0 d3 5b a3 55 2a b9 a8 b3 ee f0 + 7c 7f ec db c5 42 4a c4 db 1e 20 cb 37 d0 b2 74 + 47 69 94 0e a9 07 e1 7f bb ca 67 3b 20 52 23 80 + c5''', + # Salt + '''1d 65 49 1d 79 c8 64 b3 73 00 9b e6 f6 f2 46 7b + ac 4c 78 fa''', + SHA + ) + ) + + def testSign1(self): + for i in range(len(self._testData)): + # Build the key + comps = [ long(rws(self._testData[i][0][x]),16) for x in ('n','e','d') ] + key = MyKey(RSA.construct(comps)) + # Hash function + h = self._testData[i][4].new() + # Data to sign + h.update(t2b(self._testData[i][1])) + # Salt + test_salt = t2b(self._testData[i][3]) + key._randfunc = lambda N: test_salt + # The real test + signer = PKCS.new(key) + self.failUnless(signer.can_sign()) + s = signer.sign(h) + self.assertEqual(s, t2b(self._testData[i][2])) + + def testVerify1(self): + for i in range(len(self._testData)): + # Build the key + comps = [ long(rws(self._testData[i][0][x]),16) for x in ('n','e') ] + key = MyKey(RSA.construct(comps)) + # Hash function + h = self._testData[i][4].new() + # Data to sign + h.update(t2b(self._testData[i][1])) + # Salt + test_salt = t2b(self._testData[i][3]) + # The real test + key._randfunc = lambda N: test_salt + verifier = PKCS.new(key) + self.failIf(verifier.can_sign()) + result = verifier.verify(h, t2b(self._testData[i][2])) + self.failUnless(result) + + def testSignVerify(self): + h = SHA.new() + h.update(b('blah blah blah')) + + rng = Random.new().read + key = MyKey(RSA.generate(1024,rng)) + + # Helper function to monitor what's request from MGF + global mgfcalls + def newMGF(seed,maskLen): + global mgfcalls + mgfcalls += 1 + return bchr(0x00)*maskLen + + # Verify that PSS is friendly to all ciphers + for hashmod in (MD2,MD5,SHA,SHA224,SHA256,SHA384,RIPEMD): + h = hashmod.new() + h.update(b('blah blah blah')) + + # Verify that sign() asks for as many random bytes + # as the hash output size + key.asked = 0 + signer = PKCS.new(key) + s = signer.sign(h) + self.failUnless(signer.verify(h, s)) + self.assertEqual(key.asked, h.digest_size) + + h = SHA.new() + h.update(b('blah blah blah')) + + # Verify that sign() uses a different salt length + for sLen in (0,3,21): + key.asked = 0 + signer = PKCS.new(key, saltLen=sLen) + s = signer.sign(h) + self.assertEqual(key.asked, sLen) + self.failUnless(signer.verify(h, s)) + + # Verify that sign() uses the custom MGF + mgfcalls = 0 + signer = PKCS.new(key, newMGF) + s = signer.sign(h) + self.assertEqual(mgfcalls, 1) + self.failUnless(signer.verify(h, s)) + + # Verify that sign() does not call the RNG + # when salt length is 0, even when a new MGF is provided + key.asked = 0 + mgfcalls = 0 + signer = PKCS.new(key, newMGF, 0) + s = signer.sign(h) + self.assertEqual(key.asked,0) + self.assertEqual(mgfcalls, 1) + self.failUnless(signer.verify(h, s)) + +def get_tests(config={}): + tests = [] + tests += list_test_cases(PKCS1_PSS_Tests) + return tests + +if __name__ == '__main__': + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Util/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Util/__init__.py new file mode 100644 index 0000000..abd640a --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Util/__init__.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Util/__init__.py: Self-test for utility modules +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test for utility modules""" + +__revision__ = "$Id$" + +import os + +def get_tests(config={}): + tests = [] + if os.name == 'nt': + from Crypto.SelfTest.Util import test_winrandom; tests += test_winrandom.get_tests(config=config) + from Crypto.SelfTest.Util import test_number; tests += test_number.get_tests(config=config) + from Crypto.SelfTest.Util import test_Counter; tests += test_Counter.get_tests(config=config) + return tests + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Util/test_Counter.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Util/test_Counter.py new file mode 100644 index 0000000..33c9bd7 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Util/test_Counter.py @@ -0,0 +1,165 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Util/test_Counter: Self-test for the Crypto.Util.Counter module +# +# Written in 2009 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-tests for Crypto.Util.Counter""" + +__revision__ = "$Id$" + +import sys +if sys.version_info[0] == 2 and sys.version_info[1] == 1: + from Crypto.Util.py21compat import * +from Crypto.Util.py3compat import * + +import unittest + +class CounterTests(unittest.TestCase): + def setUp(self): + global Counter + from Crypto.Util import Counter + + def test_BE_shortcut(self): + """Big endian, shortcut enabled""" + c = Counter.new(128) + self.assertEqual(c.__PCT_CTR_SHORTCUT__,True) # assert_ + c = Counter.new(128, little_endian=False) + self.assertEqual(c.__PCT_CTR_SHORTCUT__,True) # assert_ + c = Counter.new(128, disable_shortcut=False) + self.assertEqual(c.__PCT_CTR_SHORTCUT__,True) # assert_ + c = Counter.new(128, little_endian=False, disable_shortcut=False) + self.assertEqual(c.__PCT_CTR_SHORTCUT__,True) # assert_ + + def test_LE_shortcut(self): + """Little endian, shortcut enabled""" + c = Counter.new(128, little_endian=True) + self.assertEqual(c.__PCT_CTR_SHORTCUT__,True) # assert_ + c = Counter.new(128, little_endian=True, disable_shortcut=False) + self.assertEqual(c.__PCT_CTR_SHORTCUT__,True) # assert_ + + def test_BE_no_shortcut(self): + """Big endian, shortcut disabled""" + c = Counter.new(128, disable_shortcut=True) + self.assertRaises(AttributeError, getattr, c, '__PCT_CTR_SHORTCUT__') + c = Counter.new(128, little_endian=False, disable_shortcut=True) + self.assertRaises(AttributeError, getattr, c, '__PCT_CTR_SHORTCUT__') + + def test_LE_no_shortcut(self): + """Little endian, shortcut disabled""" + c = Counter.new(128, little_endian=True, disable_shortcut=True) + self.assertRaises(AttributeError, getattr, c, '__PCT_CTR_SHORTCUT__') + + def test_BE_defaults(self): + """128-bit, Big endian, defaults""" + c = Counter.new(128) + self.assertEqual(1, c.next_value()) + self.assertEqual(b("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"), c()) + self.assertEqual(2, c.next_value()) + self.assertEqual(b("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02"), c()) + for i in xrange(3, 256): + self.assertEqual(i, c.next_value()) + self.assertEqual(b("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")+bchr(i), c()) + self.assertEqual(256, c.next_value()) + self.assertEqual(b("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00"), c()) + + def test_LE_defaults(self): + """128-bit, Little endian, defaults""" + c = Counter.new(128, little_endian=True) + self.assertEqual(1, c.next_value()) + self.assertEqual(b("\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"), c()) + self.assertEqual(2, c.next_value()) + self.assertEqual(b("\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"), c()) + for i in xrange(3, 256): + self.assertEqual(i, c.next_value()) + self.assertEqual(bchr(i)+b("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"), c()) + self.assertEqual(256, c.next_value()) + self.assertEqual(b("\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"), c()) + + def test_BE8_wraparound(self): + """8-bit, Big endian, wraparound""" + c = Counter.new(8) + for i in xrange(1, 256): + self.assertEqual(i, c.next_value()) + self.assertEqual(bchr(i), c()) + self.assertRaises(OverflowError, c.next_value) + self.assertRaises(OverflowError, c) + self.assertRaises(OverflowError, c.next_value) + self.assertRaises(OverflowError, c) + + def test_LE8_wraparound(self): + """8-bit, Little endian, wraparound""" + c = Counter.new(8, little_endian=True) + for i in xrange(1, 256): + self.assertEqual(i, c.next_value()) + self.assertEqual(bchr(i), c()) + self.assertRaises(OverflowError, c.next_value) + self.assertRaises(OverflowError, c) + self.assertRaises(OverflowError, c.next_value) + self.assertRaises(OverflowError, c) + + def test_BE8_wraparound_allowed(self): + """8-bit, Big endian, wraparound with allow_wraparound=True""" + c = Counter.new(8, allow_wraparound=True) + for i in xrange(1, 256): + self.assertEqual(i, c.next_value()) + self.assertEqual(bchr(i), c()) + self.assertEqual(0, c.next_value()) + self.assertEqual(b("\x00"), c()) + self.assertEqual(1, c.next_value()) + + def test_LE8_wraparound_allowed(self): + """8-bit, Little endian, wraparound with allow_wraparound=True""" + c = Counter.new(8, little_endian=True, allow_wraparound=True) + for i in xrange(1, 256): + self.assertEqual(i, c.next_value()) + self.assertEqual(bchr(i), c()) + self.assertEqual(0, c.next_value()) + self.assertEqual(b("\x00"), c()) + self.assertEqual(1, c.next_value()) + + def test_BE8_carry(self): + """8-bit, Big endian, carry attribute""" + c = Counter.new(8) + for i in xrange(1, 256): + self.assertEqual(0, c.carry) + self.assertEqual(i, c.next_value()) + self.assertEqual(bchr(i), c()) + self.assertEqual(1, c.carry) + + def test_LE8_carry(self): + """8-bit, Little endian, carry attribute""" + c = Counter.new(8, little_endian=True) + for i in xrange(1, 256): + self.assertEqual(0, c.carry) + self.assertEqual(i, c.next_value()) + self.assertEqual(bchr(i), c()) + self.assertEqual(1, c.carry) + +def get_tests(config={}): + from Crypto.SelfTest.st_common import list_test_cases + return list_test_cases(CounterTests) + +if __name__ == '__main__': + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Util/test_asn1.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Util/test_asn1.py new file mode 100644 index 0000000..578dabe --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Util/test_asn1.py @@ -0,0 +1,293 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Util/test_asn.py: Self-test for the Crypto.Util.asn1 module +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-tests for Crypto.Util.asn1""" + +__revision__ = "$Id$" + +import unittest +import sys + +from Crypto.Util.py3compat import * +from Crypto.Util.asn1 import DerSequence, DerObject + +class DerObjectTests(unittest.TestCase): + + def testObjEncode1(self): + # No payload + der = DerObject(b('\x33')) + self.assertEquals(der.encode(), b('\x33\x00')) + # Small payload + der.payload = b('\x45') + self.assertEquals(der.encode(), b('\x33\x01\x45')) + # Invariant + self.assertEquals(der.encode(), b('\x33\x01\x45')) + # Initialize with numerical tag + der = DerObject(b(0x33)) + der.payload = b('\x45') + self.assertEquals(der.encode(), b('\x33\x01\x45')) + + def testObjEncode2(self): + # Known types + der = DerObject('SEQUENCE') + self.assertEquals(der.encode(), b('\x30\x00')) + der = DerObject('BIT STRING') + self.assertEquals(der.encode(), b('\x03\x00')) + + def testObjEncode3(self): + # Long payload + der = DerObject(b('\x34')) + der.payload = b("0")*128 + self.assertEquals(der.encode(), b('\x34\x81\x80' + "0"*128)) + + def testObjDecode1(self): + # Decode short payload + der = DerObject() + der.decode(b('\x20\x02\x01\x02')) + self.assertEquals(der.payload, b("\x01\x02")) + self.assertEquals(der.typeTag, 0x20) + + def testObjDecode2(self): + # Decode short payload + der = DerObject() + der.decode(b('\x22\x81\x80' + "1"*128)) + self.assertEquals(der.payload, b("1")*128) + self.assertEquals(der.typeTag, 0x22) + +class DerSequenceTests(unittest.TestCase): + + def testEncode1(self): + # Empty sequence + der = DerSequence() + self.assertEquals(der.encode(), b('0\x00')) + self.failIf(der.hasOnlyInts()) + # One single-byte integer (zero) + der.append(0) + self.assertEquals(der.encode(), b('0\x03\x02\x01\x00')) + self.failUnless(der.hasOnlyInts()) + # Invariant + self.assertEquals(der.encode(), b('0\x03\x02\x01\x00')) + + def testEncode2(self): + # One single-byte integer (non-zero) + der = DerSequence() + der.append(127) + self.assertEquals(der.encode(), b('0\x03\x02\x01\x7f')) + # Indexing + der[0] = 1 + self.assertEquals(len(der),1) + self.assertEquals(der[0],1) + self.assertEquals(der[-1],1) + self.assertEquals(der.encode(), b('0\x03\x02\x01\x01')) + # + der[:] = [1] + self.assertEquals(len(der),1) + self.assertEquals(der[0],1) + self.assertEquals(der.encode(), b('0\x03\x02\x01\x01')) + + def testEncode3(self): + # One multi-byte integer (non-zero) + der = DerSequence() + der.append(0x180L) + self.assertEquals(der.encode(), b('0\x04\x02\x02\x01\x80')) + + def testEncode4(self): + # One very long integer + der = DerSequence() + der.append(2**2048) + self.assertEquals(der.encode(), b('0\x82\x01\x05')+ + b('\x02\x82\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00')) + + def testEncode5(self): + # One single-byte integer (looks negative) + der = DerSequence() + der.append(0xFFL) + self.assertEquals(der.encode(), b('0\x04\x02\x02\x00\xff')) + + def testEncode6(self): + # Two integers + der = DerSequence() + der.append(0x180L) + der.append(0xFFL) + self.assertEquals(der.encode(), b('0\x08\x02\x02\x01\x80\x02\x02\x00\xff')) + self.failUnless(der.hasOnlyInts()) + # + der.append(0x01) + der[1:] = [9,8] + self.assertEquals(len(der),3) + self.assertEqual(der[1:],[9,8]) + self.assertEqual(der[1:-1],[9]) + self.assertEquals(der.encode(), b('0\x0A\x02\x02\x01\x80\x02\x01\x09\x02\x01\x08')) + + def testEncode6(self): + # One integer and another type (no matter what it is) + der = DerSequence() + der.append(0x180L) + der.append(b('\x00\x02\x00\x00')) + self.assertEquals(der.encode(), b('0\x08\x02\x02\x01\x80\x00\x02\x00\x00')) + self.failIf(der.hasOnlyInts()) + + #### + + def testDecode1(self): + # Empty sequence + der = DerSequence() + der.decode(b('0\x00')) + self.assertEquals(len(der),0) + # One single-byte integer (zero) + der.decode(b('0\x03\x02\x01\x00')) + self.assertEquals(len(der),1) + self.assertEquals(der[0],0) + # Invariant + der.decode(b('0\x03\x02\x01\x00')) + self.assertEquals(len(der),1) + self.assertEquals(der[0],0) + + def testDecode2(self): + # One single-byte integer (non-zero) + der = DerSequence() + der.decode(b('0\x03\x02\x01\x7f')) + self.assertEquals(len(der),1) + self.assertEquals(der[0],127) + + def testDecode3(self): + # One multi-byte integer (non-zero) + der = DerSequence() + der.decode(b('0\x04\x02\x02\x01\x80')) + self.assertEquals(len(der),1) + self.assertEquals(der[0],0x180L) + + def testDecode4(self): + # One very long integer + der = DerSequence() + der.decode(b('0\x82\x01\x05')+ + b('\x02\x82\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00')) + self.assertEquals(len(der),1) + self.assertEquals(der[0],2**2048) + + def testDecode5(self): + # One single-byte integer (looks negative) + der = DerSequence() + der.decode(b('0\x04\x02\x02\x00\xff')) + self.assertEquals(len(der),1) + self.assertEquals(der[0],0xFFL) + + def testDecode6(self): + # Two integers + der = DerSequence() + der.decode(b('0\x08\x02\x02\x01\x80\x02\x02\x00\xff')) + self.assertEquals(len(der),2) + self.assertEquals(der[0],0x180L) + self.assertEquals(der[1],0xFFL) + + def testDecode7(self): + # One integer and 2 other types + der = DerSequence() + der.decode(b('0\x0A\x02\x02\x01\x80\x24\x02\xb6\x63\x12\x00')) + self.assertEquals(len(der),3) + self.assertEquals(der[0],0x180L) + self.assertEquals(der[1],b('\x24\x02\xb6\x63')) + self.assertEquals(der[2],b('\x12\x00')) + + def testDecode8(self): + # Only 2 other types + der = DerSequence() + der.decode(b('0\x06\x24\x02\xb6\x63\x12\x00')) + self.assertEquals(len(der),2) + self.assertEquals(der[0],b('\x24\x02\xb6\x63')) + self.assertEquals(der[1],b('\x12\x00')) + + def testErrDecode1(self): + # Not a sequence + der = DerSequence() + self.assertRaises(ValueError, der.decode, b('')) + self.assertRaises(ValueError, der.decode, b('\x00')) + self.assertRaises(ValueError, der.decode, b('\x30')) + + def testErrDecode2(self): + # Wrong payload type + der = DerSequence() + self.assertRaises(ValueError, der.decode, b('\x30\x00\x00'), True) + + def testErrDecode3(self): + # Wrong length format + der = DerSequence() + self.assertRaises(ValueError, der.decode, b('\x30\x04\x02\x01\x01\x00')) + self.assertRaises(ValueError, der.decode, b('\x30\x81\x03\x02\x01\x01')) + self.assertRaises(ValueError, der.decode, b('\x30\x04\x02\x81\x01\x01')) + + def testErrDecode4(self): + # Wrong integer format + der = DerSequence() + # Multi-byte encoding for zero + #self.assertRaises(ValueError, der.decode, '\x30\x04\x02\x02\x00\x00') + # Negative integer + self.assertRaises(ValueError, der.decode, b('\x30\x04\x02\x01\xFF')) + +def get_tests(config={}): + from Crypto.SelfTest.st_common import list_test_cases + listTests = [] + listTests += list_test_cases(DerObjectTests) + listTests += list_test_cases(DerSequenceTests) + return listTests + +if __name__ == '__main__': + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Util/test_number.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Util/test_number.py new file mode 100644 index 0000000..0502e9e --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Util/test_number.py @@ -0,0 +1,295 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Util/test_number.py: Self-test for parts of the Crypto.Util.number module +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-tests for (some of) Crypto.Util.number""" + +__revision__ = "$Id$" + +import sys +if sys.version_info[0] == 2 and sys.version_info[1] == 1: + from Crypto.Util.py21compat import * + +import unittest + +# NB: In some places, we compare tuples instead of just output values so that +# if any inputs cause a test failure, we'll be able to tell which ones. + +class MiscTests(unittest.TestCase): + def setUp(self): + global number, math + from Crypto.Util import number + import math + + def test_ceil_shift(self): + """Util.number.ceil_shift""" + self.assertRaises(AssertionError, number.ceil_shift, -1, 1) + self.assertRaises(AssertionError, number.ceil_shift, 1, -1) + + # b = 0 + self.assertEqual(0, number.ceil_shift(0, 0)) + self.assertEqual(1, number.ceil_shift(1, 0)) + self.assertEqual(2, number.ceil_shift(2, 0)) + self.assertEqual(3, number.ceil_shift(3, 0)) + + # b = 1 + self.assertEqual(0, number.ceil_shift(0, 1)) + self.assertEqual(1, number.ceil_shift(1, 1)) + self.assertEqual(1, number.ceil_shift(2, 1)) + self.assertEqual(2, number.ceil_shift(3, 1)) + + # b = 2 + self.assertEqual(0, number.ceil_shift(0, 2)) + self.assertEqual(1, number.ceil_shift(1, 2)) + self.assertEqual(1, number.ceil_shift(2, 2)) + self.assertEqual(1, number.ceil_shift(3, 2)) + self.assertEqual(1, number.ceil_shift(4, 2)) + self.assertEqual(2, number.ceil_shift(5, 2)) + self.assertEqual(2, number.ceil_shift(6, 2)) + self.assertEqual(2, number.ceil_shift(7, 2)) + self.assertEqual(2, number.ceil_shift(8, 2)) + self.assertEqual(3, number.ceil_shift(9, 2)) + + for b in range(3, 1+129, 3): # 3, 6, ... , 129 + self.assertEqual(0, number.ceil_shift(0, b)) + + n = 1L + while n <= 2L**(b+2): + (q, r) = divmod(n-1, 2L**b) + expected = q + int(not not r) + self.assertEqual((n-1, b, expected), + (n-1, b, number.ceil_shift(n-1, b))) + + (q, r) = divmod(n, 2L**b) + expected = q + int(not not r) + self.assertEqual((n, b, expected), + (n, b, number.ceil_shift(n, b))) + + (q, r) = divmod(n+1, 2L**b) + expected = q + int(not not r) + self.assertEqual((n+1, b, expected), + (n+1, b, number.ceil_shift(n+1, b))) + + n *= 2 + + def test_ceil_div(self): + """Util.number.ceil_div""" + self.assertRaises(TypeError, number.ceil_div, "1", 1) + self.assertRaises(ZeroDivisionError, number.ceil_div, 1, 0) + self.assertRaises(ZeroDivisionError, number.ceil_div, -1, 0) + + # b = -1 + self.assertEqual(0, number.ceil_div(0, -1)) + self.assertEqual(-1, number.ceil_div(1, -1)) + self.assertEqual(-2, number.ceil_div(2, -1)) + self.assertEqual(-3, number.ceil_div(3, -1)) + + # b = 1 + self.assertEqual(0, number.ceil_div(0, 1)) + self.assertEqual(1, number.ceil_div(1, 1)) + self.assertEqual(2, number.ceil_div(2, 1)) + self.assertEqual(3, number.ceil_div(3, 1)) + + # b = 2 + self.assertEqual(0, number.ceil_div(0, 2)) + self.assertEqual(1, number.ceil_div(1, 2)) + self.assertEqual(1, number.ceil_div(2, 2)) + self.assertEqual(2, number.ceil_div(3, 2)) + self.assertEqual(2, number.ceil_div(4, 2)) + self.assertEqual(3, number.ceil_div(5, 2)) + + # b = 3 + self.assertEqual(0, number.ceil_div(0, 3)) + self.assertEqual(1, number.ceil_div(1, 3)) + self.assertEqual(1, number.ceil_div(2, 3)) + self.assertEqual(1, number.ceil_div(3, 3)) + self.assertEqual(2, number.ceil_div(4, 3)) + self.assertEqual(2, number.ceil_div(5, 3)) + self.assertEqual(2, number.ceil_div(6, 3)) + self.assertEqual(3, number.ceil_div(7, 3)) + + # b = 4 + self.assertEqual(0, number.ceil_div(0, 4)) + self.assertEqual(1, number.ceil_div(1, 4)) + self.assertEqual(1, number.ceil_div(2, 4)) + self.assertEqual(1, number.ceil_div(3, 4)) + self.assertEqual(1, number.ceil_div(4, 4)) + self.assertEqual(2, number.ceil_div(5, 4)) + self.assertEqual(2, number.ceil_div(6, 4)) + self.assertEqual(2, number.ceil_div(7, 4)) + self.assertEqual(2, number.ceil_div(8, 4)) + self.assertEqual(3, number.ceil_div(9, 4)) + + # b = -4 + self.assertEqual(3, number.ceil_div(-9, -4)) + self.assertEqual(2, number.ceil_div(-8, -4)) + self.assertEqual(2, number.ceil_div(-7, -4)) + self.assertEqual(2, number.ceil_div(-6, -4)) + self.assertEqual(2, number.ceil_div(-5, -4)) + self.assertEqual(1, number.ceil_div(-4, -4)) + self.assertEqual(1, number.ceil_div(-3, -4)) + self.assertEqual(1, number.ceil_div(-2, -4)) + self.assertEqual(1, number.ceil_div(-1, -4)) + self.assertEqual(0, number.ceil_div(0, -4)) + self.assertEqual(0, number.ceil_div(1, -4)) + self.assertEqual(0, number.ceil_div(2, -4)) + self.assertEqual(0, number.ceil_div(3, -4)) + self.assertEqual(-1, number.ceil_div(4, -4)) + self.assertEqual(-1, number.ceil_div(5, -4)) + self.assertEqual(-1, number.ceil_div(6, -4)) + self.assertEqual(-1, number.ceil_div(7, -4)) + self.assertEqual(-2, number.ceil_div(8, -4)) + self.assertEqual(-2, number.ceil_div(9, -4)) + + def test_exact_log2(self): + """Util.number.exact_log2""" + self.assertRaises(TypeError, number.exact_log2, "0") + self.assertRaises(ValueError, number.exact_log2, -1) + self.assertRaises(ValueError, number.exact_log2, 0) + self.assertEqual(0, number.exact_log2(1)) + self.assertEqual(1, number.exact_log2(2)) + self.assertRaises(ValueError, number.exact_log2, 3) + self.assertEqual(2, number.exact_log2(4)) + self.assertRaises(ValueError, number.exact_log2, 5) + self.assertRaises(ValueError, number.exact_log2, 6) + self.assertRaises(ValueError, number.exact_log2, 7) + e = 3 + n = 8 + while e < 16: + if n == 2**e: + self.assertEqual(e, number.exact_log2(n), "expected=2**%d, n=%d" % (e, n)) + e += 1 + else: + self.assertRaises(ValueError, number.exact_log2, n) + n += 1 + + for e in range(16, 1+64, 2): + self.assertRaises(ValueError, number.exact_log2, 2L**e-1) + self.assertEqual(e, number.exact_log2(2L**e)) + self.assertRaises(ValueError, number.exact_log2, 2L**e+1) + + def test_exact_div(self): + """Util.number.exact_div""" + + # Positive numbers + self.assertEqual(1, number.exact_div(1, 1)) + self.assertRaises(ValueError, number.exact_div, 1, 2) + self.assertEqual(1, number.exact_div(2, 2)) + self.assertRaises(ValueError, number.exact_div, 3, 2) + self.assertEqual(2, number.exact_div(4, 2)) + + # Negative numbers + self.assertEqual(-1, number.exact_div(-1, 1)) + self.assertEqual(-1, number.exact_div(1, -1)) + self.assertRaises(ValueError, number.exact_div, -1, 2) + self.assertEqual(1, number.exact_div(-2, -2)) + self.assertEqual(-2, number.exact_div(-4, 2)) + + # Zero dividend + self.assertEqual(0, number.exact_div(0, 1)) + self.assertEqual(0, number.exact_div(0, 2)) + + # Zero divisor (allow_divzero == False) + self.assertRaises(ZeroDivisionError, number.exact_div, 0, 0) + self.assertRaises(ZeroDivisionError, number.exact_div, 1, 0) + + # Zero divisor (allow_divzero == True) + self.assertEqual(0, number.exact_div(0, 0, allow_divzero=True)) + self.assertRaises(ValueError, number.exact_div, 1, 0, allow_divzero=True) + + def test_floor_div(self): + """Util.number.floor_div""" + self.assertRaises(TypeError, number.floor_div, "1", 1) + for a in range(-10, 10): + for b in range(-10, 10): + if b == 0: + self.assertRaises(ZeroDivisionError, number.floor_div, a, b) + else: + self.assertEqual((a, b, int(math.floor(float(a) / b))), + (a, b, number.floor_div(a, b))) + + def test_getStrongPrime(self): + """Util.number.getStrongPrime""" + self.assertRaises(ValueError, number.getStrongPrime, 256) + self.assertRaises(ValueError, number.getStrongPrime, 513) + bits = 512 + x = number.getStrongPrime(bits) + self.assertNotEqual(x % 2, 0) + self.assertEqual(x > (1L << bits-1)-1, 1) + self.assertEqual(x < (1L << bits), 1) + e = 2**16+1 + x = number.getStrongPrime(bits, e) + self.assertEqual(number.GCD(x-1, e), 1) + self.assertNotEqual(x % 2, 0) + self.assertEqual(x > (1L << bits-1)-1, 1) + self.assertEqual(x < (1L << bits), 1) + e = 2**16+2 + x = number.getStrongPrime(bits, e) + self.assertEqual(number.GCD((x-1)>>1, e), 1) + self.assertNotEqual(x % 2, 0) + self.assertEqual(x > (1L << bits-1)-1, 1) + self.assertEqual(x < (1L << bits), 1) + + def test_isPrime(self): + """Util.number.isPrime""" + self.assertEqual(number.isPrime(-3), False) # Regression test: negative numbers should not be prime + self.assertEqual(number.isPrime(-2), False) # Regression test: negative numbers should not be prime + self.assertEqual(number.isPrime(1), False) # Regression test: isPrime(1) caused some versions of PyCrypto to crash. + self.assertEqual(number.isPrime(2), True) + self.assertEqual(number.isPrime(3), True) + self.assertEqual(number.isPrime(4), False) + self.assertEqual(number.isPrime(2L**1279-1), True) + self.assertEqual(number.isPrime(-(2L**1279-1)), False) # Regression test: negative numbers should not be prime + # test some known gmp pseudo-primes taken from + # http://www.trnicely.net/misc/mpzspsp.html + for composite in (43 * 127 * 211, 61 * 151 * 211, 15259 * 30517, + 346141L * 692281L, 1007119L * 2014237L, 3589477L * 7178953L, + 4859419L * 9718837L, 2730439L * 5460877L, + 245127919L * 490255837L, 963939391L * 1927878781L, + 4186358431L * 8372716861L, 1576820467L * 3153640933L): + self.assertEqual(number.isPrime(long(composite)), False) + + def test_size(self): + self.assertEqual(number.size(2),2) + self.assertEqual(number.size(3),2) + self.assertEqual(number.size(0xa2),8) + self.assertEqual(number.size(0xa2ba40),8*3) + self.assertEqual(number.size(0xa2ba40ee07e3b2bd2f02ce227f36a195024486e49c19cb41bbbdfbba98b22b0e577c2eeaffa20d883a76e65e394c69d4b3c05a1e8fadda27edb2a42bc000fe888b9b32c22d15add0cd76b3e7936e19955b220dd17d4ea904b1ec102b2e4de7751222aa99151024c7cb41cc5ea21d00eeb41f7c800834d2c6e06bce3bce7ea9a5L), 1024) + + def test_negative_number_roundtrip_mpzToLongObj_longObjToMPZ(self): + """Test that mpzToLongObj and longObjToMPZ (internal functions) roundtrip negative numbers correctly.""" + n = -100000000000000000000000000000000000L + e = 2L + k = number._fastmath.rsa_construct(n, e) + self.assertEqual(n, k.n) + self.assertEqual(e, k.e) + +def get_tests(config={}): + from Crypto.SelfTest.st_common import list_test_cases + return list_test_cases(MiscTests) + +if __name__ == '__main__': + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Util/test_winrandom.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Util/test_winrandom.py new file mode 100644 index 0000000..3fc5145 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/Util/test_winrandom.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Util/test_winrandom.py: Self-test for the winrandom module +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self-test suite for Crypto.Util.winrandom""" + +__revision__ = "$Id$" + +import unittest + +class WinRandomImportTest(unittest.TestCase): + def runTest(self): + """winrandom: simple test""" + # Import the winrandom module and try to use it + from Crypto.Util import winrandom + randobj = winrandom.new() + x = randobj.get_bytes(16) + y = randobj.get_bytes(16) + self.assertNotEqual(x, y) + +def get_tests(config={}): + return [WinRandomImportTest()] + +if __name__ == '__main__': + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/__init__.py new file mode 100644 index 0000000..40b3969 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/__init__.py @@ -0,0 +1,92 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/__init__.py: Self-test for PyCrypto +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Self tests + +These tests should perform quickly and can ideally be used every time an +application runs. +""" + +__revision__ = "$Id$" + +import sys +import unittest +from StringIO import StringIO + +class SelfTestError(Exception): + def __init__(self, message, result): + Exception.__init__(self, message, result) + self.message = message + self.result = result + +def run(module=None, verbosity=0, stream=None, tests=None, config=None, **kwargs): + """Execute self-tests. + + This raises SelfTestError if any test is unsuccessful. + + You may optionally pass in a sub-module of SelfTest if you only want to + perform some of the tests. For example, the following would test only the + hash modules: + + Crypto.SelfTest.run(Crypto.SelfTest.Hash) + + """ + if config is None: + config = {} + suite = unittest.TestSuite() + if module is None: + if tests is None: + tests = get_tests(config=config) + suite.addTests(tests) + else: + if tests is None: + suite.addTests(module.get_tests(config=config)) + else: + raise ValueError("'module' and 'tests' arguments are mutually exclusive") + if stream is None: + kwargs['stream'] = StringIO() + runner = unittest.TextTestRunner(verbosity=verbosity, **kwargs) + result = runner.run(suite) + if not result.wasSuccessful(): + if stream is None: + sys.stderr.write(stream.getvalue()) + raise SelfTestError("Self-test failed", result) + return result + +def get_tests(config={}): + tests = [] + from Crypto.SelfTest import Cipher; tests += Cipher.get_tests(config=config) + from Crypto.SelfTest import Hash; tests += Hash.get_tests(config=config) + from Crypto.SelfTest import Protocol; tests += Protocol.get_tests(config=config) + from Crypto.SelfTest import PublicKey; tests += PublicKey.get_tests(config=config) + from Crypto.SelfTest import Random; tests += Random.get_tests(config=config) + from Crypto.SelfTest import Util; tests += Util.get_tests(config=config) + from Crypto.SelfTest import Signature; tests += Signature.get_tests(config=config) + return tests + +if __name__ == '__main__': + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/st_common.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/st_common.py new file mode 100644 index 0000000..c56eac5 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/SelfTest/st_common.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/st_common.py: Common functions for SelfTest modules +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Common functions for SelfTest modules""" + +__revision__ = "$Id$" + +import unittest +import binascii +import sys +if sys.version_info[0] == 2 and sys.version_info[1] == 1: + from Crypto.Util.py21compat import * +from Crypto.Util.py3compat import * + +class _list_testloader(unittest.TestLoader): + suiteClass = list + +def list_test_cases(class_): + """Return a list of TestCase instances given a TestCase class + + This is useful when you have defined test* methods on your TestCase class. + """ + return _list_testloader().loadTestsFromTestCase(class_) + +def strip_whitespace(s): + """Remove whitespace from a text or byte string""" + if isinstance(s,str): + return b("".join(s.split())) + else: + return b("").join(s.split()) + +def a2b_hex(s): + """Convert hexadecimal to binary, ignoring whitespace""" + return binascii.a2b_hex(strip_whitespace(s)) + +def b2a_hex(s): + """Convert binary to hexadecimal""" + # For completeness + return binascii.b2a_hex(s) + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Signature/PKCS1_PSS.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Signature/PKCS1_PSS.py new file mode 100644 index 0000000..4f50eb8 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Signature/PKCS1_PSS.py @@ -0,0 +1,355 @@ +# -*- coding: utf-8 -*- +# +# Signature/PKCS1_PSS.py : PKCS#1 PPS +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""RSA digital signature protocol with appendix according to PKCS#1 PSS. + +See RFC3447__ or the `original RSA Labs specification`__. + +This scheme is more properly called ``RSASSA-PSS``. + +For example, a sender may authenticate a message using SHA-1 and PSS like +this: + + >>> from Crypto.Signature import PKCS1_PSS + >>> from Crypto.Hash import SHA + >>> from Crypto.PublicKey import RSA + >>> from Crypto import Random + >>> + >>> message = 'To be signed' + >>> key = RSA.importKey(open('privkey.der').read()) + >>> h = SHA.new() + >>> h.update(message) + >>> signer = PKCS1_PSS.new(key) + >>> signature = PKCS1_PSS.sign(key) + +At the receiver side, verification can be done like using the public part of +the RSA key: + + >>> key = RSA.importKey(open('pubkey.der').read()) + >>> h = SHA.new() + >>> h.update(message) + >>> verifier = PKCS1_PSS.new(key) + >>> if verifier.verify(h, signature): + >>> print "The signature is authentic." + >>> else: + >>> print "The signature is not authentic." + +:undocumented: __revision__, __package__ + +.. __: http://www.ietf.org/rfc/rfc3447.txt +.. __: http://www.rsa.com/rsalabs/node.asp?id=2125 +""" + +# Allow nested scopes in Python 2.1 +# See http://oreilly.com/pub/a/python/2001/04/19/pythonnews.html +from __future__ import nested_scopes + +__revision__ = "$Id$" +__all__ = [ 'new', 'PSS_SigScheme' ] + +from Crypto.Util.py3compat import * +if sys.version_info[0] == 2 and sys.version_info[1] == 1: + from Crypto.Util.py21compat import * +import Crypto.Util.number +from Crypto.Util.number import ceil_shift, ceil_div, long_to_bytes +from Crypto.Util.strxor import strxor + +class PSS_SigScheme: + """This signature scheme can perform PKCS#1 PSS RSA signature or verification.""" + + def __init__(self, key, mgfunc, saltLen): + """Initialize this PKCS#1 PSS signature scheme object. + + :Parameters: + key : an RSA key object + If a private half is given, both signature and verification are possible. + If a public half is given, only verification is possible. + mgfunc : callable + A mask generation function that accepts two parameters: a string to + use as seed, and the lenth of the mask to generate, in bytes. + saltLen : int + Length of the salt, in bytes. + """ + self._key = key + self._saltLen = saltLen + self._mgfunc = mgfunc + + def can_sign(self): + """Return True if this cipher object can be used for signing messages.""" + return self._key.has_private() + + def sign(self, mhash): + """Produce the PKCS#1 PSS signature of a message. + + This function is named ``RSASSA-PSS-SIGN``, and is specified in + section 8.1.1 of RFC3447. + + :Parameters: + mhash : hash object + The hash that was carried out over the message. This is an object + belonging to the `Crypto.Hash` module. + + :Return: The PSS signature encoded as a string. + :Raise ValueError: + If the RSA key length is not sufficiently long to deal with the given + hash algorithm. + :Raise TypeError: + If the RSA key has no private half. + + :attention: Modify the salt length and the mask generation function only + if you know what you are doing. + The receiver must use the same parameters too. + """ + # TODO: Verify the key is RSA + + randfunc = self._key._randfunc + + # Set defaults for salt length and mask generation function + if self._saltLen == None: + sLen = mhash.digest_size + else: + sLen = self._saltLen + if self._mgfunc: + mgf = self._mgfunc + else: + mgf = lambda x,y: MGF1(x,y,mhash) + + modBits = Crypto.Util.number.size(self._key.n) + + # See 8.1.1 in RFC3447 + k = ceil_div(modBits,8) # Convert from bits to bytes + # Step 1 + em = EMSA_PSS_ENCODE(mhash, modBits-1, randfunc, mgf, sLen) + # Step 2a (OS2IP) and 2b (RSASP1) + m = self._key.decrypt(em) + # Step 2c (I2OSP) + S = bchr(0x00)*(k-len(m)) + m + return S + + def verify(self, mhash, S): + """Verify that a certain PKCS#1 PSS signature is authentic. + + This function checks if the party holding the private half of the given + RSA key has really signed the message. + + This function is called ``RSASSA-PSS-VERIFY``, and is specified in section + 8.1.2 of RFC3447. + + :Parameters: + mhash : hash object + The hash that was carried out over the message. This is an object + belonging to the `Crypto.Hash` module. + S : string + The signature that needs to be validated. + + :Return: True if verification is correct. False otherwise. + """ + # TODO: Verify the key is RSA + + # Set defaults for salt length and mask generation function + if self._saltLen == None: + sLen = mhash.digest_size + else: + sLen = self._saltLen + if self._mgfunc: + mgf = self._mgfunc + else: + mgf = lambda x,y: MGF1(x,y,mhash) + + modBits = Crypto.Util.number.size(self._key.n) + + # See 8.1.2 in RFC3447 + k = ceil_div(modBits,8) # Convert from bits to bytes + # Step 1 + if len(S) != k: + return False + # Step 2a (O2SIP), 2b (RSAVP1), and partially 2c (I2OSP) + # Note that signature must be smaller than the module + # but RSA.py won't complain about it. + # TODO: Fix RSA object; don't do it here. + em = self._key.encrypt(S, 0)[0] + # Step 2c + emLen = ceil_div(modBits-1,8) + em = bchr(0x00)*(emLen-len(em)) + em + # Step 3 + try: + result = EMSA_PSS_VERIFY(mhash, em, modBits-1, mgf, sLen) + except ValueError: + return False + # Step 4 + return result + +def MGF1(mgfSeed, maskLen, hash): + """Mask Generation Function, described in B.2.1""" + T = b("") + for counter in xrange(ceil_div(maskLen, hash.digest_size)): + c = long_to_bytes(counter, 4) + T = T + hash.new(mgfSeed + c).digest() + assert(len(T)>=maskLen) + return T[:maskLen] + +def EMSA_PSS_ENCODE(mhash, emBits, randFunc, mgf, sLen): + """ + Implement the ``EMSA-PSS-ENCODE`` function, as defined + in PKCS#1 v2.1 (RFC3447, 9.1.1). + + The original ``EMSA-PSS-ENCODE`` actually accepts the message ``M`` as input, + and hash it internally. Here, we expect that the message has already + been hashed instead. + + :Parameters: + mhash : hash object + The hash object that holds the digest of the message being signed. + emBits : int + Maximum length of the final encoding, in bits. + randFunc : callable + An RNG function that accepts as only parameter an int, and returns + a string of random bytes, to be used as salt. + mgf : callable + A mask generation function that accepts two parameters: a string to + use as seed, and the lenth of the mask to generate, in bytes. + sLen : int + Length of the salt, in bytes. + + :Return: An ``emLen`` byte long string that encodes the hash + (with ``emLen = \ceil(emBits/8)``). + + :Raise ValueError: + When digest or salt length are too big. + """ + + emLen = ceil_div(emBits,8) + + # Bitmask of digits that fill up + lmask = 0 + for i in xrange(8*emLen-emBits): + lmask = lmask>>1 | 0x80 + + # Step 1 and 2 have been already done + # Step 3 + if emLen < mhash.digest_size+sLen+2: + raise ValueError("Digest or salt length are too long for given key size.") + # Step 4 + salt = b("") + if randFunc and sLen>0: + salt = randFunc(sLen) + # Step 5 and 6 + h = mhash.new(bchr(0x00)*8 + mhash.digest() + salt) + # Step 7 and 8 + db = bchr(0x00)*(emLen-sLen-mhash.digest_size-2) + bchr(0x01) + salt + # Step 9 + dbMask = mgf(h.digest(), emLen-mhash.digest_size-1) + # Step 10 + maskedDB = strxor(db,dbMask) + # Step 11 + maskedDB = bchr(bord(maskedDB[0]) & ~lmask) + maskedDB[1:] + # Step 12 + em = maskedDB + h.digest() + bchr(0xBC) + return em + +def EMSA_PSS_VERIFY(mhash, em, emBits, mgf, sLen): + """ + Implement the ``EMSA-PSS-VERIFY`` function, as defined + in PKCS#1 v2.1 (RFC3447, 9.1.2). + + ``EMSA-PSS-VERIFY`` actually accepts the message ``M`` as input, + and hash it internally. Here, we expect that the message has already + been hashed instead. + + :Parameters: + mhash : hash object + The hash object that holds the digest of the message to be verified. + em : string + The signature to verify, therefore proving that the sender really signed + the message that was received. + emBits : int + Length of the final encoding (em), in bits. + mgf : callable + A mask generation function that accepts two parameters: a string to + use as seed, and the lenth of the mask to generate, in bytes. + sLen : int + Length of the salt, in bytes. + + :Return: 0 if the encoding is consistent, 1 if it is inconsistent. + + :Raise ValueError: + When digest or salt length are too big. + """ + + emLen = ceil_div(emBits,8) + + # Bitmask of digits that fill up + lmask = 0 + for i in xrange(8*emLen-emBits): + lmask = lmask>>1 | 0x80 + + # Step 1 and 2 have been already done + # Step 3 + if emLen < mhash.digest_size+sLen+2: + return False + # Step 4 + if ord(em[-1:])!=0xBC: + return False + # Step 5 + maskedDB = em[:emLen-mhash.digest_size-1] + h = em[emLen-mhash.digest_size-1:-1] + # Step 6 + if lmask & bord(em[0]): + return False + # Step 7 + dbMask = mgf(h, emLen-mhash.digest_size-1) + # Step 8 + db = strxor(maskedDB, dbMask) + # Step 9 + db = bchr(bord(db[0]) & ~lmask) + db[1:] + # Step 10 + if not db.startswith(bchr(0x00)*(emLen-mhash.digest_size-sLen-2) + bchr(0x01)): + return False + # Step 11 + salt = b("") + if sLen: salt = db[-sLen:] + # Step 12 and 13 + hp = mhash.new(bchr(0x00)*8 + mhash.digest() + salt).digest() + # Step 14 + if h!=hp: + return False + return True + +def new(key, mgfunc=None, saltLen=None): + """Return a signature scheme object `PSS_SigScheme` that + can be used to perform PKCS#1 PSS signature or verification. + + :Parameters: + key : RSA key object + The key to use to sign or verify the message. This is a `Crypto.PublicKey.RSA` object. + Signing is only possible if *key* is a private RSA key. + mgfunc : callable + A mask generation function that accepts two parameters: a string to + use as seed, and the lenth of the mask to generate, in bytes. + If not specified, the standard MGF1 is used. + saltLen : int + Length of the salt, in bytes. If not specified, it matches the output + size of the hash function. + + """ + return PSS_SigScheme(key, mgfunc, saltLen) + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Signature/PKCS1_v1_5.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Signature/PKCS1_v1_5.py new file mode 100644 index 0000000..73ac251 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Signature/PKCS1_v1_5.py @@ -0,0 +1,236 @@ +# -*- coding: utf-8 -*- +# +# Signature/PKCS1-v1_5.py : PKCS#1 v1.5 +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +""" +RSA digital signature protocol according to PKCS#1 v1.5 + +See RFC3447__ or the `original RSA Labs specification`__. + +This scheme is more properly called ``RSASSA-PKCS1-v1_5``. + +For example, a sender may authenticate a message using SHA-1 like +this: + + >>> from Crypto.Signature import PKCS1_v1_5 + >>> from Crypto.Hash import SHA + >>> from Crypto.PublicKey import RSA + >>> + >>> message = 'To be signed' + >>> key = RSA.importKey(open('privkey.der').read()) + >>> h = SHA.new(message) + >>> signer = PKCS1_v1_5.new(key) + >>> signature = signer.sign(h) + +At the receiver side, verification can be done using the public part of +the RSA key: + + >>> key = RSA.importKey(open('pubkey.der').read()) + >>> h = SHA.new(message) + >>> verifier = PKCS1_v1_5.new(key) + >>> if verifier.verify(h, signature): + >>> print "The signature is authentic." + >>> else: + >>> print "The signature is not authentic." + +:undocumented: __revision__, __package__ + +.. __: http://www.ietf.org/rfc/rfc3447.txt +.. __: http://www.rsa.com/rsalabs/node.asp?id=2125 +""" + +__revision__ = "$Id$" +__all__ = [ 'new', 'PKCS115_SigScheme' ] + +import Crypto.Util.number +from Crypto.Util.number import ceil_div +from Crypto.Util.asn1 import DerSequence, DerNull, DerOctetString +from Crypto.Util.py3compat import * + +class PKCS115_SigScheme: + """This signature scheme can perform PKCS#1 v1.5 RSA signature or verification.""" + + def __init__(self, key): + """Initialize this PKCS#1 v1.5 signature scheme object. + + :Parameters: + key : an RSA key object + If a private half is given, both signature and verification are possible. + If a public half is given, only verification is possible. + """ + self._key = key + + def can_sign(self): + """Return True if this cipher object can be used for signing messages.""" + return self._key.has_private() + + def sign(self, mhash): + """Produce the PKCS#1 v1.5 signature of a message. + + This function is named ``RSASSA-PKCS1-V1_5-SIGN``, and is specified in + section 8.2.1 of RFC3447. + + :Parameters: + mhash : hash object + The hash that was carried out over the message. This is an object + belonging to the `Crypto.Hash` module. + + :Return: The signature encoded as a string. + :Raise ValueError: + If the RSA key length is not sufficiently long to deal with the given + hash algorithm. + :Raise TypeError: + If the RSA key has no private half. + """ + # TODO: Verify the key is RSA + + # See 8.2.1 in RFC3447 + modBits = Crypto.Util.number.size(self._key.n) + k = ceil_div(modBits,8) # Convert from bits to bytes + + # Step 1 + em = EMSA_PKCS1_V1_5_ENCODE(mhash, k) + # Step 2a (OS2IP) and 2b (RSASP1) + m = self._key.decrypt(em) + # Step 2c (I2OSP) + S = bchr(0x00)*(k-len(m)) + m + return S + + def verify(self, mhash, S): + """Verify that a certain PKCS#1 v1.5 signature is authentic. + + This function checks if the party holding the private half of the key + really signed the message. + + This function is named ``RSASSA-PKCS1-V1_5-VERIFY``, and is specified in + section 8.2.2 of RFC3447. + + :Parameters: + mhash : hash object + The hash that was carried out over the message. This is an object + belonging to the `Crypto.Hash` module. + S : string + The signature that needs to be validated. + + :Return: True if verification is correct. False otherwise. + """ + # TODO: Verify the key is RSA + + # See 8.2.2 in RFC3447 + modBits = Crypto.Util.number.size(self._key.n) + k = ceil_div(modBits,8) # Convert from bits to bytes + + # Step 1 + if len(S) != k: + return 0 + # Step 2a (O2SIP) and 2b (RSAVP1) + # Note that signature must be smaller than the module + # but RSA.py won't complain about it. + # TODO: Fix RSA object; don't do it here. + m = self._key.encrypt(S, 0)[0] + # Step 2c (I2OSP) + em1 = bchr(0x00)*(k-len(m)) + m + # Step 3 + try: + em2 = EMSA_PKCS1_V1_5_ENCODE(mhash, k) + except ValueError: + return 0 + # Step 4 + # By comparing the full encodings (as opposed to checking each + # of its components one at a time) we avoid attacks to the padding + # scheme like Bleichenbacher's (see http://www.mail-archive.com/cryptography@metzdowd.com/msg06537). + # + return em1==em2 + +def EMSA_PKCS1_V1_5_ENCODE(hash, emLen): + """ + Implement the ``EMSA-PKCS1-V1_5-ENCODE`` function, as defined + in PKCS#1 v2.1 (RFC3447, 9.2). + + ``EMSA-PKCS1-V1_5-ENCODE`` actually accepts the message ``M`` as input, + and hash it internally. Here, we expect that the message has already + been hashed instead. + + :Parameters: + hash : hash object + The hash object that holds the digest of the message being signed. + emLen : int + The length the final encoding must have, in bytes. + + :attention: the early standard (RFC2313) stated that ``DigestInfo`` + had to be BER-encoded. This means that old signatures + might have length tags in indefinite form, which + is not supported in DER. Such encoding cannot be + reproduced by this function. + + :attention: the same standard defined ``DigestAlgorithm`` to be + of ``AlgorithmIdentifier`` type, where the PARAMETERS + item is optional. Encodings for ``MD2/4/5`` without + ``PARAMETERS`` cannot be reproduced by this function. + + :Return: An ``emLen`` byte long string that encodes the hash. + """ + + # First, build the ASN.1 DER object DigestInfo: + # + # DigestInfo ::= SEQUENCE { + # digestAlgorithm AlgorithmIdentifier, + # digest OCTET STRING + # } + # + # where digestAlgorithm identifies the hash function and shall be an + # algorithm ID with an OID in the set PKCS1-v1-5DigestAlgorithms. + # + # PKCS1-v1-5DigestAlgorithms ALGORITHM-IDENTIFIER ::= { + # { OID id-md2 PARAMETERS NULL }| + # { OID id-md5 PARAMETERS NULL }| + # { OID id-sha1 PARAMETERS NULL }| + # { OID id-sha256 PARAMETERS NULL }| + # { OID id-sha384 PARAMETERS NULL }| + # { OID id-sha512 PARAMETERS NULL } + # } + # + digestAlgo = DerSequence([hash.oid, DerNull().encode()]) + digest = DerOctetString(hash.digest()) + digestInfo = DerSequence([ + digestAlgo.encode(), + digest.encode() + ]).encode() + + # We need at least 11 bytes for the remaining data: 3 fixed bytes and + # at least 8 bytes of padding). + if emLen +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== +"""Fast counter functions for CTR cipher modes. + +CTR is a chaining mode for symmetric block encryption or decryption. +Messages are divideded into blocks, and the cipher operation takes +place on each block using the secret key and a unique *counter block*. + +The most straightforward way to fulfil the uniqueness property is +to start with an initial, random *counter block* value, and increment it as +the next block is processed. + +The block ciphers from `Crypto.Cipher` (when configured in *MODE_CTR* mode) +invoke a callable object (the *counter* parameter) to get the next *counter block*. +Unfortunately, the Python calling protocol leads to major performance degradations. + +The counter functions instantiated by this module will be invoked directly +by the ciphers in `Crypto.Cipher`. The fact that the Python layer is bypassed +lead to more efficient (and faster) execution of CTR cipher modes. + +An example of usage is the following: + + >>> from Crypto.Cipher import AES + >>> from Crypto.Util import Counter + >>> + >>> pt = b'\x00'*1000000 + >>> ctr = Counter.new(128) + >>> cipher = AES.new(b'\x00'*16, AES.MODE_CTR, counter=ctr) + >>> ct = cipher.encrypt(pt) + +:undocumented: __package__ +""" +import sys +if sys.version_info[0] == 2 and sys.version_info[1] == 1: + from Crypto.Util.py21compat import * +from Crypto.Util.py3compat import * + +from Crypto.Util import _counter +import struct + +# Factory function +def new(nbits, prefix=b(""), suffix=b(""), initial_value=1, overflow=0, little_endian=False, allow_wraparound=False, disable_shortcut=False): + """Create a stateful counter block function suitable for CTR encryption modes. + + Each call to the function returns the next counter block. + Each counter block is made up by three parts:: + + prefix || counter value || postfix + + The counter value is incremented by one at each call. + + :Parameters: + nbits : integer + Length of the desired counter, in bits. It must be a multiple of 8. + prefix : byte string + The constant prefix of the counter block. By default, no prefix is + used. + suffix : byte string + The constant postfix of the counter block. By default, no suffix is + used. + initial_value : integer + The initial value of the counter. Default value is 1. + little_endian : boolean + If True, the counter number will be encoded in little endian format. + If False (default), in big endian format. + allow_wraparound : boolean + If True, the function will raise an *OverflowError* exception as soon + as the counter wraps around. If False (default), the counter will + simply restart from zero. + disable_shortcut : boolean + If True, do not make ciphers from `Crypto.Cipher` bypass the Python + layer when invoking the counter block function. + If False (default), bypass the Python layer. + :Returns: + The counter block function. + """ + + # Sanity-check the message size + (nbytes, remainder) = divmod(nbits, 8) + if remainder != 0: + # In the future, we might support arbitrary bit lengths, but for now we don't. + raise ValueError("nbits must be a multiple of 8; got %d" % (nbits,)) + if nbytes < 1: + raise ValueError("nbits too small") + elif nbytes > 0xffff: + raise ValueError("nbits too large") + + initval = _encode(initial_value, nbytes, little_endian) + + if little_endian: + return _counter._newLE(bstr(prefix), bstr(suffix), initval, allow_wraparound=allow_wraparound, disable_shortcut=disable_shortcut) + else: + return _counter._newBE(bstr(prefix), bstr(suffix), initval, allow_wraparound=allow_wraparound, disable_shortcut=disable_shortcut) + +def _encode(n, nbytes, little_endian=False): + retval = [] + n = long(n) + for i in range(nbytes): + if little_endian: + retval.append(bchr(n & 0xff)) + else: + retval.insert(0, bchr(n & 0xff)) + n >>= 8 + return b("").join(retval) + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/RFC1751.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/RFC1751.py new file mode 100644 index 0000000..9786e6f --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/RFC1751.py @@ -0,0 +1,364 @@ +# rfc1751.py : Converts between 128-bit strings and a human-readable +# sequence of words, as defined in RFC1751: "A Convention for +# Human-Readable 128-bit Keys", by Daniel L. McDonald. +# +# Part of the Python Cryptography Toolkit +# +# Written by Andrew M. Kuchling and others +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +__revision__ = "$Id$" + + +import binascii +from Crypto.Util.py3compat import * + +binary={0:'0000', 1:'0001', 2:'0010', 3:'0011', 4:'0100', 5:'0101', + 6:'0110', 7:'0111', 8:'1000', 9:'1001', 10:'1010', 11:'1011', + 12:'1100', 13:'1101', 14:'1110', 15:'1111'} + +def _key2bin(s): + "Convert a key into a string of binary digits" + kl=map(lambda x: bord(x), s) + kl=map(lambda x: binary[x>>4]+binary[x&15], kl) + return ''.join(kl) + +def _extract(key, start, length): + """Extract a bitstring(2.x)/bytestring(2.x) from a string of binary digits, and return its + numeric value.""" + k=key[start:start+length] + return reduce(lambda x,y: x*2+ord(y)-48, k, 0) + +def key_to_english (key): + """key_to_english(key:string(2.x)/bytes(3.x)) : string + Transform an arbitrary key into a string containing English words. + The key length must be a multiple of 8. + """ + english='' + for index in range(0, len(key), 8): # Loop over 8-byte subkeys + subkey=key[index:index+8] + # Compute the parity of the key + skbin=_key2bin(subkey) ; p=0 + for i in range(0, 64, 2): p=p+_extract(skbin, i, 2) + # Append parity bits to the subkey + skbin=_key2bin(subkey+bchr((p<<6) & 255)) + for i in range(0, 64, 11): + english=english+wordlist[_extract(skbin, i, 11)]+' ' + + return english[:-1] # Remove the trailing space + +def english_to_key (s): + """english_to_key(string):string(2.x)/bytes(2.x) + Transform a string into a corresponding key. + The string must contain words separated by whitespace; the number + of words must be a multiple of 6. + """ + + L=s.upper().split() ; key=b('') + for index in range(0, len(L), 6): + sublist=L[index:index+6] ; char=9*[0] ; bits=0 + for i in sublist: + index = wordlist.index(i) + shift = (8-(bits+11)%8) %8 + y = index << shift + cl, cc, cr = (y>>16), (y>>8)&0xff, y & 0xff + if (shift>5): + char[bits>>3] = char[bits>>3] | cl + char[(bits>>3)+1] = char[(bits>>3)+1] | cc + char[(bits>>3)+2] = char[(bits>>3)+2] | cr + elif shift>-3: + char[bits>>3] = char[bits>>3] | cc + char[(bits>>3)+1] = char[(bits>>3)+1] | cr + else: char[bits>>3] = char[bits>>3] | cr + bits=bits+11 + subkey=reduce(lambda x,y:x+bchr(y), char, b('')) + + # Check the parity of the resulting key + skbin=_key2bin(subkey) + p=0 + for i in range(0, 64, 2): p=p+_extract(skbin, i, 2) + if (p&3) != _extract(skbin, 64, 2): + raise ValueError, "Parity error in resulting key" + key=key+subkey[0:8] + return key + +wordlist=[ "A", "ABE", "ACE", "ACT", "AD", "ADA", "ADD", + "AGO", "AID", "AIM", "AIR", "ALL", "ALP", "AM", "AMY", "AN", "ANA", + "AND", "ANN", "ANT", "ANY", "APE", "APS", "APT", "ARC", "ARE", "ARK", + "ARM", "ART", "AS", "ASH", "ASK", "AT", "ATE", "AUG", "AUK", "AVE", + "AWE", "AWK", "AWL", "AWN", "AX", "AYE", "BAD", "BAG", "BAH", "BAM", + "BAN", "BAR", "BAT", "BAY", "BE", "BED", "BEE", "BEG", "BEN", "BET", + "BEY", "BIB", "BID", "BIG", "BIN", "BIT", "BOB", "BOG", "BON", "BOO", + "BOP", "BOW", "BOY", "BUB", "BUD", "BUG", "BUM", "BUN", "BUS", "BUT", + "BUY", "BY", "BYE", "CAB", "CAL", "CAM", "CAN", "CAP", "CAR", "CAT", + "CAW", "COD", "COG", "COL", "CON", "COO", "COP", "COT", "COW", "COY", + "CRY", "CUB", "CUE", "CUP", "CUR", "CUT", "DAB", "DAD", "DAM", "DAN", + "DAR", "DAY", "DEE", "DEL", "DEN", "DES", "DEW", "DID", "DIE", "DIG", + "DIN", "DIP", "DO", "DOE", "DOG", "DON", "DOT", "DOW", "DRY", "DUB", + "DUD", "DUE", "DUG", "DUN", "EAR", "EAT", "ED", "EEL", "EGG", "EGO", + "ELI", "ELK", "ELM", "ELY", "EM", "END", "EST", "ETC", "EVA", "EVE", + "EWE", "EYE", "FAD", "FAN", "FAR", "FAT", "FAY", "FED", "FEE", "FEW", + "FIB", "FIG", "FIN", "FIR", "FIT", "FLO", "FLY", "FOE", "FOG", "FOR", + "FRY", "FUM", "FUN", "FUR", "GAB", "GAD", "GAG", "GAL", "GAM", "GAP", + "GAS", "GAY", "GEE", "GEL", "GEM", "GET", "GIG", "GIL", "GIN", "GO", + "GOT", "GUM", "GUN", "GUS", "GUT", "GUY", "GYM", "GYP", "HA", "HAD", + "HAL", "HAM", "HAN", "HAP", "HAS", "HAT", "HAW", "HAY", "HE", "HEM", + "HEN", "HER", "HEW", "HEY", "HI", "HID", "HIM", "HIP", "HIS", "HIT", + "HO", "HOB", "HOC", "HOE", "HOG", "HOP", "HOT", "HOW", "HUB", "HUE", + "HUG", "HUH", "HUM", "HUT", "I", "ICY", "IDA", "IF", "IKE", "ILL", + "INK", "INN", "IO", "ION", "IQ", "IRA", "IRE", "IRK", "IS", "IT", + "ITS", "IVY", "JAB", "JAG", "JAM", "JAN", "JAR", "JAW", "JAY", "JET", + "JIG", "JIM", "JO", "JOB", "JOE", "JOG", "JOT", "JOY", "JUG", "JUT", + "KAY", "KEG", "KEN", "KEY", "KID", "KIM", "KIN", "KIT", "LA", "LAB", + "LAC", "LAD", "LAG", "LAM", "LAP", "LAW", "LAY", "LEA", "LED", "LEE", + "LEG", "LEN", "LEO", "LET", "LEW", "LID", "LIE", "LIN", "LIP", "LIT", + "LO", "LOB", "LOG", "LOP", "LOS", "LOT", "LOU", "LOW", "LOY", "LUG", + "LYE", "MA", "MAC", "MAD", "MAE", "MAN", "MAO", "MAP", "MAT", "MAW", + "MAY", "ME", "MEG", "MEL", "MEN", "MET", "MEW", "MID", "MIN", "MIT", + "MOB", "MOD", "MOE", "MOO", "MOP", "MOS", "MOT", "MOW", "MUD", "MUG", + "MUM", "MY", "NAB", "NAG", "NAN", "NAP", "NAT", "NAY", "NE", "NED", + "NEE", "NET", "NEW", "NIB", "NIL", "NIP", "NIT", "NO", "NOB", "NOD", + "NON", "NOR", "NOT", "NOV", "NOW", "NU", "NUN", "NUT", "O", "OAF", + "OAK", "OAR", "OAT", "ODD", "ODE", "OF", "OFF", "OFT", "OH", "OIL", + "OK", "OLD", "ON", "ONE", "OR", "ORB", "ORE", "ORR", "OS", "OTT", + "OUR", "OUT", "OVA", "OW", "OWE", "OWL", "OWN", "OX", "PA", "PAD", + "PAL", "PAM", "PAN", "PAP", "PAR", "PAT", "PAW", "PAY", "PEA", "PEG", + "PEN", "PEP", "PER", "PET", "PEW", "PHI", "PI", "PIE", "PIN", "PIT", + "PLY", "PO", "POD", "POE", "POP", "POT", "POW", "PRO", "PRY", "PUB", + "PUG", "PUN", "PUP", "PUT", "QUO", "RAG", "RAM", "RAN", "RAP", "RAT", + "RAW", "RAY", "REB", "RED", "REP", "RET", "RIB", "RID", "RIG", "RIM", + "RIO", "RIP", "ROB", "ROD", "ROE", "RON", "ROT", "ROW", "ROY", "RUB", + "RUE", "RUG", "RUM", "RUN", "RYE", "SAC", "SAD", "SAG", "SAL", "SAM", + "SAN", "SAP", "SAT", "SAW", "SAY", "SEA", "SEC", "SEE", "SEN", "SET", + "SEW", "SHE", "SHY", "SIN", "SIP", "SIR", "SIS", "SIT", "SKI", "SKY", + "SLY", "SO", "SOB", "SOD", "SON", "SOP", "SOW", "SOY", "SPA", "SPY", + "SUB", "SUD", "SUE", "SUM", "SUN", "SUP", "TAB", "TAD", "TAG", "TAN", + "TAP", "TAR", "TEA", "TED", "TEE", "TEN", "THE", "THY", "TIC", "TIE", + "TIM", "TIN", "TIP", "TO", "TOE", "TOG", "TOM", "TON", "TOO", "TOP", + "TOW", "TOY", "TRY", "TUB", "TUG", "TUM", "TUN", "TWO", "UN", "UP", + "US", "USE", "VAN", "VAT", "VET", "VIE", "WAD", "WAG", "WAR", "WAS", + "WAY", "WE", "WEB", "WED", "WEE", "WET", "WHO", "WHY", "WIN", "WIT", + "WOK", "WON", "WOO", "WOW", "WRY", "WU", "YAM", "YAP", "YAW", "YE", + "YEA", "YES", "YET", "YOU", "ABED", "ABEL", "ABET", "ABLE", "ABUT", + "ACHE", "ACID", "ACME", "ACRE", "ACTA", "ACTS", "ADAM", "ADDS", + "ADEN", "AFAR", "AFRO", "AGEE", "AHEM", "AHOY", "AIDA", "AIDE", + "AIDS", "AIRY", "AJAR", "AKIN", "ALAN", "ALEC", "ALGA", "ALIA", + "ALLY", "ALMA", "ALOE", "ALSO", "ALTO", "ALUM", "ALVA", "AMEN", + "AMES", "AMID", "AMMO", "AMOK", "AMOS", "AMRA", "ANDY", "ANEW", + "ANNA", "ANNE", "ANTE", "ANTI", "AQUA", "ARAB", "ARCH", "AREA", + "ARGO", "ARID", "ARMY", "ARTS", "ARTY", "ASIA", "ASKS", "ATOM", + "AUNT", "AURA", "AUTO", "AVER", "AVID", "AVIS", "AVON", "AVOW", + "AWAY", "AWRY", "BABE", "BABY", "BACH", "BACK", "BADE", "BAIL", + "BAIT", "BAKE", "BALD", "BALE", "BALI", "BALK", "BALL", "BALM", + "BAND", "BANE", "BANG", "BANK", "BARB", "BARD", "BARE", "BARK", + "BARN", "BARR", "BASE", "BASH", "BASK", "BASS", "BATE", "BATH", + "BAWD", "BAWL", "BEAD", "BEAK", "BEAM", "BEAN", "BEAR", "BEAT", + "BEAU", "BECK", "BEEF", "BEEN", "BEER", + "BEET", "BELA", "BELL", "BELT", "BEND", "BENT", "BERG", "BERN", + "BERT", "BESS", "BEST", "BETA", "BETH", "BHOY", "BIAS", "BIDE", + "BIEN", "BILE", "BILK", "BILL", "BIND", "BING", "BIRD", "BITE", + "BITS", "BLAB", "BLAT", "BLED", "BLEW", "BLOB", "BLOC", "BLOT", + "BLOW", "BLUE", "BLUM", "BLUR", "BOAR", "BOAT", "BOCA", "BOCK", + "BODE", "BODY", "BOGY", "BOHR", "BOIL", "BOLD", "BOLO", "BOLT", + "BOMB", "BONA", "BOND", "BONE", "BONG", "BONN", "BONY", "BOOK", + "BOOM", "BOON", "BOOT", "BORE", "BORG", "BORN", "BOSE", "BOSS", + "BOTH", "BOUT", "BOWL", "BOYD", "BRAD", "BRAE", "BRAG", "BRAN", + "BRAY", "BRED", "BREW", "BRIG", "BRIM", "BROW", "BUCK", "BUDD", + "BUFF", "BULB", "BULK", "BULL", "BUNK", "BUNT", "BUOY", "BURG", + "BURL", "BURN", "BURR", "BURT", "BURY", "BUSH", "BUSS", "BUST", + "BUSY", "BYTE", "CADY", "CAFE", "CAGE", "CAIN", "CAKE", "CALF", + "CALL", "CALM", "CAME", "CANE", "CANT", "CARD", "CARE", "CARL", + "CARR", "CART", "CASE", "CASH", "CASK", "CAST", "CAVE", "CEIL", + "CELL", "CENT", "CERN", "CHAD", "CHAR", "CHAT", "CHAW", "CHEF", + "CHEN", "CHEW", "CHIC", "CHIN", "CHOU", "CHOW", "CHUB", "CHUG", + "CHUM", "CITE", "CITY", "CLAD", "CLAM", "CLAN", "CLAW", "CLAY", + "CLOD", "CLOG", "CLOT", "CLUB", "CLUE", "COAL", "COAT", "COCA", + "COCK", "COCO", "CODA", "CODE", "CODY", "COED", "COIL", "COIN", + "COKE", "COLA", "COLD", "COLT", "COMA", "COMB", "COME", "COOK", + "COOL", "COON", "COOT", "CORD", "CORE", "CORK", "CORN", "COST", + "COVE", "COWL", "CRAB", "CRAG", "CRAM", "CRAY", "CREW", "CRIB", + "CROW", "CRUD", "CUBA", "CUBE", "CUFF", "CULL", "CULT", "CUNY", + "CURB", "CURD", "CURE", "CURL", "CURT", "CUTS", "DADE", "DALE", + "DAME", "DANA", "DANE", "DANG", "DANK", "DARE", "DARK", "DARN", + "DART", "DASH", "DATA", "DATE", "DAVE", "DAVY", "DAWN", "DAYS", + "DEAD", "DEAF", "DEAL", "DEAN", "DEAR", "DEBT", "DECK", "DEED", + "DEEM", "DEER", "DEFT", "DEFY", "DELL", "DENT", "DENY", "DESK", + "DIAL", "DICE", "DIED", "DIET", "DIME", "DINE", "DING", "DINT", + "DIRE", "DIRT", "DISC", "DISH", "DISK", "DIVE", "DOCK", "DOES", + "DOLE", "DOLL", "DOLT", "DOME", "DONE", "DOOM", "DOOR", "DORA", + "DOSE", "DOTE", "DOUG", "DOUR", "DOVE", "DOWN", "DRAB", "DRAG", + "DRAM", "DRAW", "DREW", "DRUB", "DRUG", "DRUM", "DUAL", "DUCK", + "DUCT", "DUEL", "DUET", "DUKE", "DULL", "DUMB", "DUNE", "DUNK", + "DUSK", "DUST", "DUTY", "EACH", "EARL", "EARN", "EASE", "EAST", + "EASY", "EBEN", "ECHO", "EDDY", "EDEN", "EDGE", "EDGY", "EDIT", + "EDNA", "EGAN", "ELAN", "ELBA", "ELLA", "ELSE", "EMIL", "EMIT", + "EMMA", "ENDS", "ERIC", "EROS", "EVEN", "EVER", "EVIL", "EYED", + "FACE", "FACT", "FADE", "FAIL", "FAIN", "FAIR", "FAKE", "FALL", + "FAME", "FANG", "FARM", "FAST", "FATE", "FAWN", "FEAR", "FEAT", + "FEED", "FEEL", "FEET", "FELL", "FELT", "FEND", "FERN", "FEST", + "FEUD", "FIEF", "FIGS", "FILE", "FILL", "FILM", "FIND", "FINE", + "FINK", "FIRE", "FIRM", "FISH", "FISK", "FIST", "FITS", "FIVE", + "FLAG", "FLAK", "FLAM", "FLAT", "FLAW", "FLEA", "FLED", "FLEW", + "FLIT", "FLOC", "FLOG", "FLOW", "FLUB", "FLUE", "FOAL", "FOAM", + "FOGY", "FOIL", "FOLD", "FOLK", "FOND", "FONT", "FOOD", "FOOL", + "FOOT", "FORD", "FORE", "FORK", "FORM", "FORT", "FOSS", "FOUL", + "FOUR", "FOWL", "FRAU", "FRAY", "FRED", "FREE", "FRET", "FREY", + "FROG", "FROM", "FUEL", "FULL", "FUME", "FUND", "FUNK", "FURY", + "FUSE", "FUSS", "GAFF", "GAGE", "GAIL", "GAIN", "GAIT", "GALA", + "GALE", "GALL", "GALT", "GAME", "GANG", "GARB", "GARY", "GASH", + "GATE", "GAUL", "GAUR", "GAVE", "GAWK", "GEAR", "GELD", "GENE", + "GENT", "GERM", "GETS", "GIBE", "GIFT", "GILD", "GILL", "GILT", + "GINA", "GIRD", "GIRL", "GIST", "GIVE", "GLAD", "GLEE", "GLEN", + "GLIB", "GLOB", "GLOM", "GLOW", "GLUE", "GLUM", "GLUT", "GOAD", + "GOAL", "GOAT", "GOER", "GOES", "GOLD", "GOLF", "GONE", "GONG", + "GOOD", "GOOF", "GORE", "GORY", "GOSH", "GOUT", "GOWN", "GRAB", + "GRAD", "GRAY", "GREG", "GREW", "GREY", "GRID", "GRIM", "GRIN", + "GRIT", "GROW", "GRUB", "GULF", "GULL", "GUNK", "GURU", "GUSH", + "GUST", "GWEN", "GWYN", "HAAG", "HAAS", "HACK", "HAIL", "HAIR", + "HALE", "HALF", "HALL", "HALO", "HALT", "HAND", "HANG", "HANK", + "HANS", "HARD", "HARK", "HARM", "HART", "HASH", "HAST", "HATE", + "HATH", "HAUL", "HAVE", "HAWK", "HAYS", "HEAD", "HEAL", "HEAR", + "HEAT", "HEBE", "HECK", "HEED", "HEEL", "HEFT", "HELD", "HELL", + "HELM", "HERB", "HERD", "HERE", "HERO", "HERS", "HESS", "HEWN", + "HICK", "HIDE", "HIGH", "HIKE", "HILL", "HILT", "HIND", "HINT", + "HIRE", "HISS", "HIVE", "HOBO", "HOCK", "HOFF", "HOLD", "HOLE", + "HOLM", "HOLT", "HOME", "HONE", "HONK", "HOOD", "HOOF", "HOOK", + "HOOT", "HORN", "HOSE", "HOST", "HOUR", "HOVE", "HOWE", "HOWL", + "HOYT", "HUCK", "HUED", "HUFF", "HUGE", "HUGH", "HUGO", "HULK", + "HULL", "HUNK", "HUNT", "HURD", "HURL", "HURT", "HUSH", "HYDE", + "HYMN", "IBIS", "ICON", "IDEA", "IDLE", "IFFY", "INCA", "INCH", + "INTO", "IONS", "IOTA", "IOWA", "IRIS", "IRMA", "IRON", "ISLE", + "ITCH", "ITEM", "IVAN", "JACK", "JADE", "JAIL", "JAKE", "JANE", + "JAVA", "JEAN", "JEFF", "JERK", "JESS", "JEST", "JIBE", "JILL", + "JILT", "JIVE", "JOAN", "JOBS", "JOCK", "JOEL", "JOEY", "JOHN", + "JOIN", "JOKE", "JOLT", "JOVE", "JUDD", "JUDE", "JUDO", "JUDY", + "JUJU", "JUKE", "JULY", "JUNE", "JUNK", "JUNO", "JURY", "JUST", + "JUTE", "KAHN", "KALE", "KANE", "KANT", "KARL", "KATE", "KEEL", + "KEEN", "KENO", "KENT", "KERN", "KERR", "KEYS", "KICK", "KILL", + "KIND", "KING", "KIRK", "KISS", "KITE", "KLAN", "KNEE", "KNEW", + "KNIT", "KNOB", "KNOT", "KNOW", "KOCH", "KONG", "KUDO", "KURD", + "KURT", "KYLE", "LACE", "LACK", "LACY", "LADY", "LAID", "LAIN", + "LAIR", "LAKE", "LAMB", "LAME", "LAND", "LANE", "LANG", "LARD", + "LARK", "LASS", "LAST", "LATE", "LAUD", "LAVA", "LAWN", "LAWS", + "LAYS", "LEAD", "LEAF", "LEAK", "LEAN", "LEAR", "LEEK", "LEER", + "LEFT", "LEND", "LENS", "LENT", "LEON", "LESK", "LESS", "LEST", + "LETS", "LIAR", "LICE", "LICK", "LIED", "LIEN", "LIES", "LIEU", + "LIFE", "LIFT", "LIKE", "LILA", "LILT", "LILY", "LIMA", "LIMB", + "LIME", "LIND", "LINE", "LINK", "LINT", "LION", "LISA", "LIST", + "LIVE", "LOAD", "LOAF", "LOAM", "LOAN", "LOCK", "LOFT", "LOGE", + "LOIS", "LOLA", "LONE", "LONG", "LOOK", "LOON", "LOOT", "LORD", + "LORE", "LOSE", "LOSS", "LOST", "LOUD", "LOVE", "LOWE", "LUCK", + "LUCY", "LUGE", "LUKE", "LULU", "LUND", "LUNG", "LURA", "LURE", + "LURK", "LUSH", "LUST", "LYLE", "LYNN", "LYON", "LYRA", "MACE", + "MADE", "MAGI", "MAID", "MAIL", "MAIN", "MAKE", "MALE", "MALI", + "MALL", "MALT", "MANA", "MANN", "MANY", "MARC", "MARE", "MARK", + "MARS", "MART", "MARY", "MASH", "MASK", "MASS", "MAST", "MATE", + "MATH", "MAUL", "MAYO", "MEAD", "MEAL", "MEAN", "MEAT", "MEEK", + "MEET", "MELD", "MELT", "MEMO", "MEND", "MENU", "MERT", "MESH", + "MESS", "MICE", "MIKE", "MILD", "MILE", "MILK", "MILL", "MILT", + "MIMI", "MIND", "MINE", "MINI", "MINK", "MINT", "MIRE", "MISS", + "MIST", "MITE", "MITT", "MOAN", "MOAT", "MOCK", "MODE", "MOLD", + "MOLE", "MOLL", "MOLT", "MONA", "MONK", "MONT", "MOOD", "MOON", + "MOOR", "MOOT", "MORE", "MORN", "MORT", "MOSS", "MOST", "MOTH", + "MOVE", "MUCH", "MUCK", "MUDD", "MUFF", "MULE", "MULL", "MURK", + "MUSH", "MUST", "MUTE", "MUTT", "MYRA", "MYTH", "NAGY", "NAIL", + "NAIR", "NAME", "NARY", "NASH", "NAVE", "NAVY", "NEAL", "NEAR", + "NEAT", "NECK", "NEED", "NEIL", "NELL", "NEON", "NERO", "NESS", + "NEST", "NEWS", "NEWT", "NIBS", "NICE", "NICK", "NILE", "NINA", + "NINE", "NOAH", "NODE", "NOEL", "NOLL", "NONE", "NOOK", "NOON", + "NORM", "NOSE", "NOTE", "NOUN", "NOVA", "NUDE", "NULL", "NUMB", + "OATH", "OBEY", "OBOE", "ODIN", "OHIO", "OILY", "OINT", "OKAY", + "OLAF", "OLDY", "OLGA", "OLIN", "OMAN", "OMEN", "OMIT", "ONCE", + "ONES", "ONLY", "ONTO", "ONUS", "ORAL", "ORGY", "OSLO", "OTIS", + "OTTO", "OUCH", "OUST", "OUTS", "OVAL", "OVEN", "OVER", "OWLY", + "OWNS", "QUAD", "QUIT", "QUOD", "RACE", "RACK", "RACY", "RAFT", + "RAGE", "RAID", "RAIL", "RAIN", "RAKE", "RANK", "RANT", "RARE", + "RASH", "RATE", "RAVE", "RAYS", "READ", "REAL", "REAM", "REAR", + "RECK", "REED", "REEF", "REEK", "REEL", "REID", "REIN", "RENA", + "REND", "RENT", "REST", "RICE", "RICH", "RICK", "RIDE", "RIFT", + "RILL", "RIME", "RING", "RINK", "RISE", "RISK", "RITE", "ROAD", + "ROAM", "ROAR", "ROBE", "ROCK", "RODE", "ROIL", "ROLL", "ROME", + "ROOD", "ROOF", "ROOK", "ROOM", "ROOT", "ROSA", "ROSE", "ROSS", + "ROSY", "ROTH", "ROUT", "ROVE", "ROWE", "ROWS", "RUBE", "RUBY", + "RUDE", "RUDY", "RUIN", "RULE", "RUNG", "RUNS", "RUNT", "RUSE", + "RUSH", "RUSK", "RUSS", "RUST", "RUTH", "SACK", "SAFE", "SAGE", + "SAID", "SAIL", "SALE", "SALK", "SALT", "SAME", "SAND", "SANE", + "SANG", "SANK", "SARA", "SAUL", "SAVE", "SAYS", "SCAN", "SCAR", + "SCAT", "SCOT", "SEAL", "SEAM", "SEAR", "SEAT", "SEED", "SEEK", + "SEEM", "SEEN", "SEES", "SELF", "SELL", "SEND", "SENT", "SETS", + "SEWN", "SHAG", "SHAM", "SHAW", "SHAY", "SHED", "SHIM", "SHIN", + "SHOD", "SHOE", "SHOT", "SHOW", "SHUN", "SHUT", "SICK", "SIDE", + "SIFT", "SIGH", "SIGN", "SILK", "SILL", "SILO", "SILT", "SINE", + "SING", "SINK", "SIRE", "SITE", "SITS", "SITU", "SKAT", "SKEW", + "SKID", "SKIM", "SKIN", "SKIT", "SLAB", "SLAM", "SLAT", "SLAY", + "SLED", "SLEW", "SLID", "SLIM", "SLIT", "SLOB", "SLOG", "SLOT", + "SLOW", "SLUG", "SLUM", "SLUR", "SMOG", "SMUG", "SNAG", "SNOB", + "SNOW", "SNUB", "SNUG", "SOAK", "SOAR", "SOCK", "SODA", "SOFA", + "SOFT", "SOIL", "SOLD", "SOME", "SONG", "SOON", "SOOT", "SORE", + "SORT", "SOUL", "SOUR", "SOWN", "STAB", "STAG", "STAN", "STAR", + "STAY", "STEM", "STEW", "STIR", "STOW", "STUB", "STUN", "SUCH", + "SUDS", "SUIT", "SULK", "SUMS", "SUNG", "SUNK", "SURE", "SURF", + "SWAB", "SWAG", "SWAM", "SWAN", "SWAT", "SWAY", "SWIM", "SWUM", + "TACK", "TACT", "TAIL", "TAKE", "TALE", "TALK", "TALL", "TANK", + "TASK", "TATE", "TAUT", "TEAL", "TEAM", "TEAR", "TECH", "TEEM", + "TEEN", "TEET", "TELL", "TEND", "TENT", "TERM", "TERN", "TESS", + "TEST", "THAN", "THAT", "THEE", "THEM", "THEN", "THEY", "THIN", + "THIS", "THUD", "THUG", "TICK", "TIDE", "TIDY", "TIED", "TIER", + "TILE", "TILL", "TILT", "TIME", "TINA", "TINE", "TINT", "TINY", + "TIRE", "TOAD", "TOGO", "TOIL", "TOLD", "TOLL", "TONE", "TONG", + "TONY", "TOOK", "TOOL", "TOOT", "TORE", "TORN", "TOTE", "TOUR", + "TOUT", "TOWN", "TRAG", "TRAM", "TRAY", "TREE", "TREK", "TRIG", + "TRIM", "TRIO", "TROD", "TROT", "TROY", "TRUE", "TUBA", "TUBE", + "TUCK", "TUFT", "TUNA", "TUNE", "TUNG", "TURF", "TURN", "TUSK", + "TWIG", "TWIN", "TWIT", "ULAN", "UNIT", "URGE", "USED", "USER", + "USES", "UTAH", "VAIL", "VAIN", "VALE", "VARY", "VASE", "VAST", + "VEAL", "VEDA", "VEIL", "VEIN", "VEND", "VENT", "VERB", "VERY", + "VETO", "VICE", "VIEW", "VINE", "VISE", "VOID", "VOLT", "VOTE", + "WACK", "WADE", "WAGE", "WAIL", "WAIT", "WAKE", "WALE", "WALK", + "WALL", "WALT", "WAND", "WANE", "WANG", "WANT", "WARD", "WARM", + "WARN", "WART", "WASH", "WAST", "WATS", "WATT", "WAVE", "WAVY", + "WAYS", "WEAK", "WEAL", "WEAN", "WEAR", "WEED", "WEEK", "WEIR", + "WELD", "WELL", "WELT", "WENT", "WERE", "WERT", "WEST", "WHAM", + "WHAT", "WHEE", "WHEN", "WHET", "WHOA", "WHOM", "WICK", "WIFE", + "WILD", "WILL", "WIND", "WINE", "WING", "WINK", "WINO", "WIRE", + "WISE", "WISH", "WITH", "WOLF", "WONT", "WOOD", "WOOL", "WORD", + "WORE", "WORK", "WORM", "WORN", "WOVE", "WRIT", "WYNN", "YALE", + "YANG", "YANK", "YARD", "YARN", "YAWL", "YAWN", "YEAH", "YEAR", + "YELL", "YOGA", "YOKE" ] + +if __name__=='__main__': + data = [('EB33F77EE73D4053', 'TIDE ITCH SLOW REIN RULE MOT'), + ('CCAC2AED591056BE4F90FD441C534766', + 'RASH BUSH MILK LOOK BAD BRIM AVID GAFF BAIT ROT POD LOVE'), + ('EFF81F9BFBC65350920CDD7416DE8009', + 'TROD MUTE TAIL WARM CHAR KONG HAAG CITY BORE O TEAL AWL') + ] + + for key, words in data: + print 'Trying key', key + key=binascii.a2b_hex(key) + w2=key_to_english(key) + if w2!=words: + print 'key_to_english fails on key', repr(key), ', producing', str(w2) + k2=english_to_key(words) + if k2!=key: + print 'english_to_key fails on key', repr(key), ', producing', repr(k2) + + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/__init__.py new file mode 100644 index 0000000..a3bef8a --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/__init__.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Miscellaneous modules + +Contains useful modules that don't belong into any of the +other Crypto.* subpackages. + +Crypto.Util.number Number-theoretic functions (primality testing, etc.) +Crypto.Util.randpool Random number generation +Crypto.Util.RFC1751 Converts between 128-bit keys and human-readable + strings of words. +Crypto.Util.asn1 Minimal support for ASN.1 DER encoding + +""" + +__all__ = ['randpool', 'RFC1751', 'number', 'strxor', 'asn1' ] + +__revision__ = "$Id$" + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/_counter.so b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/_counter.so new file mode 100755 index 0000000..34fadb0 Binary files /dev/null and b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/_counter.so differ diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/_number_new.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/_number_new.py new file mode 100644 index 0000000..b040025 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/_number_new.py @@ -0,0 +1,119 @@ +# -*- coding: ascii -*- +# +# Util/_number_new.py : utility functions +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +## NOTE: Do not import this module directly. Import these functions from Crypto.Util.number. + +__revision__ = "$Id$" +__all__ = ['ceil_shift', 'ceil_div', 'floor_div', 'exact_log2', 'exact_div'] + +import sys +if sys.version_info[0] == 2 and sys.version_info[1] == 1: + from Crypto.Util.py21compat import * + +def ceil_shift(n, b): + """Return ceil(n / 2**b) without performing any floating-point or division operations. + + This is done by right-shifting n by b bits and incrementing the result by 1 + if any '1' bits were shifted out. + """ + if not isinstance(n, (int, long)) or not isinstance(b, (int, long)): + raise TypeError("unsupported operand type(s): %r and %r" % (type(n).__name__, type(b).__name__)) + + assert n >= 0 and b >= 0 # I haven't tested or even thought about negative values + mask = (1L << b) - 1 + if n & mask: + return (n >> b) + 1 + else: + return n >> b + +def ceil_div(a, b): + """Return ceil(a / b) without performing any floating-point operations.""" + + if not isinstance(a, (int, long)) or not isinstance(b, (int, long)): + raise TypeError("unsupported operand type(s): %r and %r" % (type(a).__name__, type(b).__name__)) + + (q, r) = divmod(a, b) + if r: + return q + 1 + else: + return q + +def floor_div(a, b): + if not isinstance(a, (int, long)) or not isinstance(b, (int, long)): + raise TypeError("unsupported operand type(s): %r and %r" % (type(a).__name__, type(b).__name__)) + + (q, r) = divmod(a, b) + return q + +def exact_log2(num): + """Find and return an integer i >= 0 such that num == 2**i. + + If no such integer exists, this function raises ValueError. + """ + + if not isinstance(num, (int, long)): + raise TypeError("unsupported operand type: %r" % (type(num).__name__,)) + + n = long(num) + if n <= 0: + raise ValueError("cannot compute logarithm of non-positive number") + + i = 0 + while n != 0: + if (n & 1) and n != 1: + raise ValueError("No solution could be found") + i += 1 + n >>= 1 + i -= 1 + + assert num == (1L << i) + return i + +def exact_div(p, d, allow_divzero=False): + """Find and return an integer n such that p == n * d + + If no such integer exists, this function raises ValueError. + + Both operands must be integers. + + If the second operand is zero, this function will raise ZeroDivisionError + unless allow_divzero is true (default: False). + """ + + if not isinstance(p, (int, long)) or not isinstance(d, (int, long)): + raise TypeError("unsupported operand type(s): %r and %r" % (type(p).__name__, type(d).__name__)) + + if d == 0 and allow_divzero: + n = 0 + if p != n * d: + raise ValueError("No solution could be found") + else: + (n, r) = divmod(p, d) + if r != 0: + raise ValueError("No solution could be found") + + assert p == n * d + return n + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/asn1.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/asn1.py new file mode 100644 index 0000000..dd5ec31 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/asn1.py @@ -0,0 +1,286 @@ +# -*- coding: ascii -*- +# +# Util/asn1.py : Minimal support for ASN.1 DER binary encoding. +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +from Crypto.Util.number import long_to_bytes, bytes_to_long +import sys +from Crypto.Util.py3compat import * + +__all__ = [ 'DerObject', 'DerInteger', 'DerOctetString', 'DerNull', 'DerSequence', 'DerObjectId' ] + +class DerObject: + """Base class for defining a single DER object. + + Instantiate this class ONLY when you have to decode a DER element. + """ + + # Known TAG types + typeTags = { 'SEQUENCE': 0x30, 'BIT STRING': 0x03, 'INTEGER': 0x02, + 'OCTET STRING': 0x04, 'NULL': 0x05, 'OBJECT IDENTIFIER': 0x06 } + + def __init__(self, ASN1Type=None, payload=b('')): + """Initialize the DER object according to a specific type. + + The ASN.1 type is either specified as the ASN.1 string (e.g. + 'SEQUENCE'), directly with its numerical tag or with no tag + at all (None).""" + if isInt(ASN1Type) or ASN1Type is None: + self.typeTag = ASN1Type + else: + if len(ASN1Type)==1: + self.typeTag = ord(ASN1Type) + else: + self.typeTag = self.typeTags.get(ASN1Type) + self.payload = payload + + def isType(self, ASN1Type): + return self.typeTags[ASN1Type]==self.typeTag + + def _lengthOctets(self, payloadLen): + """Return a byte string that encodes the given payload length (in + bytes) in a format suitable for a DER length tag (L). + """ + if payloadLen>127: + encoding = long_to_bytes(payloadLen) + return bchr(len(encoding)+128) + encoding + return bchr(payloadLen) + + def encode(self): + """Return a complete DER element, fully encoded as a TLV.""" + return bchr(self.typeTag) + self._lengthOctets(len(self.payload)) + self.payload + + def _decodeLen(self, idx, der): + """Given a (part of a) DER element, and an index to the first byte of + a DER length tag (L), return a tuple with the payload size, + and the index of the first byte of the such payload (V). + + Raises a ValueError exception if the DER length is invalid. + Raises an IndexError exception if the DER element is too short. + """ + length = bord(der[idx]) + if length<=127: + return (length,idx+1) + payloadLength = bytes_to_long(der[idx+1:idx+1+(length & 0x7F)]) + if payloadLength<=127: + raise ValueError("Not a DER length tag.") + return (payloadLength, idx+1+(length & 0x7F)) + + def decode(self, derEle, noLeftOvers=0): + """Decode a complete DER element, and re-initializes this + object with it. + + @param derEle A complete DER element. It must start with a DER T + tag. + @param noLeftOvers Indicate whether it is acceptable to complete the + parsing of the DER element and find that not all + bytes in derEle have been used. + @return Index of the first unused byte in the given DER element. + + Raises a ValueError exception in case of parsing errors. + Raises an IndexError exception if the DER element is too short. + """ + try: + self.typeTag = bord(derEle[0]) + if (self.typeTag & 0x1F)==0x1F: + raise ValueError("Unsupported DER tag") + (length,idx) = self._decodeLen(1, derEle) + if noLeftOvers and len(derEle) != (idx+length): + raise ValueError("Not a DER structure") + self.payload = derEle[idx:idx+length] + except IndexError: + raise ValueError("Not a valid DER SEQUENCE.") + return idx+length + +class DerInteger(DerObject): + def __init__(self, value = 0): + """Class to model an INTEGER DER element. + + Limitation: only non-negative values are supported. + """ + DerObject.__init__(self, 'INTEGER') + self.value = value + + def encode(self): + """Return a complete INTEGER DER element, fully encoded as a TLV.""" + self.payload = long_to_bytes(self.value) + if bord(self.payload[0])>127: + self.payload = bchr(0x00) + self.payload + return DerObject.encode(self) + + def decode(self, derEle, noLeftOvers=0): + """Decode a complete INTEGER DER element, and re-initializes this + object with it. + + @param derEle A complete INTEGER DER element. It must start with a DER + INTEGER tag. + @param noLeftOvers Indicate whether it is acceptable to complete the + parsing of the DER element and find that not all + bytes in derEle have been used. + @return Index of the first unused byte in the given DER element. + + Raises a ValueError exception if the DER element is not a + valid non-negative INTEGER. + Raises an IndexError exception if the DER element is too short. + """ + tlvLength = DerObject.decode(self, derEle, noLeftOvers) + if self.typeTag!=self.typeTags['INTEGER']: + raise ValueError ("Not a DER INTEGER.") + if bord(self.payload[0])>127: + raise ValueError ("Negative INTEGER.") + self.value = bytes_to_long(self.payload) + return tlvLength + +class DerSequence(DerObject): + """Class to model a SEQUENCE DER element. + + This object behave like a dynamic Python sequence. + Sub-elements that are INTEGERs, look like Python integers. + Any other sub-element is a binary string encoded as the complete DER + sub-element (TLV). + """ + + def __init__(self, startSeq=None): + """Initialize the SEQUENCE DER object. Always empty + initially.""" + DerObject.__init__(self, 'SEQUENCE') + if startSeq==None: + self._seq = [] + else: + self._seq = startSeq + + ## A few methods to make it behave like a python sequence + + def __delitem__(self, n): + del self._seq[n] + def __getitem__(self, n): + return self._seq[n] + def __setitem__(self, key, value): + self._seq[key] = value + def __setslice__(self,i,j,sequence): + self._seq[i:j] = sequence + def __delslice__(self,i,j): + del self._seq[i:j] + def __getslice__(self, i, j): + return self._seq[max(0, i):max(0, j)] + def __len__(self): + return len(self._seq) + def append(self, item): + return self._seq.append(item) + + def hasInts(self): + """Return the number of items in this sequence that are numbers.""" + return len(filter(isInt, self._seq)) + + def hasOnlyInts(self): + """Return True if all items in this sequence are numbers.""" + return self._seq and self.hasInts()==len(self._seq) + + def encode(self): + """Return the DER encoding for the ASN.1 SEQUENCE, containing + the non-negative integers and longs added to this object. + + Limitation: Raises a ValueError exception if it some elements + in the sequence are neither Python integers nor complete DER INTEGERs. + """ + self.payload = b('') + for item in self._seq: + try: + self.payload += item + except: + try: + self.payload += DerInteger(item).encode() + except: + raise ValueError("Trying to DER encode an unknown object") + return DerObject.encode(self) + + def decode(self, derEle, noLeftOvers=0): + """Decode a complete SEQUENCE DER element, and re-initializes this + object with it. + + @param derEle A complete SEQUENCE DER element. It must start with a DER + SEQUENCE tag. + @param noLeftOvers Indicate whether it is acceptable to complete the + parsing of the DER element and find that not all + bytes in derEle have been used. + @return Index of the first unused byte in the given DER element. + + DER INTEGERs are decoded into Python integers. Any other DER + element is not decoded. Its validity is not checked. + + Raises a ValueError exception if the DER element is not a + valid DER SEQUENCE. + Raises an IndexError exception if the DER element is too short. + """ + + self._seq = [] + try: + tlvLength = DerObject.decode(self, derEle, noLeftOvers) + if self.typeTag!=self.typeTags['SEQUENCE']: + raise ValueError("Not a DER SEQUENCE.") + # Scan one TLV at once + idx = 0 + while idx= 5 to avoid timing attack vulnerability.", PowmInsecureWarning) + +# New functions +from _number_new import * + +# Commented out and replaced with faster versions below +## def long2str(n): +## s='' +## while n>0: +## s=chr(n & 255)+s +## n=n>>8 +## return s + +## import types +## def str2long(s): +## if type(s)!=types.StringType: return s # Integers will be left alone +## return reduce(lambda x,y : x*256+ord(y), s, 0L) + +def size (N): + """size(N:long) : int + Returns the size of the number N in bits. + """ + bits = 0 + while N >> bits: + bits += 1 + return bits + +def getRandomNumber(N, randfunc=None): + """Deprecated. Use getRandomInteger or getRandomNBitInteger instead.""" + warnings.warn("Crypto.Util.number.getRandomNumber has confusing semantics"+ + "and has been deprecated. Use getRandomInteger or getRandomNBitInteger instead.", + GetRandomNumber_DeprecationWarning) + return getRandomNBitInteger(N, randfunc) + +def getRandomInteger(N, randfunc=None): + """getRandomInteger(N:int, randfunc:callable):long + Return a random number with at most N bits. + + If randfunc is omitted, then Random.new().read is used. + + This function is for internal use only and may be renamed or removed in + the future. + """ + if randfunc is None: + _import_Random() + randfunc = Random.new().read + + S = randfunc(N>>3) + odd_bits = N % 8 + if odd_bits != 0: + char = ord(randfunc(1)) >> (8-odd_bits) + S = bchr(char) + S + value = bytes_to_long(S) + return value + +def getRandomRange(a, b, randfunc=None): + """getRandomRange(a:int, b:int, randfunc:callable):long + Return a random number n so that a <= n < b. + + If randfunc is omitted, then Random.new().read is used. + + This function is for internal use only and may be renamed or removed in + the future. + """ + range_ = b - a - 1 + bits = size(range_) + value = getRandomInteger(bits, randfunc) + while value > range_: + value = getRandomInteger(bits, randfunc) + return a + value + +def getRandomNBitInteger(N, randfunc=None): + """getRandomInteger(N:int, randfunc:callable):long + Return a random number with exactly N-bits, i.e. a random number + between 2**(N-1) and (2**N)-1. + + If randfunc is omitted, then Random.new().read is used. + + This function is for internal use only and may be renamed or removed in + the future. + """ + value = getRandomInteger (N-1, randfunc) + value |= 2L ** (N-1) # Ensure high bit is set + assert size(value) >= N + return value + +def GCD(x,y): + """GCD(x:long, y:long): long + Return the GCD of x and y. + """ + x = abs(x) ; y = abs(y) + while x > 0: + x, y = y % x, x + return y + +def inverse(u, v): + """inverse(u:long, v:long):long + Return the inverse of u mod v. + """ + u3, v3 = long(u), long(v) + u1, v1 = 1L, 0L + while v3 > 0: + q=divmod(u3, v3)[0] + u1, v1 = v1, u1 - v1*q + u3, v3 = v3, u3 - v3*q + while u1<0: + u1 = u1 + v + return u1 + +# Given a number of bits to generate and a random generation function, +# find a prime number of the appropriate size. + +def getPrime(N, randfunc=None): + """getPrime(N:int, randfunc:callable):long + Return a random N-bit prime number. + + If randfunc is omitted, then Random.new().read is used. + """ + if randfunc is None: + _import_Random() + randfunc = Random.new().read + + number=getRandomNBitInteger(N, randfunc) | 1 + while (not isPrime(number, randfunc=randfunc)): + number=number+2 + return number + + +def _rabinMillerTest(n, rounds, randfunc=None): + """_rabinMillerTest(n:long, rounds:int, randfunc:callable):int + Tests if n is prime. + Returns 0 when n is definitly composite. + Returns 1 when n is probably prime. + Returns 2 when n is definitly prime. + + If randfunc is omitted, then Random.new().read is used. + + This function is for internal use only and may be renamed or removed in + the future. + """ + # check special cases (n==2, n even, n < 2) + if n < 3 or (n & 1) == 0: + return n == 2 + # n might be very large so it might be beneficial to precalculate n-1 + n_1 = n - 1 + # determine m and b so that 2**b * m = n - 1 and b maximal + b = 0 + m = n_1 + while (m & 1) == 0: + b += 1 + m >>= 1 + + tested = [] + # we need to do at most n-2 rounds. + for i in xrange (min (rounds, n-2)): + # randomly choose a < n and make sure it hasn't been tested yet + a = getRandomRange (2, n, randfunc) + while a in tested: + a = getRandomRange (2, n, randfunc) + tested.append (a) + # do the rabin-miller test + z = pow (a, m, n) # (a**m) % n + if z == 1 or z == n_1: + continue + composite = 1 + for r in xrange (b): + z = (z * z) % n + if z == 1: + return 0 + elif z == n_1: + composite = 0 + break + if composite: + return 0 + return 1 + +def getStrongPrime(N, e=0, false_positive_prob=1e-6, randfunc=None): + """getStrongPrime(N:int, e:int, false_positive_prob:float, randfunc:callable):long + Return a random strong N-bit prime number. + In this context p is a strong prime if p-1 and p+1 have at + least one large prime factor. + N should be a multiple of 128 and > 512. + + If e is provided the returned prime p-1 will be coprime to e + and thus suitable for RSA where e is the public exponent. + + The optional false_positive_prob is the statistical probability + that true is returned even though it is not (pseudo-prime). + It defaults to 1e-6 (less than 1:1000000). + Note that the real probability of a false-positive is far less. This is + just the mathematically provable limit. + + randfunc should take a single int parameter and return that + many random bytes as a string. + If randfunc is omitted, then Random.new().read is used. + """ + # This function was implemented following the + # instructions found in the paper: + # "FAST GENERATION OF RANDOM, STRONG RSA PRIMES" + # by Robert D. Silverman + # RSA Laboratories + # May 17, 1997 + # which by the time of writing could be freely downloaded here: + # http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.17.2713&rep=rep1&type=pdf + + # Use the accelerator if available + if _fastmath is not None: + return _fastmath.getStrongPrime(long(N), long(e), false_positive_prob, + randfunc) + + if (N < 512) or ((N % 128) != 0): + raise ValueError ("bits must be multiple of 128 and > 512") + + rabin_miller_rounds = int(math.ceil(-math.log(false_positive_prob)/math.log(4))) + + # calculate range for X + # lower_bound = sqrt(2) * 2^{511 + 128*x} + # upper_bound = 2^{512 + 128*x} - 1 + x = (N - 512) >> 7; + # We need to approximate the sqrt(2) in the lower_bound by an integer + # expression because floating point math overflows with these numbers + lower_bound = divmod(14142135623730950489L * (2L ** (511 + 128*x)), + 10000000000000000000L)[0] + upper_bound = (1L << (512 + 128*x)) - 1 + # Randomly choose X in calculated range + X = getRandomRange (lower_bound, upper_bound, randfunc) + + # generate p1 and p2 + p = [0, 0] + for i in (0, 1): + # randomly choose 101-bit y + y = getRandomNBitInteger (101, randfunc) + # initialize the field for sieving + field = [0] * 5 * len (sieve_base) + # sieve the field + for prime in sieve_base: + offset = y % prime + for j in xrange ((prime - offset) % prime, len (field), prime): + field[j] = 1 + + # look for suitable p[i] starting at y + result = 0 + for j in range(len(field)): + composite = field[j] + # look for next canidate + if composite: + continue + tmp = y + j + result = _rabinMillerTest (tmp, rabin_miller_rounds) + if result > 0: + p[i] = tmp + break + if result == 0: + raise RuntimeError ("Couln't find prime in field. " + "Developer: Increase field_size") + + # Calculate R + # R = (p2^{-1} mod p1) * p2 - (p1^{-1} mod p2) * p1 + tmp1 = inverse (p[1], p[0]) * p[1] # (p2^-1 mod p1)*p2 + tmp2 = inverse (p[0], p[1]) * p[0] # (p1^-1 mod p2)*p1 + R = tmp1 - tmp2 # (p2^-1 mod p1)*p2 - (p1^-1 mod p2)*p1 + + # search for final prime number starting by Y0 + # Y0 = X + (R - X mod p1p2) + increment = p[0] * p[1] + X = X + (R - (X % increment)) + while 1: + is_possible_prime = 1 + # first check candidate against sieve_base + for prime in sieve_base: + if (X % prime) == 0: + is_possible_prime = 0 + break + # if e is given make sure that e and X-1 are coprime + # this is not necessarily a strong prime criterion but useful when + # creating them for RSA where the p-1 and q-1 should be coprime to + # the public exponent e + if e and is_possible_prime: + if e & 1: + if GCD (e, X-1) != 1: + is_possible_prime = 0 + else: + if GCD (e, divmod((X-1),2)[0]) != 1: + is_possible_prime = 0 + + # do some Rabin-Miller-Tests + if is_possible_prime: + result = _rabinMillerTest (X, rabin_miller_rounds) + if result > 0: + break + X += increment + # abort when X has more bits than requested + # TODO: maybe we shouldn't abort but rather start over. + if X >= 1L << N: + raise RuntimeError ("Couln't find prime in field. " + "Developer: Increase field_size") + return X + +def isPrime(N, false_positive_prob=1e-6, randfunc=None): + """isPrime(N:long, false_positive_prob:float, randfunc:callable):bool + Return true if N is prime. + + The optional false_positive_prob is the statistical probability + that true is returned even though it is not (pseudo-prime). + It defaults to 1e-6 (less than 1:1000000). + Note that the real probability of a false-positive is far less. This is + just the mathematically provable limit. + + If randfunc is omitted, then Random.new().read is used. + """ + if _fastmath is not None: + return _fastmath.isPrime(long(N), false_positive_prob, randfunc) + + if N < 3 or N & 1 == 0: + return N == 2 + for p in sieve_base: + if N == p: + return 1 + if N % p == 0: + return 0 + + rounds = int(math.ceil(-math.log(false_positive_prob)/math.log(4))) + return _rabinMillerTest(N, rounds, randfunc) + + +# Improved conversion functions contributed by Barry Warsaw, after +# careful benchmarking + +import struct + +def long_to_bytes(n, blocksize=0): + """long_to_bytes(n:long, blocksize:int) : string + Convert a long integer to a byte string. + + If optional blocksize is given and greater than zero, pad the front of the + byte string with binary zeros so that the length is a multiple of + blocksize. + """ + # after much testing, this algorithm was deemed to be the fastest + s = b('') + n = long(n) + pack = struct.pack + while n > 0: + s = pack('>I', n & 0xffffffffL) + s + n = n >> 32 + # strip off leading zeros + for i in range(len(s)): + if s[i] != b('\000')[0]: + break + else: + # only happens when n == 0 + s = b('\000') + i = 0 + s = s[i:] + # add back some pad bytes. this could be done more efficiently w.r.t. the + # de-padding being done above, but sigh... + if blocksize > 0 and len(s) % blocksize: + s = (blocksize - len(s) % blocksize) * b('\000') + s + return s + +def bytes_to_long(s): + """bytes_to_long(string) : long + Convert a byte string to a long integer. + + This is (essentially) the inverse of long_to_bytes(). + """ + acc = 0L + unpack = struct.unpack + length = len(s) + if length % 4: + extra = (4 - length % 4) + s = b('\000') * extra + s + length = length + extra + for i in range(0, length, 4): + acc = (acc << 32) + unpack('>I', s[i:i+4])[0] + return acc + +# For backwards compatibility... +import warnings +def long2str(n, blocksize=0): + warnings.warn("long2str() has been replaced by long_to_bytes()") + return long_to_bytes(n, blocksize) +def str2long(s): + warnings.warn("str2long() has been replaced by bytes_to_long()") + return bytes_to_long(s) + +def _import_Random(): + # This is called in a function instead of at the module level in order to + # avoid problems with recursive imports + global Random, StrongRandom + from Crypto import Random + from Crypto.Random.random import StrongRandom + + + +# The first 10000 primes used for checking primality. +# This should be enough to eliminate most of the odd +# numbers before needing to do a Rabin-Miller test at all. +sieve_base = ( + 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, + 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, + 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, + 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, + 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, + 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, + 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, + 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, + 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, + 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, + 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, + 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, + 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, + 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, + 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, + 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, + 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, + 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, + 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, + 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, + 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, + 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, + 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, + 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, + 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, + 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, + 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, + 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, + 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, + 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, + 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053, + 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129, + 2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, + 2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287, + 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, + 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, + 2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531, + 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617, + 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, + 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, + 2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801, 2803, 2819, + 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903, + 2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, + 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079, + 3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181, + 3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257, + 3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331, + 3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413, + 3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, + 3517, 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, + 3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643, + 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727, + 3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821, + 3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907, + 3911, 3917, 3919, 3923, 3929, 3931, 3943, 3947, 3967, 3989, + 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057, + 4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139, + 4153, 4157, 4159, 4177, 4201, 4211, 4217, 4219, 4229, 4231, + 4241, 4243, 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297, + 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409, + 4421, 4423, 4441, 4447, 4451, 4457, 4463, 4481, 4483, 4493, + 4507, 4513, 4517, 4519, 4523, 4547, 4549, 4561, 4567, 4583, + 4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657, + 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751, + 4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813, 4817, 4831, + 4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, 4933, 4937, + 4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003, + 5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087, + 5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179, + 5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279, + 5281, 5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387, + 5393, 5399, 5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443, + 5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, 5519, 5521, + 5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639, + 5641, 5647, 5651, 5653, 5657, 5659, 5669, 5683, 5689, 5693, + 5701, 5711, 5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791, + 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857, + 5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939, + 5953, 5981, 5987, 6007, 6011, 6029, 6037, 6043, 6047, 6053, + 6067, 6073, 6079, 6089, 6091, 6101, 6113, 6121, 6131, 6133, + 6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221, + 6229, 6247, 6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301, + 6311, 6317, 6323, 6329, 6337, 6343, 6353, 6359, 6361, 6367, + 6373, 6379, 6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473, + 6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571, + 6577, 6581, 6599, 6607, 6619, 6637, 6653, 6659, 6661, 6673, + 6679, 6689, 6691, 6701, 6703, 6709, 6719, 6733, 6737, 6761, + 6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833, + 6841, 6857, 6863, 6869, 6871, 6883, 6899, 6907, 6911, 6917, + 6947, 6949, 6959, 6961, 6967, 6971, 6977, 6983, 6991, 6997, + 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103, + 7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207, + 7211, 7213, 7219, 7229, 7237, 7243, 7247, 7253, 7283, 7297, + 7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369, 7393, 7411, + 7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499, + 7507, 7517, 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561, + 7573, 7577, 7583, 7589, 7591, 7603, 7607, 7621, 7639, 7643, + 7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, 7723, + 7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829, + 7841, 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919, + 7927, 7933, 7937, 7949, 7951, 7963, 7993, 8009, 8011, 8017, + 8039, 8053, 8059, 8069, 8081, 8087, 8089, 8093, 8101, 8111, + 8117, 8123, 8147, 8161, 8167, 8171, 8179, 8191, 8209, 8219, + 8221, 8231, 8233, 8237, 8243, 8263, 8269, 8273, 8287, 8291, + 8293, 8297, 8311, 8317, 8329, 8353, 8363, 8369, 8377, 8387, + 8389, 8419, 8423, 8429, 8431, 8443, 8447, 8461, 8467, 8501, + 8513, 8521, 8527, 8537, 8539, 8543, 8563, 8573, 8581, 8597, + 8599, 8609, 8623, 8627, 8629, 8641, 8647, 8663, 8669, 8677, + 8681, 8689, 8693, 8699, 8707, 8713, 8719, 8731, 8737, 8741, + 8747, 8753, 8761, 8779, 8783, 8803, 8807, 8819, 8821, 8831, + 8837, 8839, 8849, 8861, 8863, 8867, 8887, 8893, 8923, 8929, + 8933, 8941, 8951, 8963, 8969, 8971, 8999, 9001, 9007, 9011, + 9013, 9029, 9041, 9043, 9049, 9059, 9067, 9091, 9103, 9109, + 9127, 9133, 9137, 9151, 9157, 9161, 9173, 9181, 9187, 9199, + 9203, 9209, 9221, 9227, 9239, 9241, 9257, 9277, 9281, 9283, + 9293, 9311, 9319, 9323, 9337, 9341, 9343, 9349, 9371, 9377, + 9391, 9397, 9403, 9413, 9419, 9421, 9431, 9433, 9437, 9439, + 9461, 9463, 9467, 9473, 9479, 9491, 9497, 9511, 9521, 9533, + 9539, 9547, 9551, 9587, 9601, 9613, 9619, 9623, 9629, 9631, + 9643, 9649, 9661, 9677, 9679, 9689, 9697, 9719, 9721, 9733, + 9739, 9743, 9749, 9767, 9769, 9781, 9787, 9791, 9803, 9811, + 9817, 9829, 9833, 9839, 9851, 9857, 9859, 9871, 9883, 9887, + 9901, 9907, 9923, 9929, 9931, 9941, 9949, 9967, 9973, 10007, + 10009, 10037, 10039, 10061, 10067, 10069, 10079, 10091, 10093, 10099, + 10103, 10111, 10133, 10139, 10141, 10151, 10159, 10163, 10169, 10177, + 10181, 10193, 10211, 10223, 10243, 10247, 10253, 10259, 10267, 10271, + 10273, 10289, 10301, 10303, 10313, 10321, 10331, 10333, 10337, 10343, + 10357, 10369, 10391, 10399, 10427, 10429, 10433, 10453, 10457, 10459, + 10463, 10477, 10487, 10499, 10501, 10513, 10529, 10531, 10559, 10567, + 10589, 10597, 10601, 10607, 10613, 10627, 10631, 10639, 10651, 10657, + 10663, 10667, 10687, 10691, 10709, 10711, 10723, 10729, 10733, 10739, + 10753, 10771, 10781, 10789, 10799, 10831, 10837, 10847, 10853, 10859, + 10861, 10867, 10883, 10889, 10891, 10903, 10909, 10937, 10939, 10949, + 10957, 10973, 10979, 10987, 10993, 11003, 11027, 11047, 11057, 11059, + 11069, 11071, 11083, 11087, 11093, 11113, 11117, 11119, 11131, 11149, + 11159, 11161, 11171, 11173, 11177, 11197, 11213, 11239, 11243, 11251, + 11257, 11261, 11273, 11279, 11287, 11299, 11311, 11317, 11321, 11329, + 11351, 11353, 11369, 11383, 11393, 11399, 11411, 11423, 11437, 11443, + 11447, 11467, 11471, 11483, 11489, 11491, 11497, 11503, 11519, 11527, + 11549, 11551, 11579, 11587, 11593, 11597, 11617, 11621, 11633, 11657, + 11677, 11681, 11689, 11699, 11701, 11717, 11719, 11731, 11743, 11777, + 11779, 11783, 11789, 11801, 11807, 11813, 11821, 11827, 11831, 11833, + 11839, 11863, 11867, 11887, 11897, 11903, 11909, 11923, 11927, 11933, + 11939, 11941, 11953, 11959, 11969, 11971, 11981, 11987, 12007, 12011, + 12037, 12041, 12043, 12049, 12071, 12073, 12097, 12101, 12107, 12109, + 12113, 12119, 12143, 12149, 12157, 12161, 12163, 12197, 12203, 12211, + 12227, 12239, 12241, 12251, 12253, 12263, 12269, 12277, 12281, 12289, + 12301, 12323, 12329, 12343, 12347, 12373, 12377, 12379, 12391, 12401, + 12409, 12413, 12421, 12433, 12437, 12451, 12457, 12473, 12479, 12487, + 12491, 12497, 12503, 12511, 12517, 12527, 12539, 12541, 12547, 12553, + 12569, 12577, 12583, 12589, 12601, 12611, 12613, 12619, 12637, 12641, + 12647, 12653, 12659, 12671, 12689, 12697, 12703, 12713, 12721, 12739, + 12743, 12757, 12763, 12781, 12791, 12799, 12809, 12821, 12823, 12829, + 12841, 12853, 12889, 12893, 12899, 12907, 12911, 12917, 12919, 12923, + 12941, 12953, 12959, 12967, 12973, 12979, 12983, 13001, 13003, 13007, + 13009, 13033, 13037, 13043, 13049, 13063, 13093, 13099, 13103, 13109, + 13121, 13127, 13147, 13151, 13159, 13163, 13171, 13177, 13183, 13187, + 13217, 13219, 13229, 13241, 13249, 13259, 13267, 13291, 13297, 13309, + 13313, 13327, 13331, 13337, 13339, 13367, 13381, 13397, 13399, 13411, + 13417, 13421, 13441, 13451, 13457, 13463, 13469, 13477, 13487, 13499, + 13513, 13523, 13537, 13553, 13567, 13577, 13591, 13597, 13613, 13619, + 13627, 13633, 13649, 13669, 13679, 13681, 13687, 13691, 13693, 13697, + 13709, 13711, 13721, 13723, 13729, 13751, 13757, 13759, 13763, 13781, + 13789, 13799, 13807, 13829, 13831, 13841, 13859, 13873, 13877, 13879, + 13883, 13901, 13903, 13907, 13913, 13921, 13931, 13933, 13963, 13967, + 13997, 13999, 14009, 14011, 14029, 14033, 14051, 14057, 14071, 14081, + 14083, 14087, 14107, 14143, 14149, 14153, 14159, 14173, 14177, 14197, + 14207, 14221, 14243, 14249, 14251, 14281, 14293, 14303, 14321, 14323, + 14327, 14341, 14347, 14369, 14387, 14389, 14401, 14407, 14411, 14419, + 14423, 14431, 14437, 14447, 14449, 14461, 14479, 14489, 14503, 14519, + 14533, 14537, 14543, 14549, 14551, 14557, 14561, 14563, 14591, 14593, + 14621, 14627, 14629, 14633, 14639, 14653, 14657, 14669, 14683, 14699, + 14713, 14717, 14723, 14731, 14737, 14741, 14747, 14753, 14759, 14767, + 14771, 14779, 14783, 14797, 14813, 14821, 14827, 14831, 14843, 14851, + 14867, 14869, 14879, 14887, 14891, 14897, 14923, 14929, 14939, 14947, + 14951, 14957, 14969, 14983, 15013, 15017, 15031, 15053, 15061, 15073, + 15077, 15083, 15091, 15101, 15107, 15121, 15131, 15137, 15139, 15149, + 15161, 15173, 15187, 15193, 15199, 15217, 15227, 15233, 15241, 15259, + 15263, 15269, 15271, 15277, 15287, 15289, 15299, 15307, 15313, 15319, + 15329, 15331, 15349, 15359, 15361, 15373, 15377, 15383, 15391, 15401, + 15413, 15427, 15439, 15443, 15451, 15461, 15467, 15473, 15493, 15497, + 15511, 15527, 15541, 15551, 15559, 15569, 15581, 15583, 15601, 15607, + 15619, 15629, 15641, 15643, 15647, 15649, 15661, 15667, 15671, 15679, + 15683, 15727, 15731, 15733, 15737, 15739, 15749, 15761, 15767, 15773, + 15787, 15791, 15797, 15803, 15809, 15817, 15823, 15859, 15877, 15881, + 15887, 15889, 15901, 15907, 15913, 15919, 15923, 15937, 15959, 15971, + 15973, 15991, 16001, 16007, 16033, 16057, 16061, 16063, 16067, 16069, + 16073, 16087, 16091, 16097, 16103, 16111, 16127, 16139, 16141, 16183, + 16187, 16189, 16193, 16217, 16223, 16229, 16231, 16249, 16253, 16267, + 16273, 16301, 16319, 16333, 16339, 16349, 16361, 16363, 16369, 16381, + 16411, 16417, 16421, 16427, 16433, 16447, 16451, 16453, 16477, 16481, + 16487, 16493, 16519, 16529, 16547, 16553, 16561, 16567, 16573, 16603, + 16607, 16619, 16631, 16633, 16649, 16651, 16657, 16661, 16673, 16691, + 16693, 16699, 16703, 16729, 16741, 16747, 16759, 16763, 16787, 16811, + 16823, 16829, 16831, 16843, 16871, 16879, 16883, 16889, 16901, 16903, + 16921, 16927, 16931, 16937, 16943, 16963, 16979, 16981, 16987, 16993, + 17011, 17021, 17027, 17029, 17033, 17041, 17047, 17053, 17077, 17093, + 17099, 17107, 17117, 17123, 17137, 17159, 17167, 17183, 17189, 17191, + 17203, 17207, 17209, 17231, 17239, 17257, 17291, 17293, 17299, 17317, + 17321, 17327, 17333, 17341, 17351, 17359, 17377, 17383, 17387, 17389, + 17393, 17401, 17417, 17419, 17431, 17443, 17449, 17467, 17471, 17477, + 17483, 17489, 17491, 17497, 17509, 17519, 17539, 17551, 17569, 17573, + 17579, 17581, 17597, 17599, 17609, 17623, 17627, 17657, 17659, 17669, + 17681, 17683, 17707, 17713, 17729, 17737, 17747, 17749, 17761, 17783, + 17789, 17791, 17807, 17827, 17837, 17839, 17851, 17863, 17881, 17891, + 17903, 17909, 17911, 17921, 17923, 17929, 17939, 17957, 17959, 17971, + 17977, 17981, 17987, 17989, 18013, 18041, 18043, 18047, 18049, 18059, + 18061, 18077, 18089, 18097, 18119, 18121, 18127, 18131, 18133, 18143, + 18149, 18169, 18181, 18191, 18199, 18211, 18217, 18223, 18229, 18233, + 18251, 18253, 18257, 18269, 18287, 18289, 18301, 18307, 18311, 18313, + 18329, 18341, 18353, 18367, 18371, 18379, 18397, 18401, 18413, 18427, + 18433, 18439, 18443, 18451, 18457, 18461, 18481, 18493, 18503, 18517, + 18521, 18523, 18539, 18541, 18553, 18583, 18587, 18593, 18617, 18637, + 18661, 18671, 18679, 18691, 18701, 18713, 18719, 18731, 18743, 18749, + 18757, 18773, 18787, 18793, 18797, 18803, 18839, 18859, 18869, 18899, + 18911, 18913, 18917, 18919, 18947, 18959, 18973, 18979, 19001, 19009, + 19013, 19031, 19037, 19051, 19069, 19073, 19079, 19081, 19087, 19121, + 19139, 19141, 19157, 19163, 19181, 19183, 19207, 19211, 19213, 19219, + 19231, 19237, 19249, 19259, 19267, 19273, 19289, 19301, 19309, 19319, + 19333, 19373, 19379, 19381, 19387, 19391, 19403, 19417, 19421, 19423, + 19427, 19429, 19433, 19441, 19447, 19457, 19463, 19469, 19471, 19477, + 19483, 19489, 19501, 19507, 19531, 19541, 19543, 19553, 19559, 19571, + 19577, 19583, 19597, 19603, 19609, 19661, 19681, 19687, 19697, 19699, + 19709, 19717, 19727, 19739, 19751, 19753, 19759, 19763, 19777, 19793, + 19801, 19813, 19819, 19841, 19843, 19853, 19861, 19867, 19889, 19891, + 19913, 19919, 19927, 19937, 19949, 19961, 19963, 19973, 19979, 19991, + 19993, 19997, 20011, 20021, 20023, 20029, 20047, 20051, 20063, 20071, + 20089, 20101, 20107, 20113, 20117, 20123, 20129, 20143, 20147, 20149, + 20161, 20173, 20177, 20183, 20201, 20219, 20231, 20233, 20249, 20261, + 20269, 20287, 20297, 20323, 20327, 20333, 20341, 20347, 20353, 20357, + 20359, 20369, 20389, 20393, 20399, 20407, 20411, 20431, 20441, 20443, + 20477, 20479, 20483, 20507, 20509, 20521, 20533, 20543, 20549, 20551, + 20563, 20593, 20599, 20611, 20627, 20639, 20641, 20663, 20681, 20693, + 20707, 20717, 20719, 20731, 20743, 20747, 20749, 20753, 20759, 20771, + 20773, 20789, 20807, 20809, 20849, 20857, 20873, 20879, 20887, 20897, + 20899, 20903, 20921, 20929, 20939, 20947, 20959, 20963, 20981, 20983, + 21001, 21011, 21013, 21017, 21019, 21023, 21031, 21059, 21061, 21067, + 21089, 21101, 21107, 21121, 21139, 21143, 21149, 21157, 21163, 21169, + 21179, 21187, 21191, 21193, 21211, 21221, 21227, 21247, 21269, 21277, + 21283, 21313, 21317, 21319, 21323, 21341, 21347, 21377, 21379, 21383, + 21391, 21397, 21401, 21407, 21419, 21433, 21467, 21481, 21487, 21491, + 21493, 21499, 21503, 21517, 21521, 21523, 21529, 21557, 21559, 21563, + 21569, 21577, 21587, 21589, 21599, 21601, 21611, 21613, 21617, 21647, + 21649, 21661, 21673, 21683, 21701, 21713, 21727, 21737, 21739, 21751, + 21757, 21767, 21773, 21787, 21799, 21803, 21817, 21821, 21839, 21841, + 21851, 21859, 21863, 21871, 21881, 21893, 21911, 21929, 21937, 21943, + 21961, 21977, 21991, 21997, 22003, 22013, 22027, 22031, 22037, 22039, + 22051, 22063, 22067, 22073, 22079, 22091, 22093, 22109, 22111, 22123, + 22129, 22133, 22147, 22153, 22157, 22159, 22171, 22189, 22193, 22229, + 22247, 22259, 22271, 22273, 22277, 22279, 22283, 22291, 22303, 22307, + 22343, 22349, 22367, 22369, 22381, 22391, 22397, 22409, 22433, 22441, + 22447, 22453, 22469, 22481, 22483, 22501, 22511, 22531, 22541, 22543, + 22549, 22567, 22571, 22573, 22613, 22619, 22621, 22637, 22639, 22643, + 22651, 22669, 22679, 22691, 22697, 22699, 22709, 22717, 22721, 22727, + 22739, 22741, 22751, 22769, 22777, 22783, 22787, 22807, 22811, 22817, + 22853, 22859, 22861, 22871, 22877, 22901, 22907, 22921, 22937, 22943, + 22961, 22963, 22973, 22993, 23003, 23011, 23017, 23021, 23027, 23029, + 23039, 23041, 23053, 23057, 23059, 23063, 23071, 23081, 23087, 23099, + 23117, 23131, 23143, 23159, 23167, 23173, 23189, 23197, 23201, 23203, + 23209, 23227, 23251, 23269, 23279, 23291, 23293, 23297, 23311, 23321, + 23327, 23333, 23339, 23357, 23369, 23371, 23399, 23417, 23431, 23447, + 23459, 23473, 23497, 23509, 23531, 23537, 23539, 23549, 23557, 23561, + 23563, 23567, 23581, 23593, 23599, 23603, 23609, 23623, 23627, 23629, + 23633, 23663, 23669, 23671, 23677, 23687, 23689, 23719, 23741, 23743, + 23747, 23753, 23761, 23767, 23773, 23789, 23801, 23813, 23819, 23827, + 23831, 23833, 23857, 23869, 23873, 23879, 23887, 23893, 23899, 23909, + 23911, 23917, 23929, 23957, 23971, 23977, 23981, 23993, 24001, 24007, + 24019, 24023, 24029, 24043, 24049, 24061, 24071, 24077, 24083, 24091, + 24097, 24103, 24107, 24109, 24113, 24121, 24133, 24137, 24151, 24169, + 24179, 24181, 24197, 24203, 24223, 24229, 24239, 24247, 24251, 24281, + 24317, 24329, 24337, 24359, 24371, 24373, 24379, 24391, 24407, 24413, + 24419, 24421, 24439, 24443, 24469, 24473, 24481, 24499, 24509, 24517, + 24527, 24533, 24547, 24551, 24571, 24593, 24611, 24623, 24631, 24659, + 24671, 24677, 24683, 24691, 24697, 24709, 24733, 24749, 24763, 24767, + 24781, 24793, 24799, 24809, 24821, 24841, 24847, 24851, 24859, 24877, + 24889, 24907, 24917, 24919, 24923, 24943, 24953, 24967, 24971, 24977, + 24979, 24989, 25013, 25031, 25033, 25037, 25057, 25073, 25087, 25097, + 25111, 25117, 25121, 25127, 25147, 25153, 25163, 25169, 25171, 25183, + 25189, 25219, 25229, 25237, 25243, 25247, 25253, 25261, 25301, 25303, + 25307, 25309, 25321, 25339, 25343, 25349, 25357, 25367, 25373, 25391, + 25409, 25411, 25423, 25439, 25447, 25453, 25457, 25463, 25469, 25471, + 25523, 25537, 25541, 25561, 25577, 25579, 25583, 25589, 25601, 25603, + 25609, 25621, 25633, 25639, 25643, 25657, 25667, 25673, 25679, 25693, + 25703, 25717, 25733, 25741, 25747, 25759, 25763, 25771, 25793, 25799, + 25801, 25819, 25841, 25847, 25849, 25867, 25873, 25889, 25903, 25913, + 25919, 25931, 25933, 25939, 25943, 25951, 25969, 25981, 25997, 25999, + 26003, 26017, 26021, 26029, 26041, 26053, 26083, 26099, 26107, 26111, + 26113, 26119, 26141, 26153, 26161, 26171, 26177, 26183, 26189, 26203, + 26209, 26227, 26237, 26249, 26251, 26261, 26263, 26267, 26293, 26297, + 26309, 26317, 26321, 26339, 26347, 26357, 26371, 26387, 26393, 26399, + 26407, 26417, 26423, 26431, 26437, 26449, 26459, 26479, 26489, 26497, + 26501, 26513, 26539, 26557, 26561, 26573, 26591, 26597, 26627, 26633, + 26641, 26647, 26669, 26681, 26683, 26687, 26693, 26699, 26701, 26711, + 26713, 26717, 26723, 26729, 26731, 26737, 26759, 26777, 26783, 26801, + 26813, 26821, 26833, 26839, 26849, 26861, 26863, 26879, 26881, 26891, + 26893, 26903, 26921, 26927, 26947, 26951, 26953, 26959, 26981, 26987, + 26993, 27011, 27017, 27031, 27043, 27059, 27061, 27067, 27073, 27077, + 27091, 27103, 27107, 27109, 27127, 27143, 27179, 27191, 27197, 27211, + 27239, 27241, 27253, 27259, 27271, 27277, 27281, 27283, 27299, 27329, + 27337, 27361, 27367, 27397, 27407, 27409, 27427, 27431, 27437, 27449, + 27457, 27479, 27481, 27487, 27509, 27527, 27529, 27539, 27541, 27551, + 27581, 27583, 27611, 27617, 27631, 27647, 27653, 27673, 27689, 27691, + 27697, 27701, 27733, 27737, 27739, 27743, 27749, 27751, 27763, 27767, + 27773, 27779, 27791, 27793, 27799, 27803, 27809, 27817, 27823, 27827, + 27847, 27851, 27883, 27893, 27901, 27917, 27919, 27941, 27943, 27947, + 27953, 27961, 27967, 27983, 27997, 28001, 28019, 28027, 28031, 28051, + 28057, 28069, 28081, 28087, 28097, 28099, 28109, 28111, 28123, 28151, + 28163, 28181, 28183, 28201, 28211, 28219, 28229, 28277, 28279, 28283, + 28289, 28297, 28307, 28309, 28319, 28349, 28351, 28387, 28393, 28403, + 28409, 28411, 28429, 28433, 28439, 28447, 28463, 28477, 28493, 28499, + 28513, 28517, 28537, 28541, 28547, 28549, 28559, 28571, 28573, 28579, + 28591, 28597, 28603, 28607, 28619, 28621, 28627, 28631, 28643, 28649, + 28657, 28661, 28663, 28669, 28687, 28697, 28703, 28711, 28723, 28729, + 28751, 28753, 28759, 28771, 28789, 28793, 28807, 28813, 28817, 28837, + 28843, 28859, 28867, 28871, 28879, 28901, 28909, 28921, 28927, 28933, + 28949, 28961, 28979, 29009, 29017, 29021, 29023, 29027, 29033, 29059, + 29063, 29077, 29101, 29123, 29129, 29131, 29137, 29147, 29153, 29167, + 29173, 29179, 29191, 29201, 29207, 29209, 29221, 29231, 29243, 29251, + 29269, 29287, 29297, 29303, 29311, 29327, 29333, 29339, 29347, 29363, + 29383, 29387, 29389, 29399, 29401, 29411, 29423, 29429, 29437, 29443, + 29453, 29473, 29483, 29501, 29527, 29531, 29537, 29567, 29569, 29573, + 29581, 29587, 29599, 29611, 29629, 29633, 29641, 29663, 29669, 29671, + 29683, 29717, 29723, 29741, 29753, 29759, 29761, 29789, 29803, 29819, + 29833, 29837, 29851, 29863, 29867, 29873, 29879, 29881, 29917, 29921, + 29927, 29947, 29959, 29983, 29989, 30011, 30013, 30029, 30047, 30059, + 30071, 30089, 30091, 30097, 30103, 30109, 30113, 30119, 30133, 30137, + 30139, 30161, 30169, 30181, 30187, 30197, 30203, 30211, 30223, 30241, + 30253, 30259, 30269, 30271, 30293, 30307, 30313, 30319, 30323, 30341, + 30347, 30367, 30389, 30391, 30403, 30427, 30431, 30449, 30467, 30469, + 30491, 30493, 30497, 30509, 30517, 30529, 30539, 30553, 30557, 30559, + 30577, 30593, 30631, 30637, 30643, 30649, 30661, 30671, 30677, 30689, + 30697, 30703, 30707, 30713, 30727, 30757, 30763, 30773, 30781, 30803, + 30809, 30817, 30829, 30839, 30841, 30851, 30853, 30859, 30869, 30871, + 30881, 30893, 30911, 30931, 30937, 30941, 30949, 30971, 30977, 30983, + 31013, 31019, 31033, 31039, 31051, 31063, 31069, 31079, 31081, 31091, + 31121, 31123, 31139, 31147, 31151, 31153, 31159, 31177, 31181, 31183, + 31189, 31193, 31219, 31223, 31231, 31237, 31247, 31249, 31253, 31259, + 31267, 31271, 31277, 31307, 31319, 31321, 31327, 31333, 31337, 31357, + 31379, 31387, 31391, 31393, 31397, 31469, 31477, 31481, 31489, 31511, + 31513, 31517, 31531, 31541, 31543, 31547, 31567, 31573, 31583, 31601, + 31607, 31627, 31643, 31649, 31657, 31663, 31667, 31687, 31699, 31721, + 31723, 31727, 31729, 31741, 31751, 31769, 31771, 31793, 31799, 31817, + 31847, 31849, 31859, 31873, 31883, 31891, 31907, 31957, 31963, 31973, + 31981, 31991, 32003, 32009, 32027, 32029, 32051, 32057, 32059, 32063, + 32069, 32077, 32083, 32089, 32099, 32117, 32119, 32141, 32143, 32159, + 32173, 32183, 32189, 32191, 32203, 32213, 32233, 32237, 32251, 32257, + 32261, 32297, 32299, 32303, 32309, 32321, 32323, 32327, 32341, 32353, + 32359, 32363, 32369, 32371, 32377, 32381, 32401, 32411, 32413, 32423, + 32429, 32441, 32443, 32467, 32479, 32491, 32497, 32503, 32507, 32531, + 32533, 32537, 32561, 32563, 32569, 32573, 32579, 32587, 32603, 32609, + 32611, 32621, 32633, 32647, 32653, 32687, 32693, 32707, 32713, 32717, + 32719, 32749, 32771, 32779, 32783, 32789, 32797, 32801, 32803, 32831, + 32833, 32839, 32843, 32869, 32887, 32909, 32911, 32917, 32933, 32939, + 32941, 32957, 32969, 32971, 32983, 32987, 32993, 32999, 33013, 33023, + 33029, 33037, 33049, 33053, 33071, 33073, 33083, 33091, 33107, 33113, + 33119, 33149, 33151, 33161, 33179, 33181, 33191, 33199, 33203, 33211, + 33223, 33247, 33287, 33289, 33301, 33311, 33317, 33329, 33331, 33343, + 33347, 33349, 33353, 33359, 33377, 33391, 33403, 33409, 33413, 33427, + 33457, 33461, 33469, 33479, 33487, 33493, 33503, 33521, 33529, 33533, + 33547, 33563, 33569, 33577, 33581, 33587, 33589, 33599, 33601, 33613, + 33617, 33619, 33623, 33629, 33637, 33641, 33647, 33679, 33703, 33713, + 33721, 33739, 33749, 33751, 33757, 33767, 33769, 33773, 33791, 33797, + 33809, 33811, 33827, 33829, 33851, 33857, 33863, 33871, 33889, 33893, + 33911, 33923, 33931, 33937, 33941, 33961, 33967, 33997, 34019, 34031, + 34033, 34039, 34057, 34061, 34123, 34127, 34129, 34141, 34147, 34157, + 34159, 34171, 34183, 34211, 34213, 34217, 34231, 34253, 34259, 34261, + 34267, 34273, 34283, 34297, 34301, 34303, 34313, 34319, 34327, 34337, + 34351, 34361, 34367, 34369, 34381, 34403, 34421, 34429, 34439, 34457, + 34469, 34471, 34483, 34487, 34499, 34501, 34511, 34513, 34519, 34537, + 34543, 34549, 34583, 34589, 34591, 34603, 34607, 34613, 34631, 34649, + 34651, 34667, 34673, 34679, 34687, 34693, 34703, 34721, 34729, 34739, + 34747, 34757, 34759, 34763, 34781, 34807, 34819, 34841, 34843, 34847, + 34849, 34871, 34877, 34883, 34897, 34913, 34919, 34939, 34949, 34961, + 34963, 34981, 35023, 35027, 35051, 35053, 35059, 35069, 35081, 35083, + 35089, 35099, 35107, 35111, 35117, 35129, 35141, 35149, 35153, 35159, + 35171, 35201, 35221, 35227, 35251, 35257, 35267, 35279, 35281, 35291, + 35311, 35317, 35323, 35327, 35339, 35353, 35363, 35381, 35393, 35401, + 35407, 35419, 35423, 35437, 35447, 35449, 35461, 35491, 35507, 35509, + 35521, 35527, 35531, 35533, 35537, 35543, 35569, 35573, 35591, 35593, + 35597, 35603, 35617, 35671, 35677, 35729, 35731, 35747, 35753, 35759, + 35771, 35797, 35801, 35803, 35809, 35831, 35837, 35839, 35851, 35863, + 35869, 35879, 35897, 35899, 35911, 35923, 35933, 35951, 35963, 35969, + 35977, 35983, 35993, 35999, 36007, 36011, 36013, 36017, 36037, 36061, + 36067, 36073, 36083, 36097, 36107, 36109, 36131, 36137, 36151, 36161, + 36187, 36191, 36209, 36217, 36229, 36241, 36251, 36263, 36269, 36277, + 36293, 36299, 36307, 36313, 36319, 36341, 36343, 36353, 36373, 36383, + 36389, 36433, 36451, 36457, 36467, 36469, 36473, 36479, 36493, 36497, + 36523, 36527, 36529, 36541, 36551, 36559, 36563, 36571, 36583, 36587, + 36599, 36607, 36629, 36637, 36643, 36653, 36671, 36677, 36683, 36691, + 36697, 36709, 36713, 36721, 36739, 36749, 36761, 36767, 36779, 36781, + 36787, 36791, 36793, 36809, 36821, 36833, 36847, 36857, 36871, 36877, + 36887, 36899, 36901, 36913, 36919, 36923, 36929, 36931, 36943, 36947, + 36973, 36979, 36997, 37003, 37013, 37019, 37021, 37039, 37049, 37057, + 37061, 37087, 37097, 37117, 37123, 37139, 37159, 37171, 37181, 37189, + 37199, 37201, 37217, 37223, 37243, 37253, 37273, 37277, 37307, 37309, + 37313, 37321, 37337, 37339, 37357, 37361, 37363, 37369, 37379, 37397, + 37409, 37423, 37441, 37447, 37463, 37483, 37489, 37493, 37501, 37507, + 37511, 37517, 37529, 37537, 37547, 37549, 37561, 37567, 37571, 37573, + 37579, 37589, 37591, 37607, 37619, 37633, 37643, 37649, 37657, 37663, + 37691, 37693, 37699, 37717, 37747, 37781, 37783, 37799, 37811, 37813, + 37831, 37847, 37853, 37861, 37871, 37879, 37889, 37897, 37907, 37951, + 37957, 37963, 37967, 37987, 37991, 37993, 37997, 38011, 38039, 38047, + 38053, 38069, 38083, 38113, 38119, 38149, 38153, 38167, 38177, 38183, + 38189, 38197, 38201, 38219, 38231, 38237, 38239, 38261, 38273, 38281, + 38287, 38299, 38303, 38317, 38321, 38327, 38329, 38333, 38351, 38371, + 38377, 38393, 38431, 38447, 38449, 38453, 38459, 38461, 38501, 38543, + 38557, 38561, 38567, 38569, 38593, 38603, 38609, 38611, 38629, 38639, + 38651, 38653, 38669, 38671, 38677, 38693, 38699, 38707, 38711, 38713, + 38723, 38729, 38737, 38747, 38749, 38767, 38783, 38791, 38803, 38821, + 38833, 38839, 38851, 38861, 38867, 38873, 38891, 38903, 38917, 38921, + 38923, 38933, 38953, 38959, 38971, 38977, 38993, 39019, 39023, 39041, + 39043, 39047, 39079, 39089, 39097, 39103, 39107, 39113, 39119, 39133, + 39139, 39157, 39161, 39163, 39181, 39191, 39199, 39209, 39217, 39227, + 39229, 39233, 39239, 39241, 39251, 39293, 39301, 39313, 39317, 39323, + 39341, 39343, 39359, 39367, 39371, 39373, 39383, 39397, 39409, 39419, + 39439, 39443, 39451, 39461, 39499, 39503, 39509, 39511, 39521, 39541, + 39551, 39563, 39569, 39581, 39607, 39619, 39623, 39631, 39659, 39667, + 39671, 39679, 39703, 39709, 39719, 39727, 39733, 39749, 39761, 39769, + 39779, 39791, 39799, 39821, 39827, 39829, 39839, 39841, 39847, 39857, + 39863, 39869, 39877, 39883, 39887, 39901, 39929, 39937, 39953, 39971, + 39979, 39983, 39989, 40009, 40013, 40031, 40037, 40039, 40063, 40087, + 40093, 40099, 40111, 40123, 40127, 40129, 40151, 40153, 40163, 40169, + 40177, 40189, 40193, 40213, 40231, 40237, 40241, 40253, 40277, 40283, + 40289, 40343, 40351, 40357, 40361, 40387, 40423, 40427, 40429, 40433, + 40459, 40471, 40483, 40487, 40493, 40499, 40507, 40519, 40529, 40531, + 40543, 40559, 40577, 40583, 40591, 40597, 40609, 40627, 40637, 40639, + 40693, 40697, 40699, 40709, 40739, 40751, 40759, 40763, 40771, 40787, + 40801, 40813, 40819, 40823, 40829, 40841, 40847, 40849, 40853, 40867, + 40879, 40883, 40897, 40903, 40927, 40933, 40939, 40949, 40961, 40973, + 40993, 41011, 41017, 41023, 41039, 41047, 41051, 41057, 41077, 41081, + 41113, 41117, 41131, 41141, 41143, 41149, 41161, 41177, 41179, 41183, + 41189, 41201, 41203, 41213, 41221, 41227, 41231, 41233, 41243, 41257, + 41263, 41269, 41281, 41299, 41333, 41341, 41351, 41357, 41381, 41387, + 41389, 41399, 41411, 41413, 41443, 41453, 41467, 41479, 41491, 41507, + 41513, 41519, 41521, 41539, 41543, 41549, 41579, 41593, 41597, 41603, + 41609, 41611, 41617, 41621, 41627, 41641, 41647, 41651, 41659, 41669, + 41681, 41687, 41719, 41729, 41737, 41759, 41761, 41771, 41777, 41801, + 41809, 41813, 41843, 41849, 41851, 41863, 41879, 41887, 41893, 41897, + 41903, 41911, 41927, 41941, 41947, 41953, 41957, 41959, 41969, 41981, + 41983, 41999, 42013, 42017, 42019, 42023, 42043, 42061, 42071, 42073, + 42083, 42089, 42101, 42131, 42139, 42157, 42169, 42179, 42181, 42187, + 42193, 42197, 42209, 42221, 42223, 42227, 42239, 42257, 42281, 42283, + 42293, 42299, 42307, 42323, 42331, 42337, 42349, 42359, 42373, 42379, + 42391, 42397, 42403, 42407, 42409, 42433, 42437, 42443, 42451, 42457, + 42461, 42463, 42467, 42473, 42487, 42491, 42499, 42509, 42533, 42557, + 42569, 42571, 42577, 42589, 42611, 42641, 42643, 42649, 42667, 42677, + 42683, 42689, 42697, 42701, 42703, 42709, 42719, 42727, 42737, 42743, + 42751, 42767, 42773, 42787, 42793, 42797, 42821, 42829, 42839, 42841, + 42853, 42859, 42863, 42899, 42901, 42923, 42929, 42937, 42943, 42953, + 42961, 42967, 42979, 42989, 43003, 43013, 43019, 43037, 43049, 43051, + 43063, 43067, 43093, 43103, 43117, 43133, 43151, 43159, 43177, 43189, + 43201, 43207, 43223, 43237, 43261, 43271, 43283, 43291, 43313, 43319, + 43321, 43331, 43391, 43397, 43399, 43403, 43411, 43427, 43441, 43451, + 43457, 43481, 43487, 43499, 43517, 43541, 43543, 43573, 43577, 43579, + 43591, 43597, 43607, 43609, 43613, 43627, 43633, 43649, 43651, 43661, + 43669, 43691, 43711, 43717, 43721, 43753, 43759, 43777, 43781, 43783, + 43787, 43789, 43793, 43801, 43853, 43867, 43889, 43891, 43913, 43933, + 43943, 43951, 43961, 43963, 43969, 43973, 43987, 43991, 43997, 44017, + 44021, 44027, 44029, 44041, 44053, 44059, 44071, 44087, 44089, 44101, + 44111, 44119, 44123, 44129, 44131, 44159, 44171, 44179, 44189, 44201, + 44203, 44207, 44221, 44249, 44257, 44263, 44267, 44269, 44273, 44279, + 44281, 44293, 44351, 44357, 44371, 44381, 44383, 44389, 44417, 44449, + 44453, 44483, 44491, 44497, 44501, 44507, 44519, 44531, 44533, 44537, + 44543, 44549, 44563, 44579, 44587, 44617, 44621, 44623, 44633, 44641, + 44647, 44651, 44657, 44683, 44687, 44699, 44701, 44711, 44729, 44741, + 44753, 44771, 44773, 44777, 44789, 44797, 44809, 44819, 44839, 44843, + 44851, 44867, 44879, 44887, 44893, 44909, 44917, 44927, 44939, 44953, + 44959, 44963, 44971, 44983, 44987, 45007, 45013, 45053, 45061, 45077, + 45083, 45119, 45121, 45127, 45131, 45137, 45139, 45161, 45179, 45181, + 45191, 45197, 45233, 45247, 45259, 45263, 45281, 45289, 45293, 45307, + 45317, 45319, 45329, 45337, 45341, 45343, 45361, 45377, 45389, 45403, + 45413, 45427, 45433, 45439, 45481, 45491, 45497, 45503, 45523, 45533, + 45541, 45553, 45557, 45569, 45587, 45589, 45599, 45613, 45631, 45641, + 45659, 45667, 45673, 45677, 45691, 45697, 45707, 45737, 45751, 45757, + 45763, 45767, 45779, 45817, 45821, 45823, 45827, 45833, 45841, 45853, + 45863, 45869, 45887, 45893, 45943, 45949, 45953, 45959, 45971, 45979, + 45989, 46021, 46027, 46049, 46051, 46061, 46073, 46091, 46093, 46099, + 46103, 46133, 46141, 46147, 46153, 46171, 46181, 46183, 46187, 46199, + 46219, 46229, 46237, 46261, 46271, 46273, 46279, 46301, 46307, 46309, + 46327, 46337, 46349, 46351, 46381, 46399, 46411, 46439, 46441, 46447, + 46451, 46457, 46471, 46477, 46489, 46499, 46507, 46511, 46523, 46549, + 46559, 46567, 46573, 46589, 46591, 46601, 46619, 46633, 46639, 46643, + 46649, 46663, 46679, 46681, 46687, 46691, 46703, 46723, 46727, 46747, + 46751, 46757, 46769, 46771, 46807, 46811, 46817, 46819, 46829, 46831, + 46853, 46861, 46867, 46877, 46889, 46901, 46919, 46933, 46957, 46993, + 46997, 47017, 47041, 47051, 47057, 47059, 47087, 47093, 47111, 47119, + 47123, 47129, 47137, 47143, 47147, 47149, 47161, 47189, 47207, 47221, + 47237, 47251, 47269, 47279, 47287, 47293, 47297, 47303, 47309, 47317, + 47339, 47351, 47353, 47363, 47381, 47387, 47389, 47407, 47417, 47419, + 47431, 47441, 47459, 47491, 47497, 47501, 47507, 47513, 47521, 47527, + 47533, 47543, 47563, 47569, 47581, 47591, 47599, 47609, 47623, 47629, + 47639, 47653, 47657, 47659, 47681, 47699, 47701, 47711, 47713, 47717, + 47737, 47741, 47743, 47777, 47779, 47791, 47797, 47807, 47809, 47819, + 47837, 47843, 47857, 47869, 47881, 47903, 47911, 47917, 47933, 47939, + 47947, 47951, 47963, 47969, 47977, 47981, 48017, 48023, 48029, 48049, + 48073, 48079, 48091, 48109, 48119, 48121, 48131, 48157, 48163, 48179, + 48187, 48193, 48197, 48221, 48239, 48247, 48259, 48271, 48281, 48299, + 48311, 48313, 48337, 48341, 48353, 48371, 48383, 48397, 48407, 48409, + 48413, 48437, 48449, 48463, 48473, 48479, 48481, 48487, 48491, 48497, + 48523, 48527, 48533, 48539, 48541, 48563, 48571, 48589, 48593, 48611, + 48619, 48623, 48647, 48649, 48661, 48673, 48677, 48679, 48731, 48733, + 48751, 48757, 48761, 48767, 48779, 48781, 48787, 48799, 48809, 48817, + 48821, 48823, 48847, 48857, 48859, 48869, 48871, 48883, 48889, 48907, + 48947, 48953, 48973, 48989, 48991, 49003, 49009, 49019, 49031, 49033, + 49037, 49043, 49057, 49069, 49081, 49103, 49109, 49117, 49121, 49123, + 49139, 49157, 49169, 49171, 49177, 49193, 49199, 49201, 49207, 49211, + 49223, 49253, 49261, 49277, 49279, 49297, 49307, 49331, 49333, 49339, + 49363, 49367, 49369, 49391, 49393, 49409, 49411, 49417, 49429, 49433, + 49451, 49459, 49463, 49477, 49481, 49499, 49523, 49529, 49531, 49537, + 49547, 49549, 49559, 49597, 49603, 49613, 49627, 49633, 49639, 49663, + 49667, 49669, 49681, 49697, 49711, 49727, 49739, 49741, 49747, 49757, + 49783, 49787, 49789, 49801, 49807, 49811, 49823, 49831, 49843, 49853, + 49871, 49877, 49891, 49919, 49921, 49927, 49937, 49939, 49943, 49957, + 49991, 49993, 49999, 50021, 50023, 50033, 50047, 50051, 50053, 50069, + 50077, 50087, 50093, 50101, 50111, 50119, 50123, 50129, 50131, 50147, + 50153, 50159, 50177, 50207, 50221, 50227, 50231, 50261, 50263, 50273, + 50287, 50291, 50311, 50321, 50329, 50333, 50341, 50359, 50363, 50377, + 50383, 50387, 50411, 50417, 50423, 50441, 50459, 50461, 50497, 50503, + 50513, 50527, 50539, 50543, 50549, 50551, 50581, 50587, 50591, 50593, + 50599, 50627, 50647, 50651, 50671, 50683, 50707, 50723, 50741, 50753, + 50767, 50773, 50777, 50789, 50821, 50833, 50839, 50849, 50857, 50867, + 50873, 50891, 50893, 50909, 50923, 50929, 50951, 50957, 50969, 50971, + 50989, 50993, 51001, 51031, 51043, 51047, 51059, 51061, 51071, 51109, + 51131, 51133, 51137, 51151, 51157, 51169, 51193, 51197, 51199, 51203, + 51217, 51229, 51239, 51241, 51257, 51263, 51283, 51287, 51307, 51329, + 51341, 51343, 51347, 51349, 51361, 51383, 51407, 51413, 51419, 51421, + 51427, 51431, 51437, 51439, 51449, 51461, 51473, 51479, 51481, 51487, + 51503, 51511, 51517, 51521, 51539, 51551, 51563, 51577, 51581, 51593, + 51599, 51607, 51613, 51631, 51637, 51647, 51659, 51673, 51679, 51683, + 51691, 51713, 51719, 51721, 51749, 51767, 51769, 51787, 51797, 51803, + 51817, 51827, 51829, 51839, 51853, 51859, 51869, 51871, 51893, 51899, + 51907, 51913, 51929, 51941, 51949, 51971, 51973, 51977, 51991, 52009, + 52021, 52027, 52051, 52057, 52067, 52069, 52081, 52103, 52121, 52127, + 52147, 52153, 52163, 52177, 52181, 52183, 52189, 52201, 52223, 52237, + 52249, 52253, 52259, 52267, 52289, 52291, 52301, 52313, 52321, 52361, + 52363, 52369, 52379, 52387, 52391, 52433, 52453, 52457, 52489, 52501, + 52511, 52517, 52529, 52541, 52543, 52553, 52561, 52567, 52571, 52579, + 52583, 52609, 52627, 52631, 52639, 52667, 52673, 52691, 52697, 52709, + 52711, 52721, 52727, 52733, 52747, 52757, 52769, 52783, 52807, 52813, + 52817, 52837, 52859, 52861, 52879, 52883, 52889, 52901, 52903, 52919, + 52937, 52951, 52957, 52963, 52967, 52973, 52981, 52999, 53003, 53017, + 53047, 53051, 53069, 53077, 53087, 53089, 53093, 53101, 53113, 53117, + 53129, 53147, 53149, 53161, 53171, 53173, 53189, 53197, 53201, 53231, + 53233, 53239, 53267, 53269, 53279, 53281, 53299, 53309, 53323, 53327, + 53353, 53359, 53377, 53381, 53401, 53407, 53411, 53419, 53437, 53441, + 53453, 53479, 53503, 53507, 53527, 53549, 53551, 53569, 53591, 53593, + 53597, 53609, 53611, 53617, 53623, 53629, 53633, 53639, 53653, 53657, + 53681, 53693, 53699, 53717, 53719, 53731, 53759, 53773, 53777, 53783, + 53791, 53813, 53819, 53831, 53849, 53857, 53861, 53881, 53887, 53891, + 53897, 53899, 53917, 53923, 53927, 53939, 53951, 53959, 53987, 53993, + 54001, 54011, 54013, 54037, 54049, 54059, 54083, 54091, 54101, 54121, + 54133, 54139, 54151, 54163, 54167, 54181, 54193, 54217, 54251, 54269, + 54277, 54287, 54293, 54311, 54319, 54323, 54331, 54347, 54361, 54367, + 54371, 54377, 54401, 54403, 54409, 54413, 54419, 54421, 54437, 54443, + 54449, 54469, 54493, 54497, 54499, 54503, 54517, 54521, 54539, 54541, + 54547, 54559, 54563, 54577, 54581, 54583, 54601, 54617, 54623, 54629, + 54631, 54647, 54667, 54673, 54679, 54709, 54713, 54721, 54727, 54751, + 54767, 54773, 54779, 54787, 54799, 54829, 54833, 54851, 54869, 54877, + 54881, 54907, 54917, 54919, 54941, 54949, 54959, 54973, 54979, 54983, + 55001, 55009, 55021, 55049, 55051, 55057, 55061, 55073, 55079, 55103, + 55109, 55117, 55127, 55147, 55163, 55171, 55201, 55207, 55213, 55217, + 55219, 55229, 55243, 55249, 55259, 55291, 55313, 55331, 55333, 55337, + 55339, 55343, 55351, 55373, 55381, 55399, 55411, 55439, 55441, 55457, + 55469, 55487, 55501, 55511, 55529, 55541, 55547, 55579, 55589, 55603, + 55609, 55619, 55621, 55631, 55633, 55639, 55661, 55663, 55667, 55673, + 55681, 55691, 55697, 55711, 55717, 55721, 55733, 55763, 55787, 55793, + 55799, 55807, 55813, 55817, 55819, 55823, 55829, 55837, 55843, 55849, + 55871, 55889, 55897, 55901, 55903, 55921, 55927, 55931, 55933, 55949, + 55967, 55987, 55997, 56003, 56009, 56039, 56041, 56053, 56081, 56087, + 56093, 56099, 56101, 56113, 56123, 56131, 56149, 56167, 56171, 56179, + 56197, 56207, 56209, 56237, 56239, 56249, 56263, 56267, 56269, 56299, + 56311, 56333, 56359, 56369, 56377, 56383, 56393, 56401, 56417, 56431, + 56437, 56443, 56453, 56467, 56473, 56477, 56479, 56489, 56501, 56503, + 56509, 56519, 56527, 56531, 56533, 56543, 56569, 56591, 56597, 56599, + 56611, 56629, 56633, 56659, 56663, 56671, 56681, 56687, 56701, 56711, + 56713, 56731, 56737, 56747, 56767, 56773, 56779, 56783, 56807, 56809, + 56813, 56821, 56827, 56843, 56857, 56873, 56891, 56893, 56897, 56909, + 56911, 56921, 56923, 56929, 56941, 56951, 56957, 56963, 56983, 56989, + 56993, 56999, 57037, 57041, 57047, 57059, 57073, 57077, 57089, 57097, + 57107, 57119, 57131, 57139, 57143, 57149, 57163, 57173, 57179, 57191, + 57193, 57203, 57221, 57223, 57241, 57251, 57259, 57269, 57271, 57283, + 57287, 57301, 57329, 57331, 57347, 57349, 57367, 57373, 57383, 57389, + 57397, 57413, 57427, 57457, 57467, 57487, 57493, 57503, 57527, 57529, + 57557, 57559, 57571, 57587, 57593, 57601, 57637, 57641, 57649, 57653, + 57667, 57679, 57689, 57697, 57709, 57713, 57719, 57727, 57731, 57737, + 57751, 57773, 57781, 57787, 57791, 57793, 57803, 57809, 57829, 57839, + 57847, 57853, 57859, 57881, 57899, 57901, 57917, 57923, 57943, 57947, + 57973, 57977, 57991, 58013, 58027, 58031, 58043, 58049, 58057, 58061, + 58067, 58073, 58099, 58109, 58111, 58129, 58147, 58151, 58153, 58169, + 58171, 58189, 58193, 58199, 58207, 58211, 58217, 58229, 58231, 58237, + 58243, 58271, 58309, 58313, 58321, 58337, 58363, 58367, 58369, 58379, + 58391, 58393, 58403, 58411, 58417, 58427, 58439, 58441, 58451, 58453, + 58477, 58481, 58511, 58537, 58543, 58549, 58567, 58573, 58579, 58601, + 58603, 58613, 58631, 58657, 58661, 58679, 58687, 58693, 58699, 58711, + 58727, 58733, 58741, 58757, 58763, 58771, 58787, 58789, 58831, 58889, + 58897, 58901, 58907, 58909, 58913, 58921, 58937, 58943, 58963, 58967, + 58979, 58991, 58997, 59009, 59011, 59021, 59023, 59029, 59051, 59053, + 59063, 59069, 59077, 59083, 59093, 59107, 59113, 59119, 59123, 59141, + 59149, 59159, 59167, 59183, 59197, 59207, 59209, 59219, 59221, 59233, + 59239, 59243, 59263, 59273, 59281, 59333, 59341, 59351, 59357, 59359, + 59369, 59377, 59387, 59393, 59399, 59407, 59417, 59419, 59441, 59443, + 59447, 59453, 59467, 59471, 59473, 59497, 59509, 59513, 59539, 59557, + 59561, 59567, 59581, 59611, 59617, 59621, 59627, 59629, 59651, 59659, + 59663, 59669, 59671, 59693, 59699, 59707, 59723, 59729, 59743, 59747, + 59753, 59771, 59779, 59791, 59797, 59809, 59833, 59863, 59879, 59887, + 59921, 59929, 59951, 59957, 59971, 59981, 59999, 60013, 60017, 60029, + 60037, 60041, 60077, 60083, 60089, 60091, 60101, 60103, 60107, 60127, + 60133, 60139, 60149, 60161, 60167, 60169, 60209, 60217, 60223, 60251, + 60257, 60259, 60271, 60289, 60293, 60317, 60331, 60337, 60343, 60353, + 60373, 60383, 60397, 60413, 60427, 60443, 60449, 60457, 60493, 60497, + 60509, 60521, 60527, 60539, 60589, 60601, 60607, 60611, 60617, 60623, + 60631, 60637, 60647, 60649, 60659, 60661, 60679, 60689, 60703, 60719, + 60727, 60733, 60737, 60757, 60761, 60763, 60773, 60779, 60793, 60811, + 60821, 60859, 60869, 60887, 60889, 60899, 60901, 60913, 60917, 60919, + 60923, 60937, 60943, 60953, 60961, 61001, 61007, 61027, 61031, 61043, + 61051, 61057, 61091, 61099, 61121, 61129, 61141, 61151, 61153, 61169, + 61211, 61223, 61231, 61253, 61261, 61283, 61291, 61297, 61331, 61333, + 61339, 61343, 61357, 61363, 61379, 61381, 61403, 61409, 61417, 61441, + 61463, 61469, 61471, 61483, 61487, 61493, 61507, 61511, 61519, 61543, + 61547, 61553, 61559, 61561, 61583, 61603, 61609, 61613, 61627, 61631, + 61637, 61643, 61651, 61657, 61667, 61673, 61681, 61687, 61703, 61717, + 61723, 61729, 61751, 61757, 61781, 61813, 61819, 61837, 61843, 61861, + 61871, 61879, 61909, 61927, 61933, 61949, 61961, 61967, 61979, 61981, + 61987, 61991, 62003, 62011, 62017, 62039, 62047, 62053, 62057, 62071, + 62081, 62099, 62119, 62129, 62131, 62137, 62141, 62143, 62171, 62189, + 62191, 62201, 62207, 62213, 62219, 62233, 62273, 62297, 62299, 62303, + 62311, 62323, 62327, 62347, 62351, 62383, 62401, 62417, 62423, 62459, + 62467, 62473, 62477, 62483, 62497, 62501, 62507, 62533, 62539, 62549, + 62563, 62581, 62591, 62597, 62603, 62617, 62627, 62633, 62639, 62653, + 62659, 62683, 62687, 62701, 62723, 62731, 62743, 62753, 62761, 62773, + 62791, 62801, 62819, 62827, 62851, 62861, 62869, 62873, 62897, 62903, + 62921, 62927, 62929, 62939, 62969, 62971, 62981, 62983, 62987, 62989, + 63029, 63031, 63059, 63067, 63073, 63079, 63097, 63103, 63113, 63127, + 63131, 63149, 63179, 63197, 63199, 63211, 63241, 63247, 63277, 63281, + 63299, 63311, 63313, 63317, 63331, 63337, 63347, 63353, 63361, 63367, + 63377, 63389, 63391, 63397, 63409, 63419, 63421, 63439, 63443, 63463, + 63467, 63473, 63487, 63493, 63499, 63521, 63527, 63533, 63541, 63559, + 63577, 63587, 63589, 63599, 63601, 63607, 63611, 63617, 63629, 63647, + 63649, 63659, 63667, 63671, 63689, 63691, 63697, 63703, 63709, 63719, + 63727, 63737, 63743, 63761, 63773, 63781, 63793, 63799, 63803, 63809, + 63823, 63839, 63841, 63853, 63857, 63863, 63901, 63907, 63913, 63929, + 63949, 63977, 63997, 64007, 64013, 64019, 64033, 64037, 64063, 64067, + 64081, 64091, 64109, 64123, 64151, 64153, 64157, 64171, 64187, 64189, + 64217, 64223, 64231, 64237, 64271, 64279, 64283, 64301, 64303, 64319, + 64327, 64333, 64373, 64381, 64399, 64403, 64433, 64439, 64451, 64453, + 64483, 64489, 64499, 64513, 64553, 64567, 64577, 64579, 64591, 64601, + 64609, 64613, 64621, 64627, 64633, 64661, 64663, 64667, 64679, 64693, + 64709, 64717, 64747, 64763, 64781, 64783, 64793, 64811, 64817, 64849, + 64853, 64871, 64877, 64879, 64891, 64901, 64919, 64921, 64927, 64937, + 64951, 64969, 64997, 65003, 65011, 65027, 65029, 65033, 65053, 65063, + 65071, 65089, 65099, 65101, 65111, 65119, 65123, 65129, 65141, 65147, + 65167, 65171, 65173, 65179, 65183, 65203, 65213, 65239, 65257, 65267, + 65269, 65287, 65293, 65309, 65323, 65327, 65353, 65357, 65371, 65381, + 65393, 65407, 65413, 65419, 65423, 65437, 65447, 65449, 65479, 65497, + 65519, 65521, 65537, 65539, 65543, 65551, 65557, 65563, 65579, 65581, + 65587, 65599, 65609, 65617, 65629, 65633, 65647, 65651, 65657, 65677, + 65687, 65699, 65701, 65707, 65713, 65717, 65719, 65729, 65731, 65761, + 65777, 65789, 65809, 65827, 65831, 65837, 65839, 65843, 65851, 65867, + 65881, 65899, 65921, 65927, 65929, 65951, 65957, 65963, 65981, 65983, + 65993, 66029, 66037, 66041, 66047, 66067, 66071, 66083, 66089, 66103, + 66107, 66109, 66137, 66161, 66169, 66173, 66179, 66191, 66221, 66239, + 66271, 66293, 66301, 66337, 66343, 66347, 66359, 66361, 66373, 66377, + 66383, 66403, 66413, 66431, 66449, 66457, 66463, 66467, 66491, 66499, + 66509, 66523, 66529, 66533, 66541, 66553, 66569, 66571, 66587, 66593, + 66601, 66617, 66629, 66643, 66653, 66683, 66697, 66701, 66713, 66721, + 66733, 66739, 66749, 66751, 66763, 66791, 66797, 66809, 66821, 66841, + 66851, 66853, 66863, 66877, 66883, 66889, 66919, 66923, 66931, 66943, + 66947, 66949, 66959, 66973, 66977, 67003, 67021, 67033, 67043, 67049, + 67057, 67061, 67073, 67079, 67103, 67121, 67129, 67139, 67141, 67153, + 67157, 67169, 67181, 67187, 67189, 67211, 67213, 67217, 67219, 67231, + 67247, 67261, 67271, 67273, 67289, 67307, 67339, 67343, 67349, 67369, + 67391, 67399, 67409, 67411, 67421, 67427, 67429, 67433, 67447, 67453, + 67477, 67481, 67489, 67493, 67499, 67511, 67523, 67531, 67537, 67547, + 67559, 67567, 67577, 67579, 67589, 67601, 67607, 67619, 67631, 67651, + 67679, 67699, 67709, 67723, 67733, 67741, 67751, 67757, 67759, 67763, + 67777, 67783, 67789, 67801, 67807, 67819, 67829, 67843, 67853, 67867, + 67883, 67891, 67901, 67927, 67931, 67933, 67939, 67943, 67957, 67961, + 67967, 67979, 67987, 67993, 68023, 68041, 68053, 68059, 68071, 68087, + 68099, 68111, 68113, 68141, 68147, 68161, 68171, 68207, 68209, 68213, + 68219, 68227, 68239, 68261, 68279, 68281, 68311, 68329, 68351, 68371, + 68389, 68399, 68437, 68443, 68447, 68449, 68473, 68477, 68483, 68489, + 68491, 68501, 68507, 68521, 68531, 68539, 68543, 68567, 68581, 68597, + 68611, 68633, 68639, 68659, 68669, 68683, 68687, 68699, 68711, 68713, + 68729, 68737, 68743, 68749, 68767, 68771, 68777, 68791, 68813, 68819, + 68821, 68863, 68879, 68881, 68891, 68897, 68899, 68903, 68909, 68917, + 68927, 68947, 68963, 68993, 69001, 69011, 69019, 69029, 69031, 69061, + 69067, 69073, 69109, 69119, 69127, 69143, 69149, 69151, 69163, 69191, + 69193, 69197, 69203, 69221, 69233, 69239, 69247, 69257, 69259, 69263, + 69313, 69317, 69337, 69341, 69371, 69379, 69383, 69389, 69401, 69403, + 69427, 69431, 69439, 69457, 69463, 69467, 69473, 69481, 69491, 69493, + 69497, 69499, 69539, 69557, 69593, 69623, 69653, 69661, 69677, 69691, + 69697, 69709, 69737, 69739, 69761, 69763, 69767, 69779, 69809, 69821, + 69827, 69829, 69833, 69847, 69857, 69859, 69877, 69899, 69911, 69929, + 69931, 69941, 69959, 69991, 69997, 70001, 70003, 70009, 70019, 70039, + 70051, 70061, 70067, 70079, 70099, 70111, 70117, 70121, 70123, 70139, + 70141, 70157, 70163, 70177, 70181, 70183, 70199, 70201, 70207, 70223, + 70229, 70237, 70241, 70249, 70271, 70289, 70297, 70309, 70313, 70321, + 70327, 70351, 70373, 70379, 70381, 70393, 70423, 70429, 70439, 70451, + 70457, 70459, 70481, 70487, 70489, 70501, 70507, 70529, 70537, 70549, + 70571, 70573, 70583, 70589, 70607, 70619, 70621, 70627, 70639, 70657, + 70663, 70667, 70687, 70709, 70717, 70729, 70753, 70769, 70783, 70793, + 70823, 70841, 70843, 70849, 70853, 70867, 70877, 70879, 70891, 70901, + 70913, 70919, 70921, 70937, 70949, 70951, 70957, 70969, 70979, 70981, + 70991, 70997, 70999, 71011, 71023, 71039, 71059, 71069, 71081, 71089, + 71119, 71129, 71143, 71147, 71153, 71161, 71167, 71171, 71191, 71209, + 71233, 71237, 71249, 71257, 71261, 71263, 71287, 71293, 71317, 71327, + 71329, 71333, 71339, 71341, 71347, 71353, 71359, 71363, 71387, 71389, + 71399, 71411, 71413, 71419, 71429, 71437, 71443, 71453, 71471, 71473, + 71479, 71483, 71503, 71527, 71537, 71549, 71551, 71563, 71569, 71593, + 71597, 71633, 71647, 71663, 71671, 71693, 71699, 71707, 71711, 71713, + 71719, 71741, 71761, 71777, 71789, 71807, 71809, 71821, 71837, 71843, + 71849, 71861, 71867, 71879, 71881, 71887, 71899, 71909, 71917, 71933, + 71941, 71947, 71963, 71971, 71983, 71987, 71993, 71999, 72019, 72031, + 72043, 72047, 72053, 72073, 72077, 72089, 72091, 72101, 72103, 72109, + 72139, 72161, 72167, 72169, 72173, 72211, 72221, 72223, 72227, 72229, + 72251, 72253, 72269, 72271, 72277, 72287, 72307, 72313, 72337, 72341, + 72353, 72367, 72379, 72383, 72421, 72431, 72461, 72467, 72469, 72481, + 72493, 72497, 72503, 72533, 72547, 72551, 72559, 72577, 72613, 72617, + 72623, 72643, 72647, 72649, 72661, 72671, 72673, 72679, 72689, 72701, + 72707, 72719, 72727, 72733, 72739, 72763, 72767, 72797, 72817, 72823, + 72859, 72869, 72871, 72883, 72889, 72893, 72901, 72907, 72911, 72923, + 72931, 72937, 72949, 72953, 72959, 72973, 72977, 72997, 73009, 73013, + 73019, 73037, 73039, 73043, 73061, 73063, 73079, 73091, 73121, 73127, + 73133, 73141, 73181, 73189, 73237, 73243, 73259, 73277, 73291, 73303, + 73309, 73327, 73331, 73351, 73361, 73363, 73369, 73379, 73387, 73417, + 73421, 73433, 73453, 73459, 73471, 73477, 73483, 73517, 73523, 73529, + 73547, 73553, 73561, 73571, 73583, 73589, 73597, 73607, 73609, 73613, + 73637, 73643, 73651, 73673, 73679, 73681, 73693, 73699, 73709, 73721, + 73727, 73751, 73757, 73771, 73783, 73819, 73823, 73847, 73849, 73859, + 73867, 73877, 73883, 73897, 73907, 73939, 73943, 73951, 73961, 73973, + 73999, 74017, 74021, 74027, 74047, 74051, 74071, 74077, 74093, 74099, + 74101, 74131, 74143, 74149, 74159, 74161, 74167, 74177, 74189, 74197, + 74201, 74203, 74209, 74219, 74231, 74257, 74279, 74287, 74293, 74297, + 74311, 74317, 74323, 74353, 74357, 74363, 74377, 74381, 74383, 74411, + 74413, 74419, 74441, 74449, 74453, 74471, 74489, 74507, 74509, 74521, + 74527, 74531, 74551, 74561, 74567, 74573, 74587, 74597, 74609, 74611, + 74623, 74653, 74687, 74699, 74707, 74713, 74717, 74719, 74729, 74731, + 74747, 74759, 74761, 74771, 74779, 74797, 74821, 74827, 74831, 74843, + 74857, 74861, 74869, 74873, 74887, 74891, 74897, 74903, 74923, 74929, + 74933, 74941, 74959, 75011, 75013, 75017, 75029, 75037, 75041, 75079, + 75083, 75109, 75133, 75149, 75161, 75167, 75169, 75181, 75193, 75209, + 75211, 75217, 75223, 75227, 75239, 75253, 75269, 75277, 75289, 75307, + 75323, 75329, 75337, 75347, 75353, 75367, 75377, 75389, 75391, 75401, + 75403, 75407, 75431, 75437, 75479, 75503, 75511, 75521, 75527, 75533, + 75539, 75541, 75553, 75557, 75571, 75577, 75583, 75611, 75617, 75619, + 75629, 75641, 75653, 75659, 75679, 75683, 75689, 75703, 75707, 75709, + 75721, 75731, 75743, 75767, 75773, 75781, 75787, 75793, 75797, 75821, + 75833, 75853, 75869, 75883, 75913, 75931, 75937, 75941, 75967, 75979, + 75983, 75989, 75991, 75997, 76001, 76003, 76031, 76039, 76079, 76081, + 76091, 76099, 76103, 76123, 76129, 76147, 76157, 76159, 76163, 76207, + 76213, 76231, 76243, 76249, 76253, 76259, 76261, 76283, 76289, 76303, + 76333, 76343, 76367, 76369, 76379, 76387, 76403, 76421, 76423, 76441, + 76463, 76471, 76481, 76487, 76493, 76507, 76511, 76519, 76537, 76541, + 76543, 76561, 76579, 76597, 76603, 76607, 76631, 76649, 76651, 76667, + 76673, 76679, 76697, 76717, 76733, 76753, 76757, 76771, 76777, 76781, + 76801, 76819, 76829, 76831, 76837, 76847, 76871, 76873, 76883, 76907, + 76913, 76919, 76943, 76949, 76961, 76963, 76991, 77003, 77017, 77023, + 77029, 77041, 77047, 77069, 77081, 77093, 77101, 77137, 77141, 77153, + 77167, 77171, 77191, 77201, 77213, 77237, 77239, 77243, 77249, 77261, + 77263, 77267, 77269, 77279, 77291, 77317, 77323, 77339, 77347, 77351, + 77359, 77369, 77377, 77383, 77417, 77419, 77431, 77447, 77471, 77477, + 77479, 77489, 77491, 77509, 77513, 77521, 77527, 77543, 77549, 77551, + 77557, 77563, 77569, 77573, 77587, 77591, 77611, 77617, 77621, 77641, + 77647, 77659, 77681, 77687, 77689, 77699, 77711, 77713, 77719, 77723, + 77731, 77743, 77747, 77761, 77773, 77783, 77797, 77801, 77813, 77839, + 77849, 77863, 77867, 77893, 77899, 77929, 77933, 77951, 77969, 77977, + 77983, 77999, 78007, 78017, 78031, 78041, 78049, 78059, 78079, 78101, + 78121, 78137, 78139, 78157, 78163, 78167, 78173, 78179, 78191, 78193, + 78203, 78229, 78233, 78241, 78259, 78277, 78283, 78301, 78307, 78311, + 78317, 78341, 78347, 78367, 78401, 78427, 78437, 78439, 78467, 78479, + 78487, 78497, 78509, 78511, 78517, 78539, 78541, 78553, 78569, 78571, + 78577, 78583, 78593, 78607, 78623, 78643, 78649, 78653, 78691, 78697, + 78707, 78713, 78721, 78737, 78779, 78781, 78787, 78791, 78797, 78803, + 78809, 78823, 78839, 78853, 78857, 78877, 78887, 78889, 78893, 78901, + 78919, 78929, 78941, 78977, 78979, 78989, 79031, 79039, 79043, 79063, + 79087, 79103, 79111, 79133, 79139, 79147, 79151, 79153, 79159, 79181, + 79187, 79193, 79201, 79229, 79231, 79241, 79259, 79273, 79279, 79283, + 79301, 79309, 79319, 79333, 79337, 79349, 79357, 79367, 79379, 79393, + 79397, 79399, 79411, 79423, 79427, 79433, 79451, 79481, 79493, 79531, + 79537, 79549, 79559, 79561, 79579, 79589, 79601, 79609, 79613, 79621, + 79627, 79631, 79633, 79657, 79669, 79687, 79691, 79693, 79697, 79699, + 79757, 79769, 79777, 79801, 79811, 79813, 79817, 79823, 79829, 79841, + 79843, 79847, 79861, 79867, 79873, 79889, 79901, 79903, 79907, 79939, + 79943, 79967, 79973, 79979, 79987, 79997, 79999, 80021, 80039, 80051, + 80071, 80077, 80107, 80111, 80141, 80147, 80149, 80153, 80167, 80173, + 80177, 80191, 80207, 80209, 80221, 80231, 80233, 80239, 80251, 80263, + 80273, 80279, 80287, 80309, 80317, 80329, 80341, 80347, 80363, 80369, + 80387, 80407, 80429, 80447, 80449, 80471, 80473, 80489, 80491, 80513, + 80527, 80537, 80557, 80567, 80599, 80603, 80611, 80621, 80627, 80629, + 80651, 80657, 80669, 80671, 80677, 80681, 80683, 80687, 80701, 80713, + 80737, 80747, 80749, 80761, 80777, 80779, 80783, 80789, 80803, 80809, + 80819, 80831, 80833, 80849, 80863, 80897, 80909, 80911, 80917, 80923, + 80929, 80933, 80953, 80963, 80989, 81001, 81013, 81017, 81019, 81023, + 81031, 81041, 81043, 81047, 81049, 81071, 81077, 81083, 81097, 81101, + 81119, 81131, 81157, 81163, 81173, 81181, 81197, 81199, 81203, 81223, + 81233, 81239, 81281, 81283, 81293, 81299, 81307, 81331, 81343, 81349, + 81353, 81359, 81371, 81373, 81401, 81409, 81421, 81439, 81457, 81463, + 81509, 81517, 81527, 81533, 81547, 81551, 81553, 81559, 81563, 81569, + 81611, 81619, 81629, 81637, 81647, 81649, 81667, 81671, 81677, 81689, + 81701, 81703, 81707, 81727, 81737, 81749, 81761, 81769, 81773, 81799, + 81817, 81839, 81847, 81853, 81869, 81883, 81899, 81901, 81919, 81929, + 81931, 81937, 81943, 81953, 81967, 81971, 81973, 82003, 82007, 82009, + 82013, 82021, 82031, 82037, 82039, 82051, 82067, 82073, 82129, 82139, + 82141, 82153, 82163, 82171, 82183, 82189, 82193, 82207, 82217, 82219, + 82223, 82231, 82237, 82241, 82261, 82267, 82279, 82301, 82307, 82339, + 82349, 82351, 82361, 82373, 82387, 82393, 82421, 82457, 82463, 82469, + 82471, 82483, 82487, 82493, 82499, 82507, 82529, 82531, 82549, 82559, + 82561, 82567, 82571, 82591, 82601, 82609, 82613, 82619, 82633, 82651, + 82657, 82699, 82721, 82723, 82727, 82729, 82757, 82759, 82763, 82781, + 82787, 82793, 82799, 82811, 82813, 82837, 82847, 82883, 82889, 82891, + 82903, 82913, 82939, 82963, 82981, 82997, 83003, 83009, 83023, 83047, + 83059, 83063, 83071, 83077, 83089, 83093, 83101, 83117, 83137, 83177, + 83203, 83207, 83219, 83221, 83227, 83231, 83233, 83243, 83257, 83267, + 83269, 83273, 83299, 83311, 83339, 83341, 83357, 83383, 83389, 83399, + 83401, 83407, 83417, 83423, 83431, 83437, 83443, 83449, 83459, 83471, + 83477, 83497, 83537, 83557, 83561, 83563, 83579, 83591, 83597, 83609, + 83617, 83621, 83639, 83641, 83653, 83663, 83689, 83701, 83717, 83719, + 83737, 83761, 83773, 83777, 83791, 83813, 83833, 83843, 83857, 83869, + 83873, 83891, 83903, 83911, 83921, 83933, 83939, 83969, 83983, 83987, + 84011, 84017, 84047, 84053, 84059, 84061, 84067, 84089, 84121, 84127, + 84131, 84137, 84143, 84163, 84179, 84181, 84191, 84199, 84211, 84221, + 84223, 84229, 84239, 84247, 84263, 84299, 84307, 84313, 84317, 84319, + 84347, 84349, 84377, 84389, 84391, 84401, 84407, 84421, 84431, 84437, + 84443, 84449, 84457, 84463, 84467, 84481, 84499, 84503, 84509, 84521, + 84523, 84533, 84551, 84559, 84589, 84629, 84631, 84649, 84653, 84659, + 84673, 84691, 84697, 84701, 84713, 84719, 84731, 84737, 84751, 84761, + 84787, 84793, 84809, 84811, 84827, 84857, 84859, 84869, 84871, 84913, + 84919, 84947, 84961, 84967, 84977, 84979, 84991, 85009, 85021, 85027, + 85037, 85049, 85061, 85081, 85087, 85091, 85093, 85103, 85109, 85121, + 85133, 85147, 85159, 85193, 85199, 85201, 85213, 85223, 85229, 85237, + 85243, 85247, 85259, 85297, 85303, 85313, 85331, 85333, 85361, 85363, + 85369, 85381, 85411, 85427, 85429, 85439, 85447, 85451, 85453, 85469, + 85487, 85513, 85517, 85523, 85531, 85549, 85571, 85577, 85597, 85601, + 85607, 85619, 85621, 85627, 85639, 85643, 85661, 85667, 85669, 85691, + 85703, 85711, 85717, 85733, 85751, 85781, 85793, 85817, 85819, 85829, + 85831, 85837, 85843, 85847, 85853, 85889, 85903, 85909, 85931, 85933, + 85991, 85999, 86011, 86017, 86027, 86029, 86069, 86077, 86083, 86111, + 86113, 86117, 86131, 86137, 86143, 86161, 86171, 86179, 86183, 86197, + 86201, 86209, 86239, 86243, 86249, 86257, 86263, 86269, 86287, 86291, + 86293, 86297, 86311, 86323, 86341, 86351, 86353, 86357, 86369, 86371, + 86381, 86389, 86399, 86413, 86423, 86441, 86453, 86461, 86467, 86477, + 86491, 86501, 86509, 86531, 86533, 86539, 86561, 86573, 86579, 86587, + 86599, 86627, 86629, 86677, 86689, 86693, 86711, 86719, 86729, 86743, + 86753, 86767, 86771, 86783, 86813, 86837, 86843, 86851, 86857, 86861, + 86869, 86923, 86927, 86929, 86939, 86951, 86959, 86969, 86981, 86993, + 87011, 87013, 87037, 87041, 87049, 87071, 87083, 87103, 87107, 87119, + 87121, 87133, 87149, 87151, 87179, 87181, 87187, 87211, 87221, 87223, + 87251, 87253, 87257, 87277, 87281, 87293, 87299, 87313, 87317, 87323, + 87337, 87359, 87383, 87403, 87407, 87421, 87427, 87433, 87443, 87473, + 87481, 87491, 87509, 87511, 87517, 87523, 87539, 87541, 87547, 87553, + 87557, 87559, 87583, 87587, 87589, 87613, 87623, 87629, 87631, 87641, + 87643, 87649, 87671, 87679, 87683, 87691, 87697, 87701, 87719, 87721, + 87739, 87743, 87751, 87767, 87793, 87797, 87803, 87811, 87833, 87853, + 87869, 87877, 87881, 87887, 87911, 87917, 87931, 87943, 87959, 87961, + 87973, 87977, 87991, 88001, 88003, 88007, 88019, 88037, 88069, 88079, + 88093, 88117, 88129, 88169, 88177, 88211, 88223, 88237, 88241, 88259, + 88261, 88289, 88301, 88321, 88327, 88337, 88339, 88379, 88397, 88411, + 88423, 88427, 88463, 88469, 88471, 88493, 88499, 88513, 88523, 88547, + 88589, 88591, 88607, 88609, 88643, 88651, 88657, 88661, 88663, 88667, + 88681, 88721, 88729, 88741, 88747, 88771, 88789, 88793, 88799, 88801, + 88807, 88811, 88813, 88817, 88819, 88843, 88853, 88861, 88867, 88873, + 88883, 88897, 88903, 88919, 88937, 88951, 88969, 88993, 88997, 89003, + 89009, 89017, 89021, 89041, 89051, 89057, 89069, 89071, 89083, 89087, + 89101, 89107, 89113, 89119, 89123, 89137, 89153, 89189, 89203, 89209, + 89213, 89227, 89231, 89237, 89261, 89269, 89273, 89293, 89303, 89317, + 89329, 89363, 89371, 89381, 89387, 89393, 89399, 89413, 89417, 89431, + 89443, 89449, 89459, 89477, 89491, 89501, 89513, 89519, 89521, 89527, + 89533, 89561, 89563, 89567, 89591, 89597, 89599, 89603, 89611, 89627, + 89633, 89653, 89657, 89659, 89669, 89671, 89681, 89689, 89753, 89759, + 89767, 89779, 89783, 89797, 89809, 89819, 89821, 89833, 89839, 89849, + 89867, 89891, 89897, 89899, 89909, 89917, 89923, 89939, 89959, 89963, + 89977, 89983, 89989, 90001, 90007, 90011, 90017, 90019, 90023, 90031, + 90053, 90059, 90067, 90071, 90073, 90089, 90107, 90121, 90127, 90149, + 90163, 90173, 90187, 90191, 90197, 90199, 90203, 90217, 90227, 90239, + 90247, 90263, 90271, 90281, 90289, 90313, 90353, 90359, 90371, 90373, + 90379, 90397, 90401, 90403, 90407, 90437, 90439, 90469, 90473, 90481, + 90499, 90511, 90523, 90527, 90529, 90533, 90547, 90583, 90599, 90617, + 90619, 90631, 90641, 90647, 90659, 90677, 90679, 90697, 90703, 90709, + 90731, 90749, 90787, 90793, 90803, 90821, 90823, 90833, 90841, 90847, + 90863, 90887, 90901, 90907, 90911, 90917, 90931, 90947, 90971, 90977, + 90989, 90997, 91009, 91019, 91033, 91079, 91081, 91097, 91099, 91121, + 91127, 91129, 91139, 91141, 91151, 91153, 91159, 91163, 91183, 91193, + 91199, 91229, 91237, 91243, 91249, 91253, 91283, 91291, 91297, 91303, + 91309, 91331, 91367, 91369, 91373, 91381, 91387, 91393, 91397, 91411, + 91423, 91433, 91453, 91457, 91459, 91463, 91493, 91499, 91513, 91529, + 91541, 91571, 91573, 91577, 91583, 91591, 91621, 91631, 91639, 91673, + 91691, 91703, 91711, 91733, 91753, 91757, 91771, 91781, 91801, 91807, + 91811, 91813, 91823, 91837, 91841, 91867, 91873, 91909, 91921, 91939, + 91943, 91951, 91957, 91961, 91967, 91969, 91997, 92003, 92009, 92033, + 92041, 92051, 92077, 92083, 92107, 92111, 92119, 92143, 92153, 92173, + 92177, 92179, 92189, 92203, 92219, 92221, 92227, 92233, 92237, 92243, + 92251, 92269, 92297, 92311, 92317, 92333, 92347, 92353, 92357, 92363, + 92369, 92377, 92381, 92383, 92387, 92399, 92401, 92413, 92419, 92431, + 92459, 92461, 92467, 92479, 92489, 92503, 92507, 92551, 92557, 92567, + 92569, 92581, 92593, 92623, 92627, 92639, 92641, 92647, 92657, 92669, + 92671, 92681, 92683, 92693, 92699, 92707, 92717, 92723, 92737, 92753, + 92761, 92767, 92779, 92789, 92791, 92801, 92809, 92821, 92831, 92849, + 92857, 92861, 92863, 92867, 92893, 92899, 92921, 92927, 92941, 92951, + 92957, 92959, 92987, 92993, 93001, 93047, 93053, 93059, 93077, 93083, + 93089, 93097, 93103, 93113, 93131, 93133, 93139, 93151, 93169, 93179, + 93187, 93199, 93229, 93239, 93241, 93251, 93253, 93257, 93263, 93281, + 93283, 93287, 93307, 93319, 93323, 93329, 93337, 93371, 93377, 93383, + 93407, 93419, 93427, 93463, 93479, 93481, 93487, 93491, 93493, 93497, + 93503, 93523, 93529, 93553, 93557, 93559, 93563, 93581, 93601, 93607, + 93629, 93637, 93683, 93701, 93703, 93719, 93739, 93761, 93763, 93787, + 93809, 93811, 93827, 93851, 93871, 93887, 93889, 93893, 93901, 93911, + 93913, 93923, 93937, 93941, 93949, 93967, 93971, 93979, 93983, 93997, + 94007, 94009, 94033, 94049, 94057, 94063, 94079, 94099, 94109, 94111, + 94117, 94121, 94151, 94153, 94169, 94201, 94207, 94219, 94229, 94253, + 94261, 94273, 94291, 94307, 94309, 94321, 94327, 94331, 94343, 94349, + 94351, 94379, 94397, 94399, 94421, 94427, 94433, 94439, 94441, 94447, + 94463, 94477, 94483, 94513, 94529, 94531, 94541, 94543, 94547, 94559, + 94561, 94573, 94583, 94597, 94603, 94613, 94621, 94649, 94651, 94687, + 94693, 94709, 94723, 94727, 94747, 94771, 94777, 94781, 94789, 94793, + 94811, 94819, 94823, 94837, 94841, 94847, 94849, 94873, 94889, 94903, + 94907, 94933, 94949, 94951, 94961, 94993, 94999, 95003, 95009, 95021, + 95027, 95063, 95071, 95083, 95087, 95089, 95093, 95101, 95107, 95111, + 95131, 95143, 95153, 95177, 95189, 95191, 95203, 95213, 95219, 95231, + 95233, 95239, 95257, 95261, 95267, 95273, 95279, 95287, 95311, 95317, + 95327, 95339, 95369, 95383, 95393, 95401, 95413, 95419, 95429, 95441, + 95443, 95461, 95467, 95471, 95479, 95483, 95507, 95527, 95531, 95539, + 95549, 95561, 95569, 95581, 95597, 95603, 95617, 95621, 95629, 95633, + 95651, 95701, 95707, 95713, 95717, 95723, 95731, 95737, 95747, 95773, + 95783, 95789, 95791, 95801, 95803, 95813, 95819, 95857, 95869, 95873, + 95881, 95891, 95911, 95917, 95923, 95929, 95947, 95957, 95959, 95971, + 95987, 95989, 96001, 96013, 96017, 96043, 96053, 96059, 96079, 96097, + 96137, 96149, 96157, 96167, 96179, 96181, 96199, 96211, 96221, 96223, + 96233, 96259, 96263, 96269, 96281, 96289, 96293, 96323, 96329, 96331, + 96337, 96353, 96377, 96401, 96419, 96431, 96443, 96451, 96457, 96461, + 96469, 96479, 96487, 96493, 96497, 96517, 96527, 96553, 96557, 96581, + 96587, 96589, 96601, 96643, 96661, 96667, 96671, 96697, 96703, 96731, + 96737, 96739, 96749, 96757, 96763, 96769, 96779, 96787, 96797, 96799, + 96821, 96823, 96827, 96847, 96851, 96857, 96893, 96907, 96911, 96931, + 96953, 96959, 96973, 96979, 96989, 96997, 97001, 97003, 97007, 97021, + 97039, 97073, 97081, 97103, 97117, 97127, 97151, 97157, 97159, 97169, + 97171, 97177, 97187, 97213, 97231, 97241, 97259, 97283, 97301, 97303, + 97327, 97367, 97369, 97373, 97379, 97381, 97387, 97397, 97423, 97429, + 97441, 97453, 97459, 97463, 97499, 97501, 97511, 97523, 97547, 97549, + 97553, 97561, 97571, 97577, 97579, 97583, 97607, 97609, 97613, 97649, + 97651, 97673, 97687, 97711, 97729, 97771, 97777, 97787, 97789, 97813, + 97829, 97841, 97843, 97847, 97849, 97859, 97861, 97871, 97879, 97883, + 97919, 97927, 97931, 97943, 97961, 97967, 97973, 97987, 98009, 98011, + 98017, 98041, 98047, 98057, 98081, 98101, 98123, 98129, 98143, 98179, + 98207, 98213, 98221, 98227, 98251, 98257, 98269, 98297, 98299, 98317, + 98321, 98323, 98327, 98347, 98369, 98377, 98387, 98389, 98407, 98411, + 98419, 98429, 98443, 98453, 98459, 98467, 98473, 98479, 98491, 98507, + 98519, 98533, 98543, 98561, 98563, 98573, 98597, 98621, 98627, 98639, + 98641, 98663, 98669, 98689, 98711, 98713, 98717, 98729, 98731, 98737, + 98773, 98779, 98801, 98807, 98809, 98837, 98849, 98867, 98869, 98873, + 98887, 98893, 98897, 98899, 98909, 98911, 98927, 98929, 98939, 98947, + 98953, 98963, 98981, 98993, 98999, 99013, 99017, 99023, 99041, 99053, + 99079, 99083, 99089, 99103, 99109, 99119, 99131, 99133, 99137, 99139, + 99149, 99173, 99181, 99191, 99223, 99233, 99241, 99251, 99257, 99259, + 99277, 99289, 99317, 99347, 99349, 99367, 99371, 99377, 99391, 99397, + 99401, 99409, 99431, 99439, 99469, 99487, 99497, 99523, 99527, 99529, + 99551, 99559, 99563, 99571, 99577, 99581, 99607, 99611, 99623, 99643, + 99661, 99667, 99679, 99689, 99707, 99709, 99713, 99719, 99721, 99733, + 99761, 99767, 99787, 99793, 99809, 99817, 99823, 99829, 99833, 99839, + 99859, 99871, 99877, 99881, 99901, 99907, 99923, 99929, 99961, 99971, + 99989, 99991, 100003, 100019, 100043, 100049, 100057, 100069, 100103, 100109, +100129, 100151, 100153, 100169, 100183, 100189, 100193, 100207, 100213, 100237, +100267, 100271, 100279, 100291, 100297, 100313, 100333, 100343, 100357, 100361, +100363, 100379, 100391, 100393, 100403, 100411, 100417, 100447, 100459, 100469, +100483, 100493, 100501, 100511, 100517, 100519, 100523, 100537, 100547, 100549, +100559, 100591, 100609, 100613, 100621, 100649, 100669, 100673, 100693, 100699, +100703, 100733, 100741, 100747, 100769, 100787, 100799, 100801, 100811, 100823, +100829, 100847, 100853, 100907, 100913, 100927, 100931, 100937, 100943, 100957, +100981, 100987, 100999, 101009, 101021, 101027, 101051, 101063, 101081, 101089, +101107, 101111, 101113, 101117, 101119, 101141, 101149, 101159, 101161, 101173, +101183, 101197, 101203, 101207, 101209, 101221, 101267, 101273, 101279, 101281, +101287, 101293, 101323, 101333, 101341, 101347, 101359, 101363, 101377, 101383, +101399, 101411, 101419, 101429, 101449, 101467, 101477, 101483, 101489, 101501, +101503, 101513, 101527, 101531, 101533, 101537, 101561, 101573, 101581, 101599, +101603, 101611, 101627, 101641, 101653, 101663, 101681, 101693, 101701, 101719, +101723, 101737, 101741, 101747, 101749, 101771, 101789, 101797, 101807, 101833, +101837, 101839, 101863, 101869, 101873, 101879, 101891, 101917, 101921, 101929, +101939, 101957, 101963, 101977, 101987, 101999, 102001, 102013, 102019, 102023, +102031, 102043, 102059, 102061, 102071, 102077, 102079, 102101, 102103, 102107, +102121, 102139, 102149, 102161, 102181, 102191, 102197, 102199, 102203, 102217, +102229, 102233, 102241, 102251, 102253, 102259, 102293, 102299, 102301, 102317, +102329, 102337, 102359, 102367, 102397, 102407, 102409, 102433, 102437, 102451, +102461, 102481, 102497, 102499, 102503, 102523, 102533, 102539, 102547, 102551, +102559, 102563, 102587, 102593, 102607, 102611, 102643, 102647, 102653, 102667, +102673, 102677, 102679, 102701, 102761, 102763, 102769, 102793, 102797, 102811, +102829, 102841, 102859, 102871, 102877, 102881, 102911, 102913, 102929, 102931, +102953, 102967, 102983, 103001, 103007, 103043, 103049, 103067, 103069, 103079, +103087, 103091, 103093, 103099, 103123, 103141, 103171, 103177, 103183, 103217, +103231, 103237, 103289, 103291, 103307, 103319, 103333, 103349, 103357, 103387, +103391, 103393, 103399, 103409, 103421, 103423, 103451, 103457, 103471, 103483, +103511, 103529, 103549, 103553, 103561, 103567, 103573, 103577, 103583, 103591, +103613, 103619, 103643, 103651, 103657, 103669, 103681, 103687, 103699, 103703, +103723, 103769, 103787, 103801, 103811, 103813, 103837, 103841, 103843, 103867, +103889, 103903, 103913, 103919, 103951, 103963, 103967, 103969, 103979, 103981, +103991, 103993, 103997, 104003, 104009, 104021, 104033, 104047, 104053, 104059, +104087, 104089, 104107, 104113, 104119, 104123, 104147, 104149, 104161, 104173, +104179, 104183, 104207, 104231, 104233, 104239, 104243, 104281, 104287, 104297, +104309, 104311, 104323, 104327, 104347, 104369, 104381, 104383, 104393, 104399, +104417, 104459, 104471, 104473, 104479, 104491, 104513, 104527, 104537, 104543, +104549, 104551, 104561, 104579, 104593, 104597, 104623, 104639, 104651, 104659, +104677, 104681, 104683, 104693, 104701, 104707, 104711, 104717, 104723, 104729, +) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/py21compat.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/py21compat.py new file mode 100644 index 0000000..624408b --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/py21compat.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- +# +# Util/py21compat.py : Compatibility code for Python 2.1 +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Compatibility code for Python 2.1 + +Currently, this just defines: + - True and False + - object + - isinstance +""" + +__revision__ = "$Id$" +__all__ = [] + +import sys +import __builtin__ + +# 'True' and 'False' aren't defined in Python 2.1. Define them. +try: + True, False +except NameError: + (True, False) = (1, 0) + __all__ += ['True', 'False'] + +# New-style classes were introduced in Python 2.2. Defining "object" in Python +# 2.1 lets us use new-style classes in versions of Python that support them, +# while still maintaining backward compatibility with old-style classes +try: + object +except NameError: + class object: pass + __all__ += ['object'] + +# Starting with Python 2.2, isinstance allows a tuple for the second argument. +# Also, builtins like "tuple", "list", "str", "unicode", "int", and "long" +# became first-class types, rather than functions. We want to support +# constructs like: +# isinstance(x, (int, long)) +# So we hack it for Python 2.1. +try: + isinstance(5, (int, long)) +except TypeError: + __all__ += ['isinstance'] + _builtin_type_map = { + tuple: type(()), + list: type([]), + str: type(""), + unicode: type(u""), + int: type(0), + long: type(0L), + } + def isinstance(obj, t): + if not __builtin__.isinstance(t, type(())): + # t is not a tuple + return __builtin__.isinstance(obj, _builtin_type_map.get(t, t)) + else: + # t is a tuple + for typ in t: + if __builtin__.isinstance(obj, _builtin_type_map.get(typ, typ)): + return True + return False + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/py3compat.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/py3compat.py new file mode 100644 index 0000000..34e5224 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/py3compat.py @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- +# +# Util/py3compat.py : Compatibility code for handling Py3k / Python 2.x +# +# Written in 2010 by Thorsten Behrens +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Compatibility code for handling string/bytes changes from Python 2.x to Py3k + +In Python 2.x, strings (of type ''str'') contain binary data, including encoded +Unicode text (e.g. UTF-8). The separate type ''unicode'' holds Unicode text. +Unicode literals are specified via the u'...' prefix. Indexing or slicing +either type always produces a string of the same type as the original. +Data read from a file is always of '''str'' type. + +In Python 3.x, strings (type ''str'') may only contain Unicode text. The u'...' +prefix and the ''unicode'' type are now redundant. A new type (called +''bytes'') has to be used for binary data (including any particular +''encoding'' of a string). The b'...' prefix allows one to specify a binary +literal. Indexing or slicing a string produces another string. Slicing a byte +string produces another byte string, but the indexing operation produces an +integer. Data read from a file is of '''str'' type if the file was opened in +text mode, or of ''bytes'' type otherwise. + +Since PyCrypto aims at supporting both Python 2.x and 3.x, the following helper +functions are used to keep the rest of the library as independent as possible +from the actual Python version. + +In general, the code should always deal with binary strings, and use integers +instead of 1-byte character strings. + +b(s) + Take a text string literal (with no prefix or with u'...' prefix) and + make a byte string. +bchr(c) + Take an integer and make a 1-character byte string. +bord(c) + Take the result of indexing on a byte string and make an integer. +tobytes(s) + Take a text string, a byte string, or a sequence of character taken from + a byte string and make a byte string. +""" + +__revision__ = "$Id$" + +import sys + +if sys.version_info[0] == 2: + def b(s): + return s + def bchr(s): + return chr(s) + def bstr(s): + return str(s) + def bord(s): + return ord(s) + if sys.version_info[1] == 1: + def tobytes(s): + try: + return s.encode('latin-1') + except: + return ''.join(s) + else: + def tobytes(s): + if isinstance(s, unicode): + return s.encode("latin-1") + else: + return ''.join(s) +else: + def b(s): + return s.encode("latin-1") # utf-8 would cause some side-effects we don't want + def bchr(s): + return bytes([s]) + def bstr(s): + if isinstance(s,str): + return bytes(s,"latin-1") + else: + return bytes(s) + def bord(s): + return s + def tobytes(s): + if isinstance(s,bytes): + return s + else: + if isinstance(s,str): + return s.encode("latin-1") + else: + return bytes(s) + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/randpool.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/randpool.py new file mode 100644 index 0000000..8b5a0b7 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/randpool.py @@ -0,0 +1,82 @@ +# +# randpool.py : Cryptographically strong random number generation +# +# Part of the Python Cryptography Toolkit +# +# Written by Andrew M. Kuchling, Mark Moraes, and others +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== +# + +__revision__ = "$Id$" + +from Crypto.pct_warnings import RandomPool_DeprecationWarning +import Crypto.Random +import warnings + +class RandomPool: + """Deprecated. Use Random.new() instead. + + See http://www.pycrypto.org/randpool-broken + """ + def __init__(self, numbytes = 160, cipher=None, hash=None, file=None): + warnings.warn("This application uses RandomPool, which is BROKEN in older releases. See http://www.pycrypto.org/randpool-broken", + RandomPool_DeprecationWarning) + self.__rng = Crypto.Random.new() + self.bytes = numbytes + self.bits = self.bytes * 8 + self.entropy = self.bits + + def get_bytes(self, N): + return self.__rng.read(N) + + def _updateEntropyEstimate(self, nbits): + self.entropy += nbits + if self.entropy < 0: + self.entropy = 0 + elif self.entropy > self.bits: + self.entropy = self.bits + + def _randomize(self, N=0, devname="/dev/urandom"): + """Dummy _randomize() function""" + self.__rng.flush() + + def randomize(self, N=0): + """Dummy randomize() function""" + self.__rng.flush() + + def stir(self, s=''): + """Dummy stir() function""" + self.__rng.flush() + + def stir_n(self, N=3): + """Dummy stir_n() function""" + self.__rng.flush() + + def add_event(self, s=''): + """Dummy add_event() function""" + self.__rng.flush() + + def getBytes(self, N): + """Dummy getBytes() function""" + return self.get_bytes(N) + + def addEvent(self, event, s=""): + """Dummy addEvent() function""" + return self.add_event() diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/strxor.so b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/strxor.so new file mode 100755 index 0000000..988dafd Binary files /dev/null and b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/strxor.so differ diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/winrandom.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/winrandom.py new file mode 100644 index 0000000..0242815 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/Util/winrandom.py @@ -0,0 +1,28 @@ +# +# Util/winrandom.py : Stub for Crypto.Random.OSRNG.winrandom +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +__revision__ = "$Id$" + +from Crypto.Random.OSRNG.winrandom import * + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/__init__.py new file mode 100644 index 0000000..c27402e --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/__init__.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Python Cryptography Toolkit + +A collection of cryptographic modules implementing various algorithms +and protocols. + +Subpackages: + +Crypto.Cipher + Secret-key (AES, DES, ARC4) and public-key encryption (RSA PKCS#1) algorithms +Crypto.Hash + Hashing algorithms (MD5, SHA, HMAC) +Crypto.Protocol + Cryptographic protocols (Chaffing, all-or-nothing transform, key derivation + functions). This package does not contain any network protocols. +Crypto.PublicKey + Public-key encryption and signature algorithms (RSA, DSA) +Crypto.Signature + Public-key signature algorithms (RSA PKCS#1) +Crypto.Util + Various useful modules and functions (long-to-string conversion, random number + generation, number theoretic functions) +""" + +__all__ = ['Cipher', 'Hash', 'Protocol', 'PublicKey', 'Util', 'Signature'] + +__version__ = '2.6.1' # See also below and setup.py +__revision__ = "$Id$" + +# New software should look at this instead of at __version__ above. +version_info = (2, 6, 1, 'final', 0) # See also above and setup.py + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/pct_warnings.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/pct_warnings.py new file mode 100644 index 0000000..9b4361e --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Crypto/pct_warnings.py @@ -0,0 +1,60 @@ +# -*- coding: ascii -*- +# +# pct_warnings.py : PyCrypto warnings file +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +# +# Base classes. All our warnings inherit from one of these in order to allow +# the user to specifically filter them. +# + +class CryptoWarning(Warning): + """Base class for PyCrypto warnings""" + +class CryptoDeprecationWarning(DeprecationWarning, CryptoWarning): + """Base PyCrypto DeprecationWarning class""" + +class CryptoRuntimeWarning(RuntimeWarning, CryptoWarning): + """Base PyCrypto RuntimeWarning class""" + +# +# Warnings that we might actually use +# + +class RandomPool_DeprecationWarning(CryptoDeprecationWarning): + """Issued when Crypto.Util.randpool.RandomPool is instantiated.""" + +class ClockRewindWarning(CryptoRuntimeWarning): + """Warning for when the system clock moves backwards.""" + +class GetRandomNumber_DeprecationWarning(CryptoDeprecationWarning): + """Issued when Crypto.Util.number.getRandomNumber is invoked.""" + +class PowmInsecureWarning(CryptoRuntimeWarning): + """Warning for when _fastmath is built without mpz_powm_sec""" + +# By default, we want this warning to be shown every time we compensate for +# clock rewinding. +import warnings as _warnings +_warnings.filterwarnings('always', category=ClockRewindWarning, append=1) + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Fabric-1.9.0-py2.7.egg-info/PKG-INFO b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Fabric-1.9.0-py2.7.egg-info/PKG-INFO new file mode 100644 index 0000000..7044169 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Fabric-1.9.0-py2.7.egg-info/PKG-INFO @@ -0,0 +1,78 @@ +Metadata-Version: 1.1 +Name: Fabric +Version: 1.9.0 +Summary: Fabric is a simple, Pythonic tool for remote execution and deployment. +Home-page: http://fabfile.org +Author: Jeff Forcier +Author-email: jeff@bitprophet.org +License: UNKNOWN +Description: + To find out what's new in this version of Fabric, please see `the changelog + `_. + + You can also install the `in-development version + `_ using + pip, with `pip install fabric==dev`. + + ---- + + Fabric is a Python (2.5-2.7) library and command-line tool for + streamlining the use of SSH for application deployment or systems + administration tasks. + + It provides a basic suite of operations for executing local or remote shell + commands (normally or via ``sudo``) and uploading/downloading files, as well as + auxiliary functionality such as prompting the running user for input, or + aborting execution. + + Typical use involves creating a Python module containing one or more functions, + then executing them via the ``fab`` command-line tool. Below is a small but + complete "fabfile" containing a single task:: + + from fabric.api import run + + def host_type(): + run('uname -s') + + Once a task is defined, it may be run on one or more servers, like so:: + + $ fab -H localhost,linuxbox host_type + [localhost] run: uname -s + [localhost] out: Darwin + [linuxbox] run: uname -s + [linuxbox] out: Linux + + Done. + Disconnecting from localhost... done. + Disconnecting from linuxbox... done. + + In addition to use via the ``fab`` tool, Fabric's components may be imported + into other Python code, providing a Pythonic interface to the SSH protocol + suite at a higher level than that provided by e.g. the ``Paramiko`` library + (which Fabric itself uses.) + + + ---- + + For more information, please see the Fabric website or execute ``fab --help``. + +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: System Administrators +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Unix +Classifier: Operating System :: POSIX +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2.5 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Topic :: Software Development +Classifier: Topic :: Software Development :: Build Tools +Classifier: Topic :: Software Development :: Libraries +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: System :: Clustering +Classifier: Topic :: System :: Software Distribution +Classifier: Topic :: System :: Systems Administration diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Fabric-1.9.0-py2.7.egg-info/SOURCES.txt b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Fabric-1.9.0-py2.7.egg-info/SOURCES.txt new file mode 100644 index 0000000..839f76e --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Fabric-1.9.0-py2.7.egg-info/SOURCES.txt @@ -0,0 +1,126 @@ +AUTHORS +INSTALL +LICENSE +MANIFEST.in +README.rst +requirements.txt +setup.cfg +setup.py +Fabric.egg-info/PKG-INFO +Fabric.egg-info/SOURCES.txt +Fabric.egg-info/dependency_links.txt +Fabric.egg-info/entry_points.txt +Fabric.egg-info/requires.txt +Fabric.egg-info/top_level.txt +fabfile/__init__.py +fabfile/tag.py +fabfile/utils.py +fabric/__init__.py +fabric/api.py +fabric/auth.py +fabric/colors.py +fabric/context_managers.py +fabric/decorators.py +fabric/docs.py +fabric/exceptions.py +fabric/io.py +fabric/job_queue.py +fabric/main.py +fabric/network.py +fabric/operations.py +fabric/sftp.py +fabric/state.py +fabric/task_utils.py +fabric/tasks.py +fabric/thread_handling.py +fabric/utils.py +fabric/version.py +fabric/contrib/__init__.py +fabric/contrib/console.py +fabric/contrib/django.py +fabric/contrib/files.py +fabric/contrib/project.py +sites/shared_conf.py +sites/_shared_static/logo.png +sites/docs/conf.py +sites/docs/index.rst +sites/docs/tutorial.rst +sites/docs/api/contrib/console.rst +sites/docs/api/contrib/django.rst +sites/docs/api/contrib/files.rst +sites/docs/api/contrib/project.rst +sites/docs/api/core/colors.rst +sites/docs/api/core/context_managers.rst +sites/docs/api/core/decorators.rst +sites/docs/api/core/docs.rst +sites/docs/api/core/network.rst +sites/docs/api/core/operations.rst +sites/docs/api/core/tasks.rst +sites/docs/api/core/utils.rst +sites/docs/usage/env.rst +sites/docs/usage/execution.rst +sites/docs/usage/fab.rst +sites/docs/usage/fabfiles.rst +sites/docs/usage/interactivity.rst +sites/docs/usage/library.rst +sites/docs/usage/output_controls.rst +sites/docs/usage/parallel.rst +sites/docs/usage/ssh.rst +sites/docs/usage/tasks.rst +sites/www/changelog.rst +sites/www/conf.py +sites/www/contact.rst +sites/www/development.rst +sites/www/faq.rst +sites/www/index.rst +sites/www/installing.rst +sites/www/roadmap.rst +sites/www/troubleshooting.rst +tests/Python26SocketServer.py +tests/client.key +tests/client.key.pub +tests/fake_filesystem.py +tests/mock_streams.py +tests/private.key +tests/server.py +tests/test_context_managers.py +tests/test_contrib.py +tests/test_decorators.py +tests/test_io.py +tests/test_main.py +tests/test_network.py +tests/test_operations.py +tests/test_parallel.py +tests/test_project.py +tests/test_server.py +tests/test_state.py +tests/test_tasks.py +tests/test_utils.py +tests/test_version.py +tests/utils.py +tests/support/__init__.py +tests/support/classbased_task_fabfile.py +tests/support/decorated_fabfile.py +tests/support/decorated_fabfile_with_classbased_task.py +tests/support/decorated_fabfile_with_modules.py +tests/support/decorator_order.py +tests/support/deep.py +tests/support/default_task_submodule.py +tests/support/default_tasks.py +tests/support/docstring.py +tests/support/explicit_fabfile.py +tests/support/flat_alias.py +tests/support/flat_aliases.py +tests/support/implicit_fabfile.py +tests/support/mapping.py +tests/support/module_fabtasks.py +tests/support/nested_alias.py +tests/support/nested_aliases.py +tests/support/ssh_config +tests/support/testserver_ssh_config +tests/support/submodule/__init__.py +tests/support/submodule/subsubmodule/__init__.py +tests/support/tree/__init__.py +tests/support/tree/db.py +tests/support/tree/system/__init__.py +tests/support/tree/system/debian.py \ No newline at end of file diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Fabric-1.9.0-py2.7.egg-info/dependency_links.txt b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Fabric-1.9.0-py2.7.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Fabric-1.9.0-py2.7.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Fabric-1.9.0-py2.7.egg-info/entry_points.txt b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Fabric-1.9.0-py2.7.egg-info/entry_points.txt new file mode 100644 index 0000000..a500058 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Fabric-1.9.0-py2.7.egg-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +fab = fabric.main:main + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Fabric-1.9.0-py2.7.egg-info/installed-files.txt b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Fabric-1.9.0-py2.7.egg-info/installed-files.txt new file mode 100644 index 0000000..59703c7 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Fabric-1.9.0-py2.7.egg-info/installed-files.txt @@ -0,0 +1,64 @@ +../fabfile/__init__.py +../fabfile/tag.py +../fabfile/utils.py +../fabric/__init__.py +../fabric/api.py +../fabric/auth.py +../fabric/colors.py +../fabric/context_managers.py +../fabric/decorators.py +../fabric/docs.py +../fabric/exceptions.py +../fabric/io.py +../fabric/job_queue.py +../fabric/main.py +../fabric/network.py +../fabric/operations.py +../fabric/sftp.py +../fabric/state.py +../fabric/task_utils.py +../fabric/tasks.py +../fabric/thread_handling.py +../fabric/utils.py +../fabric/version.py +../fabric/contrib/__init__.py +../fabric/contrib/console.py +../fabric/contrib/django.py +../fabric/contrib/files.py +../fabric/contrib/project.py +../fabfile/__init__.pyc +../fabfile/tag.pyc +../fabfile/utils.pyc +../fabric/__init__.pyc +../fabric/api.pyc +../fabric/auth.pyc +../fabric/colors.pyc +../fabric/context_managers.pyc +../fabric/decorators.pyc +../fabric/docs.pyc +../fabric/exceptions.pyc +../fabric/io.pyc +../fabric/job_queue.pyc +../fabric/main.pyc +../fabric/network.pyc +../fabric/operations.pyc +../fabric/sftp.pyc +../fabric/state.pyc +../fabric/task_utils.pyc +../fabric/tasks.pyc +../fabric/thread_handling.pyc +../fabric/utils.pyc +../fabric/version.pyc +../fabric/contrib/__init__.pyc +../fabric/contrib/console.pyc +../fabric/contrib/django.pyc +../fabric/contrib/files.pyc +../fabric/contrib/project.pyc +./ +dependency_links.txt +entry_points.txt +PKG-INFO +requires.txt +SOURCES.txt +top_level.txt +../../../../bin/fab diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Fabric-1.9.0-py2.7.egg-info/requires.txt b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Fabric-1.9.0-py2.7.egg-info/requires.txt new file mode 100644 index 0000000..b51db92 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Fabric-1.9.0-py2.7.egg-info/requires.txt @@ -0,0 +1 @@ +paramiko>=1.10 \ No newline at end of file diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/Fabric-1.9.0-py2.7.egg-info/top_level.txt b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Fabric-1.9.0-py2.7.egg-info/top_level.txt new file mode 100644 index 0000000..fbc5595 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/Fabric-1.9.0-py2.7.egg-info/top_level.txt @@ -0,0 +1,2 @@ +fabfile +fabric diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/_markerlib/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/_markerlib/__init__.py new file mode 100644 index 0000000..e2b237b --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/_markerlib/__init__.py @@ -0,0 +1,16 @@ +try: + import ast + from _markerlib.markers import default_environment, compile, interpret +except ImportError: + if 'ast' in globals(): + raise + def default_environment(): + return {} + def compile(marker): + def marker_fn(environment=None, override=None): + # 'empty markers are True' heuristic won't install extra deps. + return not marker.strip() + marker_fn.__doc__ = marker + return marker_fn + def interpret(marker, environment=None, override=None): + return compile(marker)() diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/_markerlib/markers.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/_markerlib/markers.py new file mode 100644 index 0000000..fa83706 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/_markerlib/markers.py @@ -0,0 +1,119 @@ +# -*- coding: utf-8 -*- +"""Interpret PEP 345 environment markers. + +EXPR [in|==|!=|not in] EXPR [or|and] ... + +where EXPR belongs to any of those: + + python_version = '%s.%s' % (sys.version_info[0], sys.version_info[1]) + python_full_version = sys.version.split()[0] + os.name = os.name + sys.platform = sys.platform + platform.version = platform.version() + platform.machine = platform.machine() + platform.python_implementation = platform.python_implementation() + a free string, like '2.6', or 'win32' +""" + +__all__ = ['default_environment', 'compile', 'interpret'] + +import ast +import os +import platform +import sys +import weakref + +_builtin_compile = compile + +try: + from platform import python_implementation +except ImportError: + if os.name == "java": + # Jython 2.5 has ast module, but not platform.python_implementation() function. + def python_implementation(): + return "Jython" + else: + raise + + +# restricted set of variables +_VARS = {'sys.platform': sys.platform, + 'python_version': '%s.%s' % sys.version_info[:2], + # FIXME parsing sys.platform is not reliable, but there is no other + # way to get e.g. 2.7.2+, and the PEP is defined with sys.version + 'python_full_version': sys.version.split(' ', 1)[0], + 'os.name': os.name, + 'platform.version': platform.version(), + 'platform.machine': platform.machine(), + 'platform.python_implementation': python_implementation(), + 'extra': None # wheel extension + } + +for var in list(_VARS.keys()): + if '.' in var: + _VARS[var.replace('.', '_')] = _VARS[var] + +def default_environment(): + """Return copy of default PEP 385 globals dictionary.""" + return dict(_VARS) + +class ASTWhitelist(ast.NodeTransformer): + def __init__(self, statement): + self.statement = statement # for error messages + + ALLOWED = (ast.Compare, ast.BoolOp, ast.Attribute, ast.Name, ast.Load, ast.Str) + # Bool operations + ALLOWED += (ast.And, ast.Or) + # Comparison operations + ALLOWED += (ast.Eq, ast.Gt, ast.GtE, ast.In, ast.Is, ast.IsNot, ast.Lt, ast.LtE, ast.NotEq, ast.NotIn) + + def visit(self, node): + """Ensure statement only contains allowed nodes.""" + if not isinstance(node, self.ALLOWED): + raise SyntaxError('Not allowed in environment markers.\n%s\n%s' % + (self.statement, + (' ' * node.col_offset) + '^')) + return ast.NodeTransformer.visit(self, node) + + def visit_Attribute(self, node): + """Flatten one level of attribute access.""" + new_node = ast.Name("%s.%s" % (node.value.id, node.attr), node.ctx) + return ast.copy_location(new_node, node) + +def parse_marker(marker): + tree = ast.parse(marker, mode='eval') + new_tree = ASTWhitelist(marker).generic_visit(tree) + return new_tree + +def compile_marker(parsed_marker): + return _builtin_compile(parsed_marker, '', 'eval', + dont_inherit=True) + +_cache = weakref.WeakValueDictionary() + +def compile(marker): + """Return compiled marker as a function accepting an environment dict.""" + try: + return _cache[marker] + except KeyError: + pass + if not marker.strip(): + def marker_fn(environment=None, override=None): + """""" + return True + else: + compiled_marker = compile_marker(parse_marker(marker)) + def marker_fn(environment=None, override=None): + """override updates environment""" + if override is None: + override = {} + if environment is None: + environment = default_environment() + environment.update(override) + return eval(compiled_marker, environment) + marker_fn.__doc__ = marker + _cache[marker] = marker_fn + return _cache[marker] + +def interpret(marker, environment=None): + return compile(marker)(environment) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/easy_install.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/easy_install.py new file mode 100644 index 0000000..d87e984 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/easy_install.py @@ -0,0 +1,5 @@ +"""Run the EasyInstall command""" + +if __name__ == '__main__': + from setuptools.command.easy_install import main + main() diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa-0.11-py2.7.egg-info/PKG-INFO b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa-0.11-py2.7.egg-info/PKG-INFO new file mode 100644 index 0000000..7ff74ca --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa-0.11-py2.7.egg-info/PKG-INFO @@ -0,0 +1,17 @@ +Metadata-Version: 1.1 +Name: ecdsa +Version: 0.11 +Summary: ECDSA cryptographic signature library (pure python) +Home-page: http://github.com/warner/python-ecdsa +Author: Brian Warner +Author-email: warner-pyecdsa@lothar.com +License: MIT +Description: UNKNOWN +Platform: UNKNOWN +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.2 +Classifier: Programming Language :: Python :: 3.3 diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa-0.11-py2.7.egg-info/SOURCES.txt b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa-0.11-py2.7.egg-info/SOURCES.txt new file mode 100644 index 0000000..3b79a89 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa-0.11-py2.7.egg-info/SOURCES.txt @@ -0,0 +1,20 @@ +LICENSE +MANIFEST.in +NEWS +README.md +ecdsa/__init__.py +ecdsa/_version.py +ecdsa/curves.py +ecdsa/der.py +ecdsa/ecdsa.py +ecdsa/ellipticcurve.py +ecdsa/keys.py +ecdsa/numbertheory.py +ecdsa/rfc6979.py +ecdsa/six.py +ecdsa/test_pyecdsa.py +ecdsa/util.py +ecdsa.egg-info/PKG-INFO +ecdsa.egg-info/SOURCES.txt +ecdsa.egg-info/dependency_links.txt +ecdsa.egg-info/top_level.txt \ No newline at end of file diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa-0.11-py2.7.egg-info/dependency_links.txt b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa-0.11-py2.7.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa-0.11-py2.7.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa-0.11-py2.7.egg-info/installed-files.txt b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa-0.11-py2.7.egg-info/installed-files.txt new file mode 100644 index 0000000..0c1ab7f --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa-0.11-py2.7.egg-info/installed-files.txt @@ -0,0 +1,29 @@ +../ecdsa/__init__.py +../ecdsa/_version.py +../ecdsa/curves.py +../ecdsa/der.py +../ecdsa/ecdsa.py +../ecdsa/ellipticcurve.py +../ecdsa/keys.py +../ecdsa/numbertheory.py +../ecdsa/rfc6979.py +../ecdsa/six.py +../ecdsa/test_pyecdsa.py +../ecdsa/util.py +../ecdsa/__init__.pyc +../ecdsa/_version.pyc +../ecdsa/curves.pyc +../ecdsa/der.pyc +../ecdsa/ecdsa.pyc +../ecdsa/ellipticcurve.pyc +../ecdsa/keys.pyc +../ecdsa/numbertheory.pyc +../ecdsa/rfc6979.pyc +../ecdsa/six.pyc +../ecdsa/test_pyecdsa.pyc +../ecdsa/util.pyc +./ +dependency_links.txt +PKG-INFO +SOURCES.txt +top_level.txt diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa-0.11-py2.7.egg-info/top_level.txt b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa-0.11-py2.7.egg-info/top_level.txt new file mode 100644 index 0000000..aa5efdb --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa-0.11-py2.7.egg-info/top_level.txt @@ -0,0 +1 @@ +ecdsa diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/__init__.py new file mode 100644 index 0000000..15c1b4e --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/__init__.py @@ -0,0 +1,17 @@ +__all__ = ["curves", "der", "ecdsa", "ellipticcurve", "keys", "numbertheory", + "test_pyecdsa", "util", "six"] +from .keys import SigningKey, VerifyingKey, BadSignatureError, BadDigestError +from .curves import NIST192p, NIST224p, NIST256p, NIST384p, NIST521p, SECP256k1 + +_hush_pyflakes = [SigningKey, VerifyingKey, BadSignatureError, BadDigestError, + NIST192p, NIST224p, NIST256p, NIST384p, NIST521p, SECP256k1] +del _hush_pyflakes + +# This code comes from http://github.com/warner/python-ecdsa + +try: + from _version import __version__ as v + __version__ = v + del v +except ImportError: + __version__ = "UNKNOWN" diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/_version.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/_version.py new file mode 100644 index 0000000..3d9d7a4 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/_version.py @@ -0,0 +1,5 @@ + +# This file is originally generated from Git information by running 'setup.py +# version'. Distribution tarballs contain a pre-generated copy of this file. + +__version__ = '0.11' diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/curves.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/curves.py new file mode 100644 index 0000000..b8772a2 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/curves.py @@ -0,0 +1,46 @@ +from __future__ import division + +from . import der, ecdsa + +class UnknownCurveError(Exception): + pass + +def orderlen(order): + return (1+len("%x"%order))//2 # bytes + +# the NIST curves +class Curve: + def __init__(self, name, curve, generator, oid): + self.name = name + self.curve = curve + self.generator = generator + self.order = generator.order() + self.baselen = orderlen(self.order) + self.verifying_key_length = 2*self.baselen + self.signature_length = 2*self.baselen + self.oid = oid + self.encoded_oid = der.encode_oid(*oid) + +NIST192p = Curve("NIST192p", ecdsa.curve_192, ecdsa.generator_192, + (1, 2, 840, 10045, 3, 1, 1)) +NIST224p = Curve("NIST224p", ecdsa.curve_224, ecdsa.generator_224, + (1, 3, 132, 0, 33)) +NIST256p = Curve("NIST256p", ecdsa.curve_256, ecdsa.generator_256, + (1, 2, 840, 10045, 3, 1, 7)) +NIST384p = Curve("NIST384p", ecdsa.curve_384, ecdsa.generator_384, + (1, 3, 132, 0, 34)) +NIST521p = Curve("NIST521p", ecdsa.curve_521, ecdsa.generator_521, + (1, 3, 132, 0, 35)) +SECP256k1 = Curve("SECP256k1", + ecdsa.curve_secp256k1, ecdsa.generator_secp256k1, + (1, 3, 132, 0, 10)) + +curves = [NIST192p, NIST224p, NIST256p, NIST384p, NIST521p, SECP256k1] + +def find_curve(oid_curve): + for c in curves: + if c.oid == oid_curve: + return c + raise UnknownCurveError("I don't know about the curve with oid %s." + "I only know about these: %s" % + (oid_curve, [c.name for c in curves])) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/der.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/der.py new file mode 100644 index 0000000..e7b6068 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/der.py @@ -0,0 +1,199 @@ +from __future__ import division + +import binascii +import base64 +from .six import int2byte, b, PY3, integer_types, text_type + +class UnexpectedDER(Exception): + pass + +def encode_constructed(tag, value): + return int2byte(0xa0+tag) + encode_length(len(value)) + value +def encode_integer(r): + assert r >= 0 # can't support negative numbers yet + h = ("%x" % r).encode() + if len(h) % 2: + h = b("0") + h + s = binascii.unhexlify(h) + num = s[0] if isinstance(s[0], integer_types) else ord(s[0]) + if num <= 0x7f: + return b("\x02") + int2byte(len(s)) + s + else: + # DER integers are two's complement, so if the first byte is + # 0x80-0xff then we need an extra 0x00 byte to prevent it from + # looking negative. + return b("\x02") + int2byte(len(s)+1) + b("\x00") + s + +def encode_bitstring(s): + return b("\x03") + encode_length(len(s)) + s +def encode_octet_string(s): + return b("\x04") + encode_length(len(s)) + s +def encode_oid(first, second, *pieces): + assert first <= 2 + assert second <= 39 + encoded_pieces = [int2byte(40*first+second)] + [encode_number(p) + for p in pieces] + body = b('').join(encoded_pieces) + return b('\x06') + encode_length(len(body)) + body +def encode_sequence(*encoded_pieces): + total_len = sum([len(p) for p in encoded_pieces]) + return b('\x30') + encode_length(total_len) + b('').join(encoded_pieces) +def encode_number(n): + b128_digits = [] + while n: + b128_digits.insert(0, (n & 0x7f) | 0x80) + n = n >> 7 + if not b128_digits: + b128_digits.append(0) + b128_digits[-1] &= 0x7f + return b('').join([int2byte(d) for d in b128_digits]) + +def remove_constructed(string): + s0 = string[0] if isinstance(string[0], integer_types) else ord(string[0]) + if (s0 & 0xe0) != 0xa0: + raise UnexpectedDER("wanted constructed tag (0xa0-0xbf), got 0x%02x" + % s0) + tag = s0 & 0x1f + length, llen = read_length(string[1:]) + body = string[1+llen:1+llen+length] + rest = string[1+llen+length:] + return tag, body, rest + +def remove_sequence(string): + if not string.startswith(b("\x30")): + n = string[0] if isinstance(string[0], integer_types) else ord(string[0]) + raise UnexpectedDER("wanted sequence (0x30), got 0x%02x" % n) + length, lengthlength = read_length(string[1:]) + endseq = 1+lengthlength+length + return string[1+lengthlength:endseq], string[endseq:] + +def remove_octet_string(string): + if not string.startswith(b("\x04")): + n = string[0] if isinstance(string[0], integer_types) else ord(string[0]) + raise UnexpectedDER("wanted octetstring (0x04), got 0x%02x" % n) + length, llen = read_length(string[1:]) + body = string[1+llen:1+llen+length] + rest = string[1+llen+length:] + return body, rest + +def remove_object(string): + if not string.startswith(b("\x06")): + n = string[0] if isinstance(string[0], integer_types) else ord(string[0]) + raise UnexpectedDER("wanted object (0x06), got 0x%02x" % n) + length, lengthlength = read_length(string[1:]) + body = string[1+lengthlength:1+lengthlength+length] + rest = string[1+lengthlength+length:] + numbers = [] + while body: + n, ll = read_number(body) + numbers.append(n) + body = body[ll:] + n0 = numbers.pop(0) + first = n0//40 + second = n0-(40*first) + numbers.insert(0, first) + numbers.insert(1, second) + return tuple(numbers), rest + +def remove_integer(string): + if not string.startswith(b("\x02")): + n = string[0] if isinstance(string[0], integer_types) else ord(string[0]) + raise UnexpectedDER("wanted integer (0x02), got 0x%02x" % n) + length, llen = read_length(string[1:]) + numberbytes = string[1+llen:1+llen+length] + rest = string[1+llen+length:] + nbytes = numberbytes[0] if isinstance(numberbytes[0], integer_types) else ord(numberbytes[0]) + assert nbytes < 0x80 # can't support negative numbers yet + return int(binascii.hexlify(numberbytes), 16), rest + +def read_number(string): + number = 0 + llen = 0 + # base-128 big endian, with b7 set in all but the last byte + while True: + if llen > len(string): + raise UnexpectedDER("ran out of length bytes") + number = number << 7 + d = string[llen] if isinstance(string[llen], integer_types) else ord(string[llen]) + number += (d & 0x7f) + llen += 1 + if not d & 0x80: + break + return number, llen + +def encode_length(l): + assert l >= 0 + if l < 0x80: + return int2byte(l) + s = ("%x" % l).encode() + if len(s)%2: + s = b("0")+s + s = binascii.unhexlify(s) + llen = len(s) + return int2byte(0x80|llen) + s + +def read_length(string): + num = string[0] if isinstance(string[0], integer_types) else ord(string[0]) + if not (num & 0x80): + # short form + return (num & 0x7f), 1 + # else long-form: b0&0x7f is number of additional base256 length bytes, + # big-endian + llen = num & 0x7f + if llen > len(string)-1: + raise UnexpectedDER("ran out of length bytes") + return int(binascii.hexlify(string[1:1+llen]), 16), 1+llen + +def remove_bitstring(string): + num = string[0] if isinstance(string[0], integer_types) else ord(string[0]) + if not string.startswith(b("\x03")): + raise UnexpectedDER("wanted bitstring (0x03), got 0x%02x" % num) + length, llen = read_length(string[1:]) + body = string[1+llen:1+llen+length] + rest = string[1+llen+length:] + return body, rest + +# SEQUENCE([1, STRING(secexp), cont[0], OBJECT(curvename), cont[1], BINTSTRING) + + +# signatures: (from RFC3279) +# ansi-X9-62 OBJECT IDENTIFIER ::= { +# iso(1) member-body(2) us(840) 10045 } +# +# id-ecSigType OBJECT IDENTIFIER ::= { +# ansi-X9-62 signatures(4) } +# ecdsa-with-SHA1 OBJECT IDENTIFIER ::= { +# id-ecSigType 1 } +## so 1,2,840,10045,4,1 +## so 0x42, .. .. + +# Ecdsa-Sig-Value ::= SEQUENCE { +# r INTEGER, +# s INTEGER } + +# id-public-key-type OBJECT IDENTIFIER ::= { ansi-X9.62 2 } +# +# id-ecPublicKey OBJECT IDENTIFIER ::= { id-publicKeyType 1 } + +# I think the secp224r1 identifier is (t=06,l=05,v=2b81040021) +# secp224r1 OBJECT IDENTIFIER ::= { +# iso(1) identified-organization(3) certicom(132) curve(0) 33 } +# and the secp384r1 is (t=06,l=05,v=2b81040022) +# secp384r1 OBJECT IDENTIFIER ::= { +# iso(1) identified-organization(3) certicom(132) curve(0) 34 } + +def unpem(pem): + if isinstance(pem, text_type): + pem = pem.encode() + + d = b("").join([l.strip() for l in pem.split(b("\n")) + if l and not l.startswith(b("-----"))]) + return base64.b64decode(d) +def topem(der, name): + b64 = base64.b64encode(der) + lines = [("-----BEGIN %s-----\n" % name).encode()] + lines.extend([b64[start:start+64]+b("\n") + for start in range(0, len(b64), 64)]) + lines.append(("-----END %s-----\n" % name).encode()) + return b("").join(lines) + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/ecdsa.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/ecdsa.py new file mode 100644 index 0000000..4eb2d39 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/ecdsa.py @@ -0,0 +1,576 @@ +#! /usr/bin/env python + +""" +Implementation of Elliptic-Curve Digital Signatures. + +Classes and methods for elliptic-curve signatures: +private keys, public keys, signatures, +NIST prime-modulus curves with modulus lengths of +192, 224, 256, 384, and 521 bits. + +Example: + + # (In real-life applications, you would probably want to + # protect against defects in SystemRandom.) + from random import SystemRandom + randrange = SystemRandom().randrange + + # Generate a public/private key pair using the NIST Curve P-192: + + g = generator_192 + n = g.order() + secret = randrange( 1, n ) + pubkey = Public_key( g, g * secret ) + privkey = Private_key( pubkey, secret ) + + # Signing a hash value: + + hash = randrange( 1, n ) + signature = privkey.sign( hash, randrange( 1, n ) ) + + # Verifying a signature for a hash value: + + if pubkey.verifies( hash, signature ): + print_("Demo verification succeeded.") + else: + print_("*** Demo verification failed.") + + # Verification fails if the hash value is modified: + + if pubkey.verifies( hash-1, signature ): + print_("**** Demo verification failed to reject tampered hash.") + else: + print_("Demo verification correctly rejected tampered hash.") + +Version of 2009.05.16. + +Revision history: + 2005.12.31 - Initial version. + 2008.11.25 - Substantial revisions introducing new classes. + 2009.05.16 - Warn against using random.randrange in real applications. + 2009.05.17 - Use random.SystemRandom by default. + +Written in 2005 by Peter Pearson and placed in the public domain. +""" + +from .six import int2byte, b, print_ +from . import ellipticcurve +from . import numbertheory +import random + + + +class Signature( object ): + """ECDSA signature. + """ + def __init__( self, r, s ): + self.r = r + self.s = s + + + +class Public_key( object ): + """Public key for ECDSA. + """ + + def __init__( self, generator, point ): + """generator is the Point that generates the group, + point is the Point that defines the public key. + """ + + self.curve = generator.curve() + self.generator = generator + self.point = point + n = generator.order() + if not n: + raise RuntimeError("Generator point must have order.") + if not n * point == ellipticcurve.INFINITY: + raise RuntimeError("Generator point order is bad.") + if point.x() < 0 or n <= point.x() or point.y() < 0 or n <= point.y(): + raise RuntimeError("Generator point has x or y out of range.") + + + def verifies( self, hash, signature ): + """Verify that signature is a valid signature of hash. + Return True if the signature is valid. + """ + + # From X9.62 J.3.1. + + G = self.generator + n = G.order() + r = signature.r + s = signature.s + if r < 1 or r > n-1: return False + if s < 1 or s > n-1: return False + c = numbertheory.inverse_mod( s, n ) + u1 = ( hash * c ) % n + u2 = ( r * c ) % n + xy = u1 * G + u2 * self.point + v = xy.x() % n + return v == r + + + +class Private_key( object ): + """Private key for ECDSA. + """ + + def __init__( self, public_key, secret_multiplier ): + """public_key is of class Public_key; + secret_multiplier is a large integer. + """ + + self.public_key = public_key + self.secret_multiplier = secret_multiplier + + def sign( self, hash, random_k ): + """Return a signature for the provided hash, using the provided + random nonce. It is absolutely vital that random_k be an unpredictable + number in the range [1, self.public_key.point.order()-1]. If + an attacker can guess random_k, he can compute our private key from a + single signature. Also, if an attacker knows a few high-order + bits (or a few low-order bits) of random_k, he can compute our private + key from many signatures. The generation of nonces with adequate + cryptographic strength is very difficult and far beyond the scope + of this comment. + + May raise RuntimeError, in which case retrying with a new + random value k is in order. + """ + + G = self.public_key.generator + n = G.order() + k = random_k % n + p1 = k * G + r = p1.x() + if r == 0: raise RuntimeError("amazingly unlucky random number r") + s = ( numbertheory.inverse_mod( k, n ) * \ + ( hash + ( self.secret_multiplier * r ) % n ) ) % n + if s == 0: raise RuntimeError("amazingly unlucky random number s") + return Signature( r, s ) + + + +def int_to_string( x ): + """Convert integer x into a string of bytes, as per X9.62.""" + assert x >= 0 + if x == 0: return b('\0') + result = [] + while x: + ordinal = x & 0xFF + result.append(int2byte(ordinal)) + x >>= 8 + + result.reverse() + return b('').join(result) + + +def string_to_int( s ): + """Convert a string of bytes into an integer, as per X9.62.""" + result = 0 + for c in s: + if not isinstance(c, int): c = ord( c ) + result = 256 * result + c + return result + + +def digest_integer( m ): + """Convert an integer into a string of bytes, compute + its SHA-1 hash, and convert the result to an integer.""" + # + # I don't expect this function to be used much. I wrote + # it in order to be able to duplicate the examples + # in ECDSAVS. + # + from hashlib import sha1 + return string_to_int( sha1( int_to_string( m ) ).digest() ) + + +def point_is_valid( generator, x, y ): + """Is (x,y) a valid public key based on the specified generator?""" + + # These are the tests specified in X9.62. + + n = generator.order() + curve = generator.curve() + if x < 0 or n <= x or y < 0 or n <= y: + return False + if not curve.contains_point( x, y ): + return False + if not n*ellipticcurve.Point( curve, x, y ) == \ + ellipticcurve.INFINITY: + return False + return True + + + +# NIST Curve P-192: +_p = 6277101735386680763835789423207666416083908700390324961279 +_r = 6277101735386680763835789423176059013767194773182842284081 +# s = 0x3045ae6fc8422f64ed579528d38120eae12196d5L +# c = 0x3099d2bbbfcb2538542dcd5fb078b6ef5f3d6fe2c745de65L +_b = 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1 +_Gx = 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012 +_Gy = 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811 + +curve_192 = ellipticcurve.CurveFp( _p, -3, _b ) +generator_192 = ellipticcurve.Point( curve_192, _Gx, _Gy, _r ) + + +# NIST Curve P-224: +_p = 26959946667150639794667015087019630673557916260026308143510066298881 +_r = 26959946667150639794667015087019625940457807714424391721682722368061 +# s = 0xbd71344799d5c7fcdc45b59fa3b9ab8f6a948bc5L +# c = 0x5b056c7e11dd68f40469ee7f3c7a7d74f7d121116506d031218291fbL +_b = 0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4 +_Gx =0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21 +_Gy = 0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34 + +curve_224 = ellipticcurve.CurveFp( _p, -3, _b ) +generator_224 = ellipticcurve.Point( curve_224, _Gx, _Gy, _r ) + +# NIST Curve P-256: +_p = 115792089210356248762697446949407573530086143415290314195533631308867097853951 +_r = 115792089210356248762697446949407573529996955224135760342422259061068512044369 +# s = 0xc49d360886e704936a6678e1139d26b7819f7e90L +# c = 0x7efba1662985be9403cb055c75d4f7e0ce8d84a9c5114abcaf3177680104fa0dL +_b = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b +_Gx = 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296 +_Gy = 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5 + +curve_256 = ellipticcurve.CurveFp( _p, -3, _b ) +generator_256 = ellipticcurve.Point( curve_256, _Gx, _Gy, _r ) + +# NIST Curve P-384: +_p = 39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319 +_r = 39402006196394479212279040100143613805079739270465446667946905279627659399113263569398956308152294913554433653942643 +# s = 0xa335926aa319a27a1d00896a6773a4827acdac73L +# c = 0x79d1e655f868f02fff48dcdee14151ddb80643c1406d0ca10dfe6fc52009540a495e8042ea5f744f6e184667cc722483L +_b = 0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef +_Gx = 0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7 +_Gy = 0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f + +curve_384 = ellipticcurve.CurveFp( _p, -3, _b ) +generator_384 = ellipticcurve.Point( curve_384, _Gx, _Gy, _r ) + +# NIST Curve P-521: +_p = 6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151 +_r = 6864797660130609714981900799081393217269435300143305409394463459185543183397655394245057746333217197532963996371363321113864768612440380340372808892707005449 +# s = 0xd09e8800291cb85396cc6717393284aaa0da64baL +# c = 0x0b48bfa5f420a34949539d2bdfc264eeeeb077688e44fbf0ad8f6d0edb37bd6b533281000518e19f1b9ffbe0fe9ed8a3c2200b8f875e523868c70c1e5bf55bad637L +_b = 0x051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00 +_Gx = 0xc6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66 +_Gy = 0x11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650 + +curve_521 = ellipticcurve.CurveFp( _p, -3, _b ) +generator_521 = ellipticcurve.Point( curve_521, _Gx, _Gy, _r ) + +# Certicom secp256-k1 +_a = 0x0000000000000000000000000000000000000000000000000000000000000000 +_b = 0x0000000000000000000000000000000000000000000000000000000000000007 +_p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f +_Gx = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 +_Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 +_r = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 + +curve_secp256k1 = ellipticcurve.CurveFp( _p, _a, _b) +generator_secp256k1 = ellipticcurve.Point( curve_secp256k1, _Gx, _Gy, _r) + + + +def __main__(): + class TestFailure(Exception): pass + + def test_point_validity( generator, x, y, expected ): + """generator defines the curve; is (x,y) a point on + this curve? "expected" is True if the right answer is Yes.""" + if point_is_valid( generator, x, y ) == expected: + print_("Point validity tested as expected.") + else: + raise TestFailure("*** Point validity test gave wrong result.") + + def test_signature_validity( Msg, Qx, Qy, R, S, expected ): + """Msg = message, Qx and Qy represent the base point on + elliptic curve c192, R and S are the signature, and + "expected" is True iff the signature is expected to be valid.""" + pubk = Public_key( generator_192, + ellipticcurve.Point( curve_192, Qx, Qy ) ) + got = pubk.verifies( digest_integer( Msg ), Signature( R, S ) ) + if got == expected: + print_("Signature tested as expected: got %s, expected %s." % \ + ( got, expected )) + else: + raise TestFailure("*** Signature test failed: got %s, expected %s." % \ + ( got, expected )) + + print_("NIST Curve P-192:") + + p192 = generator_192 + + # From X9.62: + + d = 651056770906015076056810763456358567190100156695615665659 + Q = d * p192 + if Q.x() != 0x62B12D60690CDCF330BABAB6E69763B471F994DD702D16A5: + raise TestFailure("*** p192 * d came out wrong.") + else: + print_("p192 * d came out right.") + + k = 6140507067065001063065065565667405560006161556565665656654 + R = k * p192 + if R.x() != 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD \ + or R.y() != 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835: + raise TestFailure("*** k * p192 came out wrong.") + else: + print_("k * p192 came out right.") + + u1 = 2563697409189434185194736134579731015366492496392189760599 + u2 = 6266643813348617967186477710235785849136406323338782220568 + temp = u1 * p192 + u2 * Q + if temp.x() != 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD \ + or temp.y() != 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835: + raise TestFailure("*** u1 * p192 + u2 * Q came out wrong.") + else: + print_("u1 * p192 + u2 * Q came out right.") + + e = 968236873715988614170569073515315707566766479517 + pubk = Public_key( generator_192, generator_192 * d ) + privk = Private_key( pubk, d ) + sig = privk.sign( e, k ) + r, s = sig.r, sig.s + if r != 3342403536405981729393488334694600415596881826869351677613 \ + or s != 5735822328888155254683894997897571951568553642892029982342: + raise TestFailure("*** r or s came out wrong.") + else: + print_("r and s came out right.") + + valid = pubk.verifies( e, sig ) + if valid: print_("Signature verified OK.") + else: raise TestFailure("*** Signature failed verification.") + + valid = pubk.verifies( e-1, sig ) + if not valid: print_("Forgery was correctly rejected.") + else: raise TestFailure("*** Forgery was erroneously accepted.") + + print_("Testing point validity, as per ECDSAVS.pdf B.2.2:") + + test_point_validity( \ + p192, \ + 0xcd6d0f029a023e9aaca429615b8f577abee685d8257cc83a, \ + 0x00019c410987680e9fb6c0b6ecc01d9a2647c8bae27721bacdfc, \ + False ) + + test_point_validity( + p192, \ + 0x00017f2fce203639e9eaf9fb50b81fc32776b30e3b02af16c73b, \ + 0x95da95c5e72dd48e229d4748d4eee658a9a54111b23b2adb, \ + False ) + + test_point_validity( + p192, \ + 0x4f77f8bc7fccbadd5760f4938746d5f253ee2168c1cf2792, \ + 0x000147156ff824d131629739817edb197717c41aab5c2a70f0f6, \ + False ) + + test_point_validity( + p192, \ + 0xc58d61f88d905293bcd4cd0080bcb1b7f811f2ffa41979f6, \ + 0x8804dc7a7c4c7f8b5d437f5156f3312ca7d6de8a0e11867f, \ + True ) + + test_point_validity( + p192, \ + 0xcdf56c1aa3d8afc53c521adf3ffb96734a6a630a4a5b5a70, \ + 0x97c1c44a5fb229007b5ec5d25f7413d170068ffd023caa4e, \ + True ) + + test_point_validity( + p192, \ + 0x89009c0dc361c81e99280c8e91df578df88cdf4b0cdedced, \ + 0x27be44a529b7513e727251f128b34262a0fd4d8ec82377b9, \ + True ) + + test_point_validity( + p192, \ + 0x6a223d00bd22c52833409a163e057e5b5da1def2a197dd15, \ + 0x7b482604199367f1f303f9ef627f922f97023e90eae08abf, \ + True ) + + test_point_validity( + p192, \ + 0x6dccbde75c0948c98dab32ea0bc59fe125cf0fb1a3798eda, \ + 0x0001171a3e0fa60cf3096f4e116b556198de430e1fbd330c8835, \ + False ) + + test_point_validity( + p192, \ + 0xd266b39e1f491fc4acbbbc7d098430931cfa66d55015af12, \ + 0x193782eb909e391a3148b7764e6b234aa94e48d30a16dbb2, \ + False ) + + test_point_validity( + p192, \ + 0x9d6ddbcd439baa0c6b80a654091680e462a7d1d3f1ffeb43, \ + 0x6ad8efc4d133ccf167c44eb4691c80abffb9f82b932b8caa, \ + False ) + + test_point_validity( + p192, \ + 0x146479d944e6bda87e5b35818aa666a4c998a71f4e95edbc, \ + 0xa86d6fe62bc8fbd88139693f842635f687f132255858e7f6, \ + False ) + + test_point_validity( + p192, \ + 0xe594d4a598046f3598243f50fd2c7bd7d380edb055802253, \ + 0x509014c0c4d6b536e3ca750ec09066af39b4c8616a53a923, \ + False ) + + print_("Trying signature-verification tests from ECDSAVS.pdf B.2.4:") + print_("P-192:") + Msg = 0x84ce72aa8699df436059f052ac51b6398d2511e49631bcb7e71f89c499b9ee425dfbc13a5f6d408471b054f2655617cbbaf7937b7c80cd8865cf02c8487d30d2b0fbd8b2c4e102e16d828374bbc47b93852f212d5043c3ea720f086178ff798cc4f63f787b9c2e419efa033e7644ea7936f54462dc21a6c4580725f7f0e7d158 + Qx = 0xd9dbfb332aa8e5ff091e8ce535857c37c73f6250ffb2e7ac + Qy = 0x282102e364feded3ad15ddf968f88d8321aa268dd483ebc4 + R = 0x64dca58a20787c488d11d6dd96313f1b766f2d8efe122916 + S = 0x1ecba28141e84ab4ecad92f56720e2cc83eb3d22dec72479 + test_signature_validity( Msg, Qx, Qy, R, S, True ) + + Msg = 0x94bb5bacd5f8ea765810024db87f4224ad71362a3c28284b2b9f39fab86db12e8beb94aae899768229be8fdb6c4f12f28912bb604703a79ccff769c1607f5a91450f30ba0460d359d9126cbd6296be6d9c4bb96c0ee74cbb44197c207f6db326ab6f5a659113a9034e54be7b041ced9dcf6458d7fb9cbfb2744d999f7dfd63f4 + Qx = 0x3e53ef8d3112af3285c0e74842090712cd324832d4277ae7 + Qy = 0xcc75f8952d30aec2cbb719fc6aa9934590b5d0ff5a83adb7 + R = 0x8285261607283ba18f335026130bab31840dcfd9c3e555af + S = 0x356d89e1b04541afc9704a45e9c535ce4a50929e33d7e06c + test_signature_validity( Msg, Qx, Qy, R, S, True ) + + Msg = 0xf6227a8eeb34afed1621dcc89a91d72ea212cb2f476839d9b4243c66877911b37b4ad6f4448792a7bbba76c63bdd63414b6facab7dc71c3396a73bd7ee14cdd41a659c61c99b779cecf07bc51ab391aa3252386242b9853ea7da67fd768d303f1b9b513d401565b6f1eb722dfdb96b519fe4f9bd5de67ae131e64b40e78c42dd + Qx = 0x16335dbe95f8e8254a4e04575d736befb258b8657f773cb7 + Qy = 0x421b13379c59bc9dce38a1099ca79bbd06d647c7f6242336 + R = 0x4141bd5d64ea36c5b0bd21ef28c02da216ed9d04522b1e91 + S = 0x159a6aa852bcc579e821b7bb0994c0861fb08280c38daa09 + test_signature_validity( Msg, Qx, Qy, R, S, False ) + + Msg = 0x16b5f93afd0d02246f662761ed8e0dd9504681ed02a253006eb36736b563097ba39f81c8e1bce7a16c1339e345efabbc6baa3efb0612948ae51103382a8ee8bc448e3ef71e9f6f7a9676694831d7f5dd0db5446f179bcb737d4a526367a447bfe2c857521c7f40b6d7d7e01a180d92431fb0bbd29c04a0c420a57b3ed26ccd8a + Qx = 0xfd14cdf1607f5efb7b1793037b15bdf4baa6f7c16341ab0b + Qy = 0x83fa0795cc6c4795b9016dac928fd6bac32f3229a96312c4 + R = 0x8dfdb832951e0167c5d762a473c0416c5c15bc1195667dc1 + S = 0x1720288a2dc13fa1ec78f763f8fe2ff7354a7e6fdde44520 + test_signature_validity( Msg, Qx, Qy, R, S, False ) + + Msg = 0x08a2024b61b79d260e3bb43ef15659aec89e5b560199bc82cf7c65c77d39192e03b9a895d766655105edd9188242b91fbde4167f7862d4ddd61e5d4ab55196683d4f13ceb90d87aea6e07eb50a874e33086c4a7cb0273a8e1c4408f4b846bceae1ebaac1b2b2ea851a9b09de322efe34cebe601653efd6ddc876ce8c2f2072fb + Qx = 0x674f941dc1a1f8b763c9334d726172d527b90ca324db8828 + Qy = 0x65adfa32e8b236cb33a3e84cf59bfb9417ae7e8ede57a7ff + R = 0x9508b9fdd7daf0d8126f9e2bc5a35e4c6d800b5b804d7796 + S = 0x36f2bf6b21b987c77b53bb801b3435a577e3d493744bfab0 + test_signature_validity( Msg, Qx, Qy, R, S, False ) + + Msg = 0x1843aba74b0789d4ac6b0b8923848023a644a7b70afa23b1191829bbe4397ce15b629bf21a8838298653ed0c19222b95fa4f7390d1b4c844d96e645537e0aae98afb5c0ac3bd0e4c37f8daaff25556c64e98c319c52687c904c4de7240a1cc55cd9756b7edaef184e6e23b385726e9ffcba8001b8f574987c1a3fedaaa83ca6d + Qx = 0x10ecca1aad7220b56a62008b35170bfd5e35885c4014a19f + Qy = 0x04eb61984c6c12ade3bc47f3c629ece7aa0a033b9948d686 + R = 0x82bfa4e82c0dfe9274169b86694e76ce993fd83b5c60f325 + S = 0xa97685676c59a65dbde002fe9d613431fb183e8006d05633 + test_signature_validity( Msg, Qx, Qy, R, S, False ) + + Msg = 0x5a478f4084ddd1a7fea038aa9732a822106385797d02311aeef4d0264f824f698df7a48cfb6b578cf3da416bc0799425bb491be5b5ecc37995b85b03420a98f2c4dc5c31a69a379e9e322fbe706bbcaf0f77175e05cbb4fa162e0da82010a278461e3e974d137bc746d1880d6eb02aa95216014b37480d84b87f717bb13f76e1 + Qx = 0x6636653cb5b894ca65c448277b29da3ad101c4c2300f7c04 + Qy = 0xfdf1cbb3fc3fd6a4f890b59e554544175fa77dbdbeb656c1 + R = 0xeac2ddecddfb79931a9c3d49c08de0645c783a24cb365e1c + S = 0x3549fee3cfa7e5f93bc47d92d8ba100e881a2a93c22f8d50 + test_signature_validity( Msg, Qx, Qy, R, S, False ) + + Msg = 0xc598774259a058fa65212ac57eaa4f52240e629ef4c310722088292d1d4af6c39b49ce06ba77e4247b20637174d0bd67c9723feb57b5ead232b47ea452d5d7a089f17c00b8b6767e434a5e16c231ba0efa718a340bf41d67ea2d295812ff1b9277daacb8bc27b50ea5e6443bcf95ef4e9f5468fe78485236313d53d1c68f6ba2 + Qx = 0xa82bd718d01d354001148cd5f69b9ebf38ff6f21898f8aaa + Qy = 0xe67ceede07fc2ebfafd62462a51e4b6c6b3d5b537b7caf3e + R = 0x4d292486c620c3de20856e57d3bb72fcde4a73ad26376955 + S = 0xa85289591a6081d5728825520e62ff1c64f94235c04c7f95 + test_signature_validity( Msg, Qx, Qy, R, S, False ) + + Msg = 0xca98ed9db081a07b7557f24ced6c7b9891269a95d2026747add9e9eb80638a961cf9c71a1b9f2c29744180bd4c3d3db60f2243c5c0b7cc8a8d40a3f9a7fc910250f2187136ee6413ffc67f1a25e1c4c204fa9635312252ac0e0481d89b6d53808f0c496ba87631803f6c572c1f61fa049737fdacce4adff757afed4f05beb658 + Qx = 0x7d3b016b57758b160c4fca73d48df07ae3b6b30225126c2f + Qy = 0x4af3790d9775742bde46f8da876711be1b65244b2b39e7ec + R = 0x95f778f5f656511a5ab49a5d69ddd0929563c29cbc3a9e62 + S = 0x75c87fc358c251b4c83d2dd979faad496b539f9f2ee7a289 + test_signature_validity( Msg, Qx, Qy, R, S, False ) + + Msg = 0x31dd9a54c8338bea06b87eca813d555ad1850fac9742ef0bbe40dad400e10288acc9c11ea7dac79eb16378ebea9490e09536099f1b993e2653cd50240014c90a9c987f64545abc6a536b9bd2435eb5e911fdfde2f13be96ea36ad38df4ae9ea387b29cced599af777338af2794820c9cce43b51d2112380a35802ab7e396c97a + Qx = 0x9362f28c4ef96453d8a2f849f21e881cd7566887da8beb4a + Qy = 0xe64d26d8d74c48a024ae85d982ee74cd16046f4ee5333905 + R = 0xf3923476a296c88287e8de914b0b324ad5a963319a4fe73b + S = 0xf0baeed7624ed00d15244d8ba2aede085517dbdec8ac65f5 + test_signature_validity( Msg, Qx, Qy, R, S, True ) + + Msg = 0xb2b94e4432267c92f9fdb9dc6040c95ffa477652761290d3c7de312283f6450d89cc4aabe748554dfb6056b2d8e99c7aeaad9cdddebdee9dbc099839562d9064e68e7bb5f3a6bba0749ca9a538181fc785553a4000785d73cc207922f63e8ce1112768cb1de7b673aed83a1e4a74592f1268d8e2a4e9e63d414b5d442bd0456d + Qx = 0xcc6fc032a846aaac25533eb033522824f94e670fa997ecef + Qy = 0xe25463ef77a029eccda8b294fd63dd694e38d223d30862f1 + R = 0x066b1d07f3a40e679b620eda7f550842a35c18b80c5ebe06 + S = 0xa0b0fb201e8f2df65e2c4508ef303bdc90d934016f16b2dc + test_signature_validity( Msg, Qx, Qy, R, S, False ) + + Msg = 0x4366fcadf10d30d086911de30143da6f579527036937007b337f7282460eae5678b15cccda853193ea5fc4bc0a6b9d7a31128f27e1214988592827520b214eed5052f7775b750b0c6b15f145453ba3fee24a085d65287e10509eb5d5f602c440341376b95c24e5c4727d4b859bfe1483d20538acdd92c7997fa9c614f0f839d7 + Qx = 0x955c908fe900a996f7e2089bee2f6376830f76a19135e753 + Qy = 0xba0c42a91d3847de4a592a46dc3fdaf45a7cc709b90de520 + R = 0x1f58ad77fc04c782815a1405b0925e72095d906cbf52a668 + S = 0xf2e93758b3af75edf784f05a6761c9b9a6043c66b845b599 + test_signature_validity( Msg, Qx, Qy, R, S, False ) + + Msg = 0x543f8af57d750e33aa8565e0cae92bfa7a1ff78833093421c2942cadf9986670a5ff3244c02a8225e790fbf30ea84c74720abf99cfd10d02d34377c3d3b41269bea763384f372bb786b5846f58932defa68023136cd571863b304886e95e52e7877f445b9364b3f06f3c28da12707673fecb4b8071de06b6e0a3c87da160cef3 + Qx = 0x31f7fa05576d78a949b24812d4383107a9a45bb5fccdd835 + Qy = 0x8dc0eb65994a90f02b5e19bd18b32d61150746c09107e76b + R = 0xbe26d59e4e883dde7c286614a767b31e49ad88789d3a78ff + S = 0x8762ca831c1ce42df77893c9b03119428e7a9b819b619068 + test_signature_validity( Msg, Qx, Qy, R, S, False ) + + Msg = 0xd2e8454143ce281e609a9d748014dcebb9d0bc53adb02443a6aac2ffe6cb009f387c346ecb051791404f79e902ee333ad65e5c8cb38dc0d1d39a8dc90add5023572720e5b94b190d43dd0d7873397504c0c7aef2727e628eb6a74411f2e400c65670716cb4a815dc91cbbfeb7cfe8c929e93184c938af2c078584da045e8f8d1 + Qx = 0x66aa8edbbdb5cf8e28ceb51b5bda891cae2df84819fe25c0 + Qy = 0x0c6bc2f69030a7ce58d4a00e3b3349844784a13b8936f8da + R = 0xa4661e69b1734f4a71b788410a464b71e7ffe42334484f23 + S = 0x738421cf5e049159d69c57a915143e226cac8355e149afe9 + test_signature_validity( Msg, Qx, Qy, R, S, False ) + + Msg = 0x6660717144040f3e2f95a4e25b08a7079c702a8b29babad5a19a87654bc5c5afa261512a11b998a4fb36b5d8fe8bd942792ff0324b108120de86d63f65855e5461184fc96a0a8ffd2ce6d5dfb0230cbbdd98f8543e361b3205f5da3d500fdc8bac6db377d75ebef3cb8f4d1ff738071ad0938917889250b41dd1d98896ca06fb + Qx = 0xbcfacf45139b6f5f690a4c35a5fffa498794136a2353fc77 + Qy = 0x6f4a6c906316a6afc6d98fe1f0399d056f128fe0270b0f22 + R = 0x9db679a3dafe48f7ccad122933acfe9da0970b71c94c21c1 + S = 0x984c2db99827576c0a41a5da41e07d8cc768bc82f18c9da9 + test_signature_validity( Msg, Qx, Qy, R, S, False ) + + + + print_("Testing the example code:") + + # Building a public/private key pair from the NIST Curve P-192: + + g = generator_192 + n = g.order() + + # (random.SystemRandom is supposed to provide + # crypto-quality random numbers, but as Debian recently + # illustrated, a systems programmer can accidentally + # demolish this security, so in serious applications + # further precautions are appropriate.) + + randrange = random.SystemRandom().randrange + + secret = randrange( 1, n ) + pubkey = Public_key( g, g * secret ) + privkey = Private_key( pubkey, secret ) + + # Signing a hash value: + + hash = randrange( 1, n ) + signature = privkey.sign( hash, randrange( 1, n ) ) + + # Verifying a signature for a hash value: + + if pubkey.verifies( hash, signature ): + print_("Demo verification succeeded.") + else: + raise TestFailure("*** Demo verification failed.") + + if pubkey.verifies( hash-1, signature ): + raise TestFailure( "**** Demo verification failed to reject tampered hash.") + else: + print_("Demo verification correctly rejected tampered hash.") + +if __name__ == "__main__": + __main__() diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/ellipticcurve.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/ellipticcurve.py new file mode 100644 index 0000000..390bb35 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/ellipticcurve.py @@ -0,0 +1,293 @@ +#! /usr/bin/env python +# +# Implementation of elliptic curves, for cryptographic applications. +# +# This module doesn't provide any way to choose a random elliptic +# curve, nor to verify that an elliptic curve was chosen randomly, +# because one can simply use NIST's standard curves. +# +# Notes from X9.62-1998 (draft): +# Nomenclature: +# - Q is a public key. +# The "Elliptic Curve Domain Parameters" include: +# - q is the "field size", which in our case equals p. +# - p is a big prime. +# - G is a point of prime order (5.1.1.1). +# - n is the order of G (5.1.1.1). +# Public-key validation (5.2.2): +# - Verify that Q is not the point at infinity. +# - Verify that X_Q and Y_Q are in [0,p-1]. +# - Verify that Q is on the curve. +# - Verify that nQ is the point at infinity. +# Signature generation (5.3): +# - Pick random k from [1,n-1]. +# Signature checking (5.4.2): +# - Verify that r and s are in [1,n-1]. +# +# Version of 2008.11.25. +# +# Revision history: +# 2005.12.31 - Initial version. +# 2008.11.25 - Change CurveFp.is_on to contains_point. +# +# Written in 2005 by Peter Pearson and placed in the public domain. + +from __future__ import division + +from .six import print_ +from . import numbertheory + +class CurveFp( object ): + """Elliptic Curve over the field of integers modulo a prime.""" + def __init__( self, p, a, b ): + """The curve of points satisfying y^2 = x^3 + a*x + b (mod p).""" + self.__p = p + self.__a = a + self.__b = b + + def p( self ): + return self.__p + + def a( self ): + return self.__a + + def b( self ): + return self.__b + + def contains_point( self, x, y ): + """Is the point (x,y) on this curve?""" + return ( y * y - ( x * x * x + self.__a * x + self.__b ) ) % self.__p == 0 + + + +class Point( object ): + """A point on an elliptic curve. Altering x and y is forbidding, + but they can be read by the x() and y() methods.""" + def __init__( self, curve, x, y, order = None ): + """curve, x, y, order; order (optional) is the order of this point.""" + self.__curve = curve + self.__x = x + self.__y = y + self.__order = order + # self.curve is allowed to be None only for INFINITY: + if self.__curve: assert self.__curve.contains_point( x, y ) + if order: assert self * order == INFINITY + + def __eq__( self, other ): + """Return True if the points are identical, False otherwise.""" + if self.__curve == other.__curve \ + and self.__x == other.__x \ + and self.__y == other.__y: + return True + else: + return False + + def __add__( self, other ): + """Add one point to another point.""" + + # X9.62 B.3: + + if other == INFINITY: return self + if self == INFINITY: return other + assert self.__curve == other.__curve + if self.__x == other.__x: + if ( self.__y + other.__y ) % self.__curve.p() == 0: + return INFINITY + else: + return self.double() + + p = self.__curve.p() + + l = ( ( other.__y - self.__y ) * \ + numbertheory.inverse_mod( other.__x - self.__x, p ) ) % p + + x3 = ( l * l - self.__x - other.__x ) % p + y3 = ( l * ( self.__x - x3 ) - self.__y ) % p + + return Point( self.__curve, x3, y3 ) + + def __mul__( self, other ): + """Multiply a point by an integer.""" + + def leftmost_bit( x ): + assert x > 0 + result = 1 + while result <= x: result = 2 * result + return result // 2 + + e = other + if self.__order: e = e % self.__order + if e == 0: return INFINITY + if self == INFINITY: return INFINITY + assert e > 0 + + # From X9.62 D.3.2: + + e3 = 3 * e + negative_self = Point( self.__curve, self.__x, -self.__y, self.__order ) + i = leftmost_bit( e3 ) // 2 + result = self + # print_("Multiplying %s by %d (e3 = %d):" % ( self, other, e3 )) + while i > 1: + result = result.double() + if ( e3 & i ) != 0 and ( e & i ) == 0: result = result + self + if ( e3 & i ) == 0 and ( e & i ) != 0: result = result + negative_self + # print_(". . . i = %d, result = %s" % ( i, result )) + i = i // 2 + + return result + + def __rmul__( self, other ): + """Multiply a point by an integer.""" + + return self * other + + def __str__( self ): + if self == INFINITY: return "infinity" + return "(%d,%d)" % ( self.__x, self.__y ) + + def double( self ): + """Return a new point that is twice the old.""" + + if self == INFINITY: + return INFINITY + + # X9.62 B.3: + + p = self.__curve.p() + a = self.__curve.a() + + l = ( ( 3 * self.__x * self.__x + a ) * \ + numbertheory.inverse_mod( 2 * self.__y, p ) ) % p + + x3 = ( l * l - 2 * self.__x ) % p + y3 = ( l * ( self.__x - x3 ) - self.__y ) % p + + return Point( self.__curve, x3, y3 ) + + def x( self ): + return self.__x + + def y( self ): + return self.__y + + def curve( self ): + return self.__curve + + def order( self ): + return self.__order + + +# This one point is the Point At Infinity for all purposes: +INFINITY = Point( None, None, None ) + +def __main__(): + + class FailedTest(Exception): pass + def test_add( c, x1, y1, x2, y2, x3, y3 ): + """We expect that on curve c, (x1,y1) + (x2, y2 ) = (x3, y3).""" + p1 = Point( c, x1, y1 ) + p2 = Point( c, x2, y2 ) + p3 = p1 + p2 + print_("%s + %s = %s" % ( p1, p2, p3 ), end=' ') + if p3.x() != x3 or p3.y() != y3: + raise FailedTest("Failure: should give (%d,%d)." % ( x3, y3 )) + else: + print_(" Good.") + + def test_double( c, x1, y1, x3, y3 ): + """We expect that on curve c, 2*(x1,y1) = (x3, y3).""" + p1 = Point( c, x1, y1 ) + p3 = p1.double() + print_("%s doubled = %s" % ( p1, p3 ), end=' ') + if p3.x() != x3 or p3.y() != y3: + raise FailedTest("Failure: should give (%d,%d)." % ( x3, y3 )) + else: + print_(" Good.") + + def test_double_infinity( c ): + """We expect that on curve c, 2*INFINITY = INFINITY.""" + p1 = INFINITY + p3 = p1.double() + print_("%s doubled = %s" % ( p1, p3 ), end=' ') + if p3.x() != INFINITY.x() or p3.y() != INFINITY.y(): + raise FailedTest("Failure: should give (%d,%d)." % ( INFINITY.x(), INFINITY.y() )) + else: + print_(" Good.") + + def test_multiply( c, x1, y1, m, x3, y3 ): + """We expect that on curve c, m*(x1,y1) = (x3,y3).""" + p1 = Point( c, x1, y1 ) + p3 = p1 * m + print_("%s * %d = %s" % ( p1, m, p3 ), end=' ') + if p3.x() != x3 or p3.y() != y3: + raise FailedTest("Failure: should give (%d,%d)." % ( x3, y3 )) + else: + print_(" Good.") + + + # A few tests from X9.62 B.3: + + c = CurveFp( 23, 1, 1 ) + test_add( c, 3, 10, 9, 7, 17, 20 ) + test_double( c, 3, 10, 7, 12 ) + test_add( c, 3, 10, 3, 10, 7, 12 ) # (Should just invoke double.) + test_multiply( c, 3, 10, 2, 7, 12 ) + + test_double_infinity(c) + + # From X9.62 I.1 (p. 96): + + g = Point( c, 13, 7, 7 ) + + check = INFINITY + for i in range( 7 + 1 ): + p = ( i % 7 ) * g + print_("%s * %d = %s, expected %s . . ." % ( g, i, p, check ), end=' ') + if p == check: + print_(" Good.") + else: + raise FailedTest("Bad.") + check = check + g + + # NIST Curve P-192: + p = 6277101735386680763835789423207666416083908700390324961279 + r = 6277101735386680763835789423176059013767194773182842284081 + #s = 0x3045ae6fc8422f64ed579528d38120eae12196d5L + c = 0x3099d2bbbfcb2538542dcd5fb078b6ef5f3d6fe2c745de65 + b = 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1 + Gx = 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012 + Gy = 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811 + + c192 = CurveFp( p, -3, b ) + p192 = Point( c192, Gx, Gy, r ) + + # Checking against some sample computations presented + # in X9.62: + + d = 651056770906015076056810763456358567190100156695615665659 + Q = d * p192 + if Q.x() != 0x62B12D60690CDCF330BABAB6E69763B471F994DD702D16A5: + raise FailedTest("p192 * d came out wrong.") + else: + print_("p192 * d came out right.") + + k = 6140507067065001063065065565667405560006161556565665656654 + R = k * p192 + if R.x() != 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD \ + or R.y() != 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835: + raise FailedTest("k * p192 came out wrong.") + else: + print_("k * p192 came out right.") + + u1 = 2563697409189434185194736134579731015366492496392189760599 + u2 = 6266643813348617967186477710235785849136406323338782220568 + temp = u1 * p192 + u2 * Q + if temp.x() != 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD \ + or temp.y() != 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835: + raise FailedTest("u1 * p192 + u2 * Q came out wrong.") + else: + print_("u1 * p192 + u2 * Q came out right.") + +if __name__ == "__main__": + __main__() diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/keys.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/keys.py new file mode 100644 index 0000000..75042f6 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/keys.py @@ -0,0 +1,282 @@ +import binascii + +from . import ecdsa +from . import der +from . import rfc6979 +from .curves import NIST192p, find_curve +from .util import string_to_number, number_to_string, randrange +from .util import sigencode_string, sigdecode_string +from .util import oid_ecPublicKey, encoded_oid_ecPublicKey +from .six import PY3, b +from hashlib import sha1 + +class BadSignatureError(Exception): + pass +class BadDigestError(Exception): + pass + +class VerifyingKey: + def __init__(self, _error__please_use_generate=None): + if not _error__please_use_generate: + raise TypeError("Please use SigningKey.generate() to construct me") + + @classmethod + def from_public_point(klass, point, curve=NIST192p, hashfunc=sha1): + self = klass(_error__please_use_generate=True) + self.curve = curve + self.default_hashfunc = hashfunc + self.pubkey = ecdsa.Public_key(curve.generator, point) + self.pubkey.order = curve.order + return self + + @classmethod + def from_string(klass, string, curve=NIST192p, hashfunc=sha1, + validate_point=True): + order = curve.order + assert len(string) == curve.verifying_key_length, \ + (len(string), curve.verifying_key_length) + xs = string[:curve.baselen] + ys = string[curve.baselen:] + assert len(xs) == curve.baselen, (len(xs), curve.baselen) + assert len(ys) == curve.baselen, (len(ys), curve.baselen) + x = string_to_number(xs) + y = string_to_number(ys) + if validate_point: + assert ecdsa.point_is_valid(curve.generator, x, y) + from . import ellipticcurve + point = ellipticcurve.Point(curve.curve, x, y, order) + return klass.from_public_point(point, curve, hashfunc) + + @classmethod + def from_pem(klass, string): + return klass.from_der(der.unpem(string)) + + @classmethod + def from_der(klass, string): + # [[oid_ecPublicKey,oid_curve], point_str_bitstring] + s1,empty = der.remove_sequence(string) + if empty != b(""): + raise der.UnexpectedDER("trailing junk after DER pubkey: %s" % + binascii.hexlify(empty)) + s2,point_str_bitstring = der.remove_sequence(s1) + # s2 = oid_ecPublicKey,oid_curve + oid_pk, rest = der.remove_object(s2) + oid_curve, empty = der.remove_object(rest) + if empty != b(""): + raise der.UnexpectedDER("trailing junk after DER pubkey objects: %s" % + binascii.hexlify(empty)) + assert oid_pk == oid_ecPublicKey, (oid_pk, oid_ecPublicKey) + curve = find_curve(oid_curve) + point_str, empty = der.remove_bitstring(point_str_bitstring) + if empty != b(""): + raise der.UnexpectedDER("trailing junk after pubkey pointstring: %s" % + binascii.hexlify(empty)) + assert point_str.startswith(b("\x00\x04")) + return klass.from_string(point_str[2:], curve) + + def to_string(self): + # VerifyingKey.from_string(vk.to_string()) == vk as long as the + # curves are the same: the curve itself is not included in the + # serialized form + order = self.pubkey.order + x_str = number_to_string(self.pubkey.point.x(), order) + y_str = number_to_string(self.pubkey.point.y(), order) + return x_str + y_str + + def to_pem(self): + return der.topem(self.to_der(), "PUBLIC KEY") + + def to_der(self): + order = self.pubkey.order + x_str = number_to_string(self.pubkey.point.x(), order) + y_str = number_to_string(self.pubkey.point.y(), order) + point_str = b("\x00\x04") + x_str + y_str + return der.encode_sequence(der.encode_sequence(encoded_oid_ecPublicKey, + self.curve.encoded_oid), + der.encode_bitstring(point_str)) + + def verify(self, signature, data, hashfunc=None, sigdecode=sigdecode_string): + hashfunc = hashfunc or self.default_hashfunc + digest = hashfunc(data).digest() + return self.verify_digest(signature, digest, sigdecode) + + def verify_digest(self, signature, digest, sigdecode=sigdecode_string): + if len(digest) > self.curve.baselen: + raise BadDigestError("this curve (%s) is too short " + "for your digest (%d)" % (self.curve.name, + 8*len(digest))) + number = string_to_number(digest) + r, s = sigdecode(signature, self.pubkey.order) + sig = ecdsa.Signature(r, s) + if self.pubkey.verifies(number, sig): + return True + raise BadSignatureError + +class SigningKey: + def __init__(self, _error__please_use_generate=None): + if not _error__please_use_generate: + raise TypeError("Please use SigningKey.generate() to construct me") + + @classmethod + def generate(klass, curve=NIST192p, entropy=None, hashfunc=sha1): + secexp = randrange(curve.order, entropy) + return klass.from_secret_exponent(secexp, curve, hashfunc) + + # to create a signing key from a short (arbitrary-length) seed, convert + # that seed into an integer with something like + # secexp=util.randrange_from_seed__X(seed, curve.order), and then pass + # that integer into SigningKey.from_secret_exponent(secexp, curve) + + @classmethod + def from_secret_exponent(klass, secexp, curve=NIST192p, hashfunc=sha1): + self = klass(_error__please_use_generate=True) + self.curve = curve + self.default_hashfunc = hashfunc + self.baselen = curve.baselen + n = curve.order + assert 1 <= secexp < n + pubkey_point = curve.generator*secexp + pubkey = ecdsa.Public_key(curve.generator, pubkey_point) + pubkey.order = n + self.verifying_key = VerifyingKey.from_public_point(pubkey_point, curve, + hashfunc) + self.privkey = ecdsa.Private_key(pubkey, secexp) + self.privkey.order = n + return self + + @classmethod + def from_string(klass, string, curve=NIST192p, hashfunc=sha1): + assert len(string) == curve.baselen, (len(string), curve.baselen) + secexp = string_to_number(string) + return klass.from_secret_exponent(secexp, curve, hashfunc) + + @classmethod + def from_pem(klass, string, hashfunc=sha1): + # the privkey pem file has two sections: "EC PARAMETERS" and "EC + # PRIVATE KEY". The first is redundant. + if PY3 and isinstance(string, str): + string = string.encode() + privkey_pem = string[string.index(b("-----BEGIN EC PRIVATE KEY-----")):] + return klass.from_der(der.unpem(privkey_pem), hashfunc) + @classmethod + def from_der(klass, string, hashfunc=sha1): + # SEQ([int(1), octetstring(privkey),cont[0], oid(secp224r1), + # cont[1],bitstring]) + s, empty = der.remove_sequence(string) + if empty != b(""): + raise der.UnexpectedDER("trailing junk after DER privkey: %s" % + binascii.hexlify(empty)) + one, s = der.remove_integer(s) + if one != 1: + raise der.UnexpectedDER("expected '1' at start of DER privkey," + " got %d" % one) + privkey_str, s = der.remove_octet_string(s) + tag, curve_oid_str, s = der.remove_constructed(s) + if tag != 0: + raise der.UnexpectedDER("expected tag 0 in DER privkey," + " got %d" % tag) + curve_oid, empty = der.remove_object(curve_oid_str) + if empty != b(""): + raise der.UnexpectedDER("trailing junk after DER privkey " + "curve_oid: %s" % binascii.hexlify(empty)) + curve = find_curve(curve_oid) + + # we don't actually care about the following fields + # + #tag, pubkey_bitstring, s = der.remove_constructed(s) + #if tag != 1: + # raise der.UnexpectedDER("expected tag 1 in DER privkey, got %d" + # % tag) + #pubkey_str = der.remove_bitstring(pubkey_bitstring) + #if empty != "": + # raise der.UnexpectedDER("trailing junk after DER privkey " + # "pubkeystr: %s" % binascii.hexlify(empty)) + + # our from_string method likes fixed-length privkey strings + if len(privkey_str) < curve.baselen: + privkey_str = b("\x00")*(curve.baselen-len(privkey_str)) + privkey_str + return klass.from_string(privkey_str, curve, hashfunc) + + def to_string(self): + secexp = self.privkey.secret_multiplier + s = number_to_string(secexp, self.privkey.order) + return s + + def to_pem(self): + # TODO: "BEGIN ECPARAMETERS" + return der.topem(self.to_der(), "EC PRIVATE KEY") + + def to_der(self): + # SEQ([int(1), octetstring(privkey),cont[0], oid(secp224r1), + # cont[1],bitstring]) + encoded_vk = b("\x00\x04") + self.get_verifying_key().to_string() + return der.encode_sequence(der.encode_integer(1), + der.encode_octet_string(self.to_string()), + der.encode_constructed(0, self.curve.encoded_oid), + der.encode_constructed(1, der.encode_bitstring(encoded_vk)), + ) + + def get_verifying_key(self): + return self.verifying_key + + def sign_deterministic(self, data, hashfunc=None, sigencode=sigencode_string): + hashfunc = hashfunc or self.default_hashfunc + digest = hashfunc(data).digest() + + return self.sign_digest_deterministic(digest, hashfunc=hashfunc, sigencode=sigencode) + + def sign_digest_deterministic(self, digest, hashfunc=None, sigencode=sigencode_string): + """ + Calculates 'k' from data itself, removing the need for strong + random generator and producing deterministic (reproducible) signatures. + See RFC 6979 for more details. + """ + secexp = self.privkey.secret_multiplier + k = rfc6979.generate_k(self.curve.generator, secexp, hashfunc, digest) + + return self.sign_digest(digest, sigencode=sigencode, k=k) + + def sign(self, data, entropy=None, hashfunc=None, sigencode=sigencode_string, k=None): + """ + hashfunc= should behave like hashlib.sha1 . The output length of the + hash (in bytes) must not be longer than the length of the curve order + (rounded up to the nearest byte), so using SHA256 with nist256p is + ok, but SHA256 with nist192p is not. (In the 2**-96ish unlikely event + of a hash output larger than the curve order, the hash will + effectively be wrapped mod n). + + Use hashfunc=hashlib.sha1 to match openssl's -ecdsa-with-SHA1 mode, + or hashfunc=hashlib.sha256 for openssl-1.0.0's -ecdsa-with-SHA256. + """ + + hashfunc = hashfunc or self.default_hashfunc + h = hashfunc(data).digest() + return self.sign_digest(h, entropy, sigencode, k) + + def sign_digest(self, digest, entropy=None, sigencode=sigencode_string, k=None): + if len(digest) > self.curve.baselen: + raise BadDigestError("this curve (%s) is too short " + "for your digest (%d)" % (self.curve.name, + 8*len(digest))) + number = string_to_number(digest) + r, s = self.sign_number(number, entropy, k) + return sigencode(r, s, self.privkey.order) + + def sign_number(self, number, entropy=None, k=None): + # returns a pair of numbers + order = self.privkey.order + # privkey.sign() may raise RuntimeError in the amazingly unlikely + # (2**-192) event that r=0 or s=0, because that would leak the key. + # We could re-try with a different 'k', but we couldn't test that + # code, so I choose to allow the signature to fail instead. + + # If k is set, it is used directly. In other cases + # it is generated using entropy function + if k is not None: + _k = k + else: + _k = randrange(order, entropy) + + assert 1 <= _k < order + sig = self.privkey.sign(number, _k) + return sig.r, sig.s diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/numbertheory.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/numbertheory.py new file mode 100644 index 0000000..a06fd80 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/numbertheory.py @@ -0,0 +1,614 @@ +#! /usr/bin/env python +# +# Provide some simple capabilities from number theory. +# +# Version of 2008.11.14. +# +# Written in 2005 and 2006 by Peter Pearson and placed in the public domain. +# Revision history: +# 2008.11.14: Use pow( base, exponent, modulus ) for modular_exp. +# Make gcd and lcm accept arbitrarly many arguments. + +from __future__ import division + +from .six import print_, integer_types +from .six.moves import reduce + +import math +import types + + +class Error( Exception ): + """Base class for exceptions in this module.""" + pass + +class SquareRootError( Error ): + pass + +class NegativeExponentError( Error ): + pass + + +def modular_exp( base, exponent, modulus ): + "Raise base to exponent, reducing by modulus" + if exponent < 0: + raise NegativeExponentError( "Negative exponents (%d) not allowed" \ + % exponent ) + return pow( base, exponent, modulus ) +# result = 1L +# x = exponent +# b = base + 0L +# while x > 0: +# if x % 2 > 0: result = (result * b) % modulus +# x = x // 2 +# b = ( b * b ) % modulus +# return result + + +def polynomial_reduce_mod( poly, polymod, p ): + """Reduce poly by polymod, integer arithmetic modulo p. + + Polynomials are represented as lists of coefficients + of increasing powers of x.""" + + # This module has been tested only by extensive use + # in calculating modular square roots. + + # Just to make this easy, require a monic polynomial: + assert polymod[-1] == 1 + + assert len( polymod ) > 1 + + while len( poly ) >= len( polymod ): + if poly[-1] != 0: + for i in range( 2, len( polymod ) + 1 ): + poly[-i] = ( poly[-i] - poly[-1] * polymod[-i] ) % p + poly = poly[0:-1] + + return poly + + + +def polynomial_multiply_mod( m1, m2, polymod, p ): + """Polynomial multiplication modulo a polynomial over ints mod p. + + Polynomials are represented as lists of coefficients + of increasing powers of x.""" + + # This is just a seat-of-the-pants implementation. + + # This module has been tested only by extensive use + # in calculating modular square roots. + + # Initialize the product to zero: + + prod = ( len( m1 ) + len( m2 ) - 1 ) * [0] + + # Add together all the cross-terms: + + for i in range( len( m1 ) ): + for j in range( len( m2 ) ): + prod[i+j] = ( prod[i+j] + m1[i] * m2[j] ) % p + + return polynomial_reduce_mod( prod, polymod, p ) + + +def polynomial_exp_mod( base, exponent, polymod, p ): + """Polynomial exponentiation modulo a polynomial over ints mod p. + + Polynomials are represented as lists of coefficients + of increasing powers of x.""" + + # Based on the Handbook of Applied Cryptography, algorithm 2.227. + + # This module has been tested only by extensive use + # in calculating modular square roots. + + assert exponent < p + + if exponent == 0: return [ 1 ] + + G = base + k = exponent + if k%2 == 1: s = G + else: s = [ 1 ] + + while k > 1: + k = k // 2 + G = polynomial_multiply_mod( G, G, polymod, p ) + if k%2 == 1: s = polynomial_multiply_mod( G, s, polymod, p ) + + return s + + + +def jacobi( a, n ): + """Jacobi symbol""" + + # Based on the Handbook of Applied Cryptography (HAC), algorithm 2.149. + + # This function has been tested by comparison with a small + # table printed in HAC, and by extensive use in calculating + # modular square roots. + + assert n >= 3 + assert n%2 == 1 + a = a % n + if a == 0: return 0 + if a == 1: return 1 + a1, e = a, 0 + while a1%2 == 0: + a1, e = a1//2, e+1 + if e%2 == 0 or n%8 == 1 or n%8 == 7: s = 1 + else: s = -1 + if a1 == 1: return s + if n%4 == 3 and a1%4 == 3: s = -s + return s * jacobi( n % a1, a1 ) + + + +def square_root_mod_prime( a, p ): + """Modular square root of a, mod p, p prime.""" + + # Based on the Handbook of Applied Cryptography, algorithms 3.34 to 3.39. + + # This module has been tested for all values in [0,p-1] for + # every prime p from 3 to 1229. + + assert 0 <= a < p + assert 1 < p + + if a == 0: return 0 + if p == 2: return a + + jac = jacobi( a, p ) + if jac == -1: raise SquareRootError( "%d has no square root modulo %d" \ + % ( a, p ) ) + + if p % 4 == 3: return modular_exp( a, (p+1)//4, p ) + + if p % 8 == 5: + d = modular_exp( a, (p-1)//4, p ) + if d == 1: return modular_exp( a, (p+3)//8, p ) + if d == p-1: return ( 2 * a * modular_exp( 4*a, (p-5)//8, p ) ) % p + raise RuntimeError("Shouldn't get here.") + + for b in range( 2, p ): + if jacobi( b*b-4*a, p ) == -1: + f = ( a, -b, 1 ) + ff = polynomial_exp_mod( ( 0, 1 ), (p+1)//2, f, p ) + assert ff[1] == 0 + return ff[0] + raise RuntimeError("No b found.") + + + +def inverse_mod( a, m ): + """Inverse of a mod m.""" + + if a < 0 or m <= a: a = a % m + + # From Ferguson and Schneier, roughly: + + c, d = a, m + uc, vc, ud, vd = 1, 0, 0, 1 + while c != 0: + q, c, d = divmod( d, c ) + ( c, ) + uc, vc, ud, vd = ud - q*uc, vd - q*vc, uc, vc + + # At this point, d is the GCD, and ud*a+vd*m = d. + # If d == 1, this means that ud is a inverse. + + assert d == 1 + if ud > 0: return ud + else: return ud + m + + +def gcd2(a, b): + """Greatest common divisor using Euclid's algorithm.""" + while a: + a, b = b%a, a + return b + + +def gcd( *a ): + """Greatest common divisor. + + Usage: gcd( [ 2, 4, 6 ] ) + or: gcd( 2, 4, 6 ) + """ + + if len( a ) > 1: return reduce( gcd2, a ) + if hasattr( a[0], "__iter__" ): return reduce( gcd2, a[0] ) + return a[0] + + +def lcm2(a,b): + """Least common multiple of two integers.""" + + return (a*b)//gcd(a,b) + + +def lcm( *a ): + """Least common multiple. + + Usage: lcm( [ 3, 4, 5 ] ) + or: lcm( 3, 4, 5 ) + """ + + if len( a ) > 1: return reduce( lcm2, a ) + if hasattr( a[0], "__iter__" ): return reduce( lcm2, a[0] ) + return a[0] + + + +def factorization( n ): + """Decompose n into a list of (prime,exponent) pairs.""" + + assert isinstance( n, integer_types ) + + if n < 2: return [] + + result = [] + d = 2 + + # Test the small primes: + + for d in smallprimes: + if d > n: break + q, r = divmod( n, d ) + if r == 0: + count = 1 + while d <= n: + n = q + q, r = divmod( n, d ) + if r != 0: break + count = count + 1 + result.append( ( d, count ) ) + + # If n is still greater than the last of our small primes, + # it may require further work: + + if n > smallprimes[-1]: + if is_prime( n ): # If what's left is prime, it's easy: + result.append( ( n, 1 ) ) + else: # Ugh. Search stupidly for a divisor: + d = smallprimes[-1] + while 1: + d = d + 2 # Try the next divisor. + q, r = divmod( n, d ) + if q < d: break # n < d*d means we're done, n = 1 or prime. + if r == 0: # d divides n. How many times? + count = 1 + n = q + while d <= n: # As long as d might still divide n, + q, r = divmod( n, d ) # see if it does. + if r != 0: break + n = q # It does. Reduce n, increase count. + count = count + 1 + result.append( ( d, count ) ) + if n > 1: result.append( ( n, 1 ) ) + + return result + + + +def phi( n ): + """Return the Euler totient function of n.""" + + assert isinstance( n, integer_types ) + + if n < 3: return 1 + + result = 1 + ff = factorization( n ) + for f in ff: + e = f[1] + if e > 1: + result = result * f[0] ** (e-1) * ( f[0] - 1 ) + else: + result = result * ( f[0] - 1 ) + return result + + +def carmichael( n ): + """Return Carmichael function of n. + + Carmichael(n) is the smallest integer x such that + m**x = 1 mod n for all m relatively prime to n. + """ + + return carmichael_of_factorized( factorization( n ) ) + + +def carmichael_of_factorized( f_list ): + """Return the Carmichael function of a number that is + represented as a list of (prime,exponent) pairs. + """ + + if len( f_list ) < 1: return 1 + + result = carmichael_of_ppower( f_list[0] ) + for i in range( 1, len( f_list ) ): + result = lcm( result, carmichael_of_ppower( f_list[i] ) ) + + return result + +def carmichael_of_ppower( pp ): + """Carmichael function of the given power of the given prime. + """ + + p, a = pp + if p == 2 and a > 2: return 2**(a-2) + else: return (p-1) * p**(a-1) + + + +def order_mod( x, m ): + """Return the order of x in the multiplicative group mod m. + """ + + # Warning: this implementation is not very clever, and will + # take a long time if m is very large. + + if m <= 1: return 0 + + assert gcd( x, m ) == 1 + + z = x + result = 1 + while z != 1: + z = ( z * x ) % m + result = result + 1 + return result + + +def largest_factor_relatively_prime( a, b ): + """Return the largest factor of a relatively prime to b. + """ + + while 1: + d = gcd( a, b ) + if d <= 1: break + b = d + while 1: + q, r = divmod( a, d ) + if r > 0: + break + a = q + return a + + +def kinda_order_mod( x, m ): + """Return the order of x in the multiplicative group mod m', + where m' is the largest factor of m relatively prime to x. + """ + + return order_mod( x, largest_factor_relatively_prime( m, x ) ) + + +def is_prime( n ): + """Return True if x is prime, False otherwise. + + We use the Miller-Rabin test, as given in Menezes et al. p. 138. + This test is not exact: there are composite values n for which + it returns True. + + In testing the odd numbers from 10000001 to 19999999, + about 66 composites got past the first test, + 5 got past the second test, and none got past the third. + Since factors of 2, 3, 5, 7, and 11 were detected during + preliminary screening, the number of numbers tested by + Miller-Rabin was (19999999 - 10000001)*(2/3)*(4/5)*(6/7) + = 4.57 million. + """ + + # (This is used to study the risk of false positives:) + global miller_rabin_test_count + + miller_rabin_test_count = 0 + + if n <= smallprimes[-1]: + if n in smallprimes: return True + else: return False + + if gcd( n, 2*3*5*7*11 ) != 1: return False + + # Choose a number of iterations sufficient to reduce the + # probability of accepting a composite below 2**-80 + # (from Menezes et al. Table 4.4): + + t = 40 + n_bits = 1 + int( math.log( n, 2 ) ) + for k, tt in ( ( 100, 27 ), + ( 150, 18 ), + ( 200, 15 ), + ( 250, 12 ), + ( 300, 9 ), + ( 350, 8 ), + ( 400, 7 ), + ( 450, 6 ), + ( 550, 5 ), + ( 650, 4 ), + ( 850, 3 ), + ( 1300, 2 ), + ): + if n_bits < k: break + t = tt + + # Run the test t times: + + s = 0 + r = n - 1 + while ( r % 2 ) == 0: + s = s + 1 + r = r // 2 + for i in range( t ): + a = smallprimes[ i ] + y = modular_exp( a, r, n ) + if y != 1 and y != n-1: + j = 1 + while j <= s - 1 and y != n - 1: + y = modular_exp( y, 2, n ) + if y == 1: + miller_rabin_test_count = i + 1 + return False + j = j + 1 + if y != n-1: + miller_rabin_test_count = i + 1 + return False + return True + + +def next_prime( starting_value ): + "Return the smallest prime larger than the starting value." + + if starting_value < 2: return 2 + result = ( starting_value + 1 ) | 1 + while not is_prime( result ): result = result + 2 + return result + + +smallprimes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, + 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, + 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, + 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, + 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, + 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, + 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, + 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, + 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, + 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, + 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, + 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, + 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, + 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, + 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, + 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, + 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, + 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, + 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, + 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229] + +miller_rabin_test_count = 0 + +def __main__(): + + # Making sure locally defined exceptions work: + # p = modular_exp( 2, -2, 3 ) + # p = square_root_mod_prime( 2, 3 ) + + + print_("Testing gcd...") + assert gcd( 3*5*7, 3*5*11, 3*5*13 ) == 3*5 + assert gcd( [ 3*5*7, 3*5*11, 3*5*13 ] ) == 3*5 + assert gcd( 3 ) == 3 + + print_("Testing lcm...") + assert lcm( 3, 5*3, 7*3 ) == 3*5*7 + assert lcm( [ 3, 5*3, 7*3 ] ) == 3*5*7 + assert lcm( 3 ) == 3 + + print_("Testing next_prime...") + bigprimes = ( 999671, + 999683, + 999721, + 999727, + 999749, + 999763, + 999769, + 999773, + 999809, + 999853, + 999863, + 999883, + 999907, + 999917, + 999931, + 999953, + 999959, + 999961, + 999979, + 999983 ) + + for i in range( len( bigprimes ) - 1 ): + assert next_prime( bigprimes[i] ) == bigprimes[ i+1 ] + + error_tally = 0 + + # Test the square_root_mod_prime function: + + for p in smallprimes: + print_("Testing square_root_mod_prime for modulus p = %d." % p) + squares = [] + + for root in range( 0, 1+p//2 ): + sq = ( root * root ) % p + squares.append( sq ) + calculated = square_root_mod_prime( sq, p ) + if ( calculated * calculated ) % p != sq: + error_tally = error_tally + 1 + print_("Failed to find %d as sqrt( %d ) mod %d. Said %d." % \ + ( root, sq, p, calculated )) + + for nonsquare in range( 0, p ): + if nonsquare not in squares: + try: + calculated = square_root_mod_prime( nonsquare, p ) + except SquareRootError: + pass + else: + error_tally = error_tally + 1 + print_("Failed to report no root for sqrt( %d ) mod %d." % \ + ( nonsquare, p )) + + # Test the jacobi function: + for m in range( 3, 400, 2 ): + print_("Testing jacobi for modulus m = %d." % m) + if is_prime( m ): + squares = [] + for root in range( 1, m ): + if jacobi( root * root, m ) != 1: + error_tally = error_tally + 1 + print_("jacobi( %d * %d, %d ) != 1" % ( root, root, m )) + squares.append( root * root % m ) + for i in range( 1, m ): + if not i in squares: + if jacobi( i, m ) != -1: + error_tally = error_tally + 1 + print_("jacobi( %d, %d ) != -1" % ( i, m )) + else: # m is not prime. + f = factorization( m ) + for a in range( 1, m ): + c = 1 + for i in f: + c = c * jacobi( a, i[0] ) ** i[1] + if c != jacobi( a, m ): + error_tally = error_tally + 1 + print_("%d != jacobi( %d, %d )" % ( c, a, m )) + + +# Test the inverse_mod function: + print_("Testing inverse_mod . . .") + import random + n_tests = 0 + for i in range( 100 ): + m = random.randint( 20, 10000 ) + for j in range( 100 ): + a = random.randint( 1, m-1 ) + if gcd( a, m ) == 1: + n_tests = n_tests + 1 + inv = inverse_mod( a, m ) + if inv <= 0 or inv >= m or ( a * inv ) % m != 1: + error_tally = error_tally + 1 + print_("%d = inverse_mod( %d, %d ) is wrong." % ( inv, a, m )) + assert n_tests > 1000 + print_(n_tests, " tests of inverse_mod completed.") + + class FailedTest(Exception): pass + print_(error_tally, "errors detected.") + if error_tally != 0: + raise FailedTest("%d errors detected" % error_tally) + +if __name__ == '__main__': + __main__() diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/rfc6979.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/rfc6979.py new file mode 100644 index 0000000..7c805be --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/rfc6979.py @@ -0,0 +1,103 @@ +''' +RFC 6979: + Deterministic Usage of the Digital Signature Algorithm (DSA) and + Elliptic Curve Digital Signature Algorithm (ECDSA) + + http://tools.ietf.org/html/rfc6979 + +Many thanks to Coda Hale for his implementation in Go language: + https://github.com/codahale/rfc6979 +''' + +import hmac +from binascii import hexlify +from .util import number_to_string, number_to_string_crop +from .six import b + +try: + bin(0) +except NameError: + binmap = {"0": "0000", "1": "0001", "2": "0010", "3": "0011", + "4": "0100", "5": "0101", "6": "0110", "7": "0111", + "8": "1000", "9": "1001", "a": "1010", "b": "1011", + "c": "1100", "d": "1101", "e": "1110", "f": "1111"} + def bin(value): # for python2.5 + v = "".join(binmap[x] for x in "%x"%abs(value)).lstrip("0") + if value < 0: + return "-0b" + v + return "0b" + v + +def bit_length(num): + # http://docs.python.org/dev/library/stdtypes.html#int.bit_length + s = bin(num) # binary representation: bin(-37) --> '-0b100101' + s = s.lstrip('-0b') # remove leading zeros and minus sign + return len(s) # len('100101') --> 6 + +def bits2int(data, qlen): + x = int(hexlify(data), 16) + l = len(data) * 8 + + if l > qlen: + return x >> (l-qlen) + return x + +def bits2octets(data, order): + z1 = bits2int(data, bit_length(order)) + z2 = z1 - order + + if z2 < 0: + z2 = z1 + + return number_to_string_crop(z2, order) + +# https://tools.ietf.org/html/rfc6979#section-3.2 +def generate_k(generator, secexp, hash_func, data): + ''' + generator - ECDSA generator used in the signature + secexp - secure exponent (private key) in numeric form + hash_func - reference to the same hash function used for generating hash + data - hash in binary form of the signing data + ''' + + qlen = bit_length(generator.order()) + holen = hash_func().digest_size + rolen = (qlen + 7) / 8 + bx = number_to_string(secexp, generator.order()) + bits2octets(data, generator.order()) + + # Step B + v = b('\x01') * holen + + # Step C + k = b('\x00') * holen + + # Step D + + k = hmac.new(k, v+b('\x00')+bx, hash_func).digest() + + # Step E + v = hmac.new(k, v, hash_func).digest() + + # Step F + k = hmac.new(k, v+b('\x01')+bx, hash_func).digest() + + # Step G + v = hmac.new(k, v, hash_func).digest() + + # Step H + while True: + # Step H1 + t = b('') + + # Step H2 + while len(t) < rolen: + v = hmac.new(k, v, hash_func).digest() + t += v + + # Step H3 + secret = bits2int(t, qlen) + + if secret >= 1 and secret < generator.order(): + return secret + + k = hmac.new(k, v+b('\x00'), hash_func).digest() + v = hmac.new(k, v, hash_func).digest() diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/six.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/six.py new file mode 100644 index 0000000..3003f8f --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/six.py @@ -0,0 +1,394 @@ +"""Utilities for writing code that runs on Python 2 and 3""" + +# Copyright (c) 2010-2012 Benjamin Peterson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +# the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import operator +import sys +import types + +__author__ = "Benjamin Peterson " +__version__ = "1.2.0" + + +# True if we are running on Python 3. +PY3 = sys.version_info[0] == 3 + +if PY3: + string_types = str, + integer_types = int, + class_types = type, + text_type = str + binary_type = bytes + + MAXSIZE = sys.maxsize +else: + string_types = basestring, + integer_types = (int, long) + class_types = (type, types.ClassType) + text_type = unicode + binary_type = str + + if sys.platform.startswith("java"): + # Jython always uses 32 bits. + MAXSIZE = int((1 << 31) - 1) + else: + # It's possible to have sizeof(long) != sizeof(Py_ssize_t). + class X(object): + def __len__(self): + return 1 << 31 + try: + len(X()) + except OverflowError: + # 32-bit + MAXSIZE = int((1 << 31) - 1) + else: + # 64-bit + MAXSIZE = int((1 << 63) - 1) + del X + + +def _add_doc(func, doc): + """Add documentation to a function.""" + func.__doc__ = doc + + +def _import_module(name): + """Import module, returning the module after the last dot.""" + __import__(name) + return sys.modules[name] + + +class _LazyDescr(object): + + def __init__(self, name): + self.name = name + + def __get__(self, obj, tp): + result = self._resolve() + setattr(obj, self.name, result) + # This is a bit ugly, but it avoids running this again. + delattr(tp, self.name) + return result + + +class MovedModule(_LazyDescr): + + def __init__(self, name, old, new=None): + super(MovedModule, self).__init__(name) + if PY3: + if new is None: + new = name + self.mod = new + else: + self.mod = old + + def _resolve(self): + return _import_module(self.mod) + + +class MovedAttribute(_LazyDescr): + + def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): + super(MovedAttribute, self).__init__(name) + if PY3: + if new_mod is None: + new_mod = name + self.mod = new_mod + if new_attr is None: + if old_attr is None: + new_attr = name + else: + new_attr = old_attr + self.attr = new_attr + else: + self.mod = old_mod + if old_attr is None: + old_attr = name + self.attr = old_attr + + def _resolve(self): + module = _import_module(self.mod) + return getattr(module, self.attr) + + + +class _MovedItems(types.ModuleType): + """Lazy loading of moved objects""" + + +_moved_attributes = [ + MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), + MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), + MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), + MovedAttribute("map", "itertools", "builtins", "imap", "map"), + MovedAttribute("reload_module", "__builtin__", "imp", "reload"), + MovedAttribute("reduce", "__builtin__", "functools"), + MovedAttribute("StringIO", "StringIO", "io"), + MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), + + MovedModule("builtins", "__builtin__"), + MovedModule("configparser", "ConfigParser"), + MovedModule("copyreg", "copy_reg"), + MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), + MovedModule("http_cookies", "Cookie", "http.cookies"), + MovedModule("html_entities", "htmlentitydefs", "html.entities"), + MovedModule("html_parser", "HTMLParser", "html.parser"), + MovedModule("http_client", "httplib", "http.client"), + MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), + MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), + MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), + MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), + MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), + MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), + MovedModule("cPickle", "cPickle", "pickle"), + MovedModule("queue", "Queue"), + MovedModule("reprlib", "repr"), + MovedModule("socketserver", "SocketServer"), + MovedModule("tkinter", "Tkinter"), + MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), + MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), + MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), + MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), + MovedModule("tkinter_tix", "Tix", "tkinter.tix"), + MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), + MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), + MovedModule("tkinter_colorchooser", "tkColorChooser", + "tkinter.colorchooser"), + MovedModule("tkinter_commondialog", "tkCommonDialog", + "tkinter.commondialog"), + MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), + MovedModule("tkinter_font", "tkFont", "tkinter.font"), + MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), + MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", + "tkinter.simpledialog"), + MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), + MovedModule("winreg", "_winreg"), +] +for attr in _moved_attributes: + setattr(_MovedItems, attr.name, attr) +del attr + +moves = sys.modules[__name__ + ".moves"] = _MovedItems("moves") + + +def add_move(move): + """Add an item to six.moves.""" + setattr(_MovedItems, move.name, move) + + +def remove_move(name): + """Remove item from six.moves.""" + try: + delattr(_MovedItems, name) + except AttributeError: + try: + del moves.__dict__[name] + except KeyError: + raise AttributeError("no such move, %r" % (name,)) + + +if PY3: + _meth_func = "__func__" + _meth_self = "__self__" + + _func_code = "__code__" + _func_defaults = "__defaults__" + + _iterkeys = "keys" + _itervalues = "values" + _iteritems = "items" +else: + _meth_func = "im_func" + _meth_self = "im_self" + + _func_code = "func_code" + _func_defaults = "func_defaults" + + _iterkeys = "iterkeys" + _itervalues = "itervalues" + _iteritems = "iteritems" + + +try: + advance_iterator = next +except NameError: + def advance_iterator(it): + return it.next() +next = advance_iterator + + +try: + callable = callable +except NameError: + def callable(obj): + return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) + + +if PY3: + def get_unbound_function(unbound): + return unbound + + Iterator = object +else: + def get_unbound_function(unbound): + return unbound.im_func + + class Iterator(object): + + def next(self): + return type(self).__next__(self) + + callable = callable +_add_doc(get_unbound_function, + """Get the function out of a possibly unbound function""") + + +get_method_function = operator.attrgetter(_meth_func) +get_method_self = operator.attrgetter(_meth_self) +get_function_code = operator.attrgetter(_func_code) +get_function_defaults = operator.attrgetter(_func_defaults) + + +def iterkeys(d): + """Return an iterator over the keys of a dictionary.""" + return iter(getattr(d, _iterkeys)()) + +def itervalues(d): + """Return an iterator over the values of a dictionary.""" + return iter(getattr(d, _itervalues)()) + +def iteritems(d): + """Return an iterator over the (key, value) pairs of a dictionary.""" + return iter(getattr(d, _iteritems)()) + + +if PY3: + def b(s): + return s.encode("latin-1") + def u(s): + return s + if sys.version_info[1] <= 1: + def int2byte(i): + return bytes((i,)) + else: + # This is about 2x faster than the implementation above on 3.2+ + int2byte = operator.methodcaller("to_bytes", 1, "big") + import io + StringIO = io.StringIO + BytesIO = io.BytesIO +else: + def b(s): + return s + def u(s): + if isinstance(s, unicode): + return s + return unicode(s, "unicode_escape") + int2byte = chr + import StringIO + StringIO = BytesIO = StringIO.StringIO +_add_doc(b, """Byte literal""") +_add_doc(u, """Text literal""") + + +if PY3: + import builtins + exec_ = getattr(builtins, "exec") + + + def reraise(tp, value, tb=None): + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + + + print_ = getattr(builtins, "print") + del builtins + +else: + def exec_(_code_, _globs_=None, _locs_=None): + """Execute code in a namespace.""" + if _globs_ is None: + frame = sys._getframe(1) + _globs_ = frame.f_globals + if _locs_ is None: + _locs_ = frame.f_locals + del frame + elif _locs_ is None: + _locs_ = _globs_ + exec("""exec _code_ in _globs_, _locs_""") + + + exec_("""def reraise(tp, value, tb=None): + raise tp, value, tb +""") + + + def print_(*args, **kwargs): + """The new-style print function.""" + fp = kwargs.pop("file", sys.stdout) + if fp is None: + return + def write(data): + if not isinstance(data, basestring): + data = str(data) + fp.write(data) + want_unicode = False + sep = kwargs.pop("sep", None) + if sep is not None: + if isinstance(sep, unicode): + want_unicode = True + elif not isinstance(sep, str): + raise TypeError("sep must be None or a string") + end = kwargs.pop("end", None) + if end is not None: + if isinstance(end, unicode): + want_unicode = True + elif not isinstance(end, str): + raise TypeError("end must be None or a string") + if kwargs: + raise TypeError("invalid keyword arguments to print()") + if not want_unicode: + for arg in args: + if isinstance(arg, unicode): + want_unicode = True + break + if want_unicode: + newline = unicode("\n") + space = unicode(" ") + else: + newline = "\n" + space = " " + if sep is None: + sep = space + if end is None: + end = newline + for i, arg in enumerate(args): + if i: + write(sep) + write(arg) + write(end) + +_add_doc(reraise, """Reraise an exception.""") + + +def with_metaclass(meta, base=object): + """Create a base class with a metaclass.""" + return meta("NewBase", (base,), {}) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/test_pyecdsa.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/test_pyecdsa.py new file mode 100644 index 0000000..9d510bb --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/test_pyecdsa.py @@ -0,0 +1,650 @@ +from __future__ import with_statement, division + +import unittest +import os +import time +import shutil +import subprocess +from binascii import hexlify, unhexlify +from hashlib import sha1, sha256, sha512 + +from .six import b, print_, binary_type +from .keys import SigningKey, VerifyingKey +from .keys import BadSignatureError +from . import util +from .util import sigencode_der, sigencode_strings +from .util import sigdecode_der, sigdecode_strings +from .curves import Curve, UnknownCurveError +from .curves import NIST192p, NIST224p, NIST256p, NIST384p, NIST521p, SECP256k1 +from .ellipticcurve import Point +from . import der +from . import rfc6979 + +class SubprocessError(Exception): + pass + +def run_openssl(cmd): + OPENSSL = "openssl" + p = subprocess.Popen([OPENSSL] + cmd.split(), + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + stdout, ignored = p.communicate() + if p.returncode != 0: + raise SubprocessError("cmd '%s %s' failed: rc=%s, stdout/err was %s" % + (OPENSSL, cmd, p.returncode, stdout)) + return stdout.decode() + +BENCH = False + +class ECDSA(unittest.TestCase): + def test_basic(self): + priv = SigningKey.generate() + pub = priv.get_verifying_key() + + data = b("blahblah") + sig = priv.sign(data) + + self.assertTrue(pub.verify(sig, data)) + self.assertRaises(BadSignatureError, pub.verify, sig, data+b("bad")) + + pub2 = VerifyingKey.from_string(pub.to_string()) + self.assertTrue(pub2.verify(sig, data)) + + def test_deterministic(self): + data = b("blahblah") + secexp = int("9d0219792467d7d37b4d43298a7d0c05", 16) + + priv = SigningKey.from_secret_exponent(secexp, SECP256k1, sha256) + pub = priv.get_verifying_key() + + k = rfc6979.generate_k(SECP256k1.generator, secexp, sha256, sha256(data).digest()) + + sig1 = priv.sign(data, k=k) + self.assertTrue(pub.verify(sig1, data)) + + sig2 = priv.sign(data, k=k) + self.assertTrue(pub.verify(sig2, data)) + + sig3 = priv.sign_deterministic(data, sha256) + self.assertTrue(pub.verify(sig3, data)) + + self.assertEqual(sig1, sig2) + self.assertEqual(sig1, sig3) + + def test_bad_usage(self): + # sk=SigningKey() is wrong + self.assertRaises(TypeError, SigningKey) + self.assertRaises(TypeError, VerifyingKey) + + def test_lengths(self): + default = NIST192p + priv = SigningKey.generate() + pub = priv.get_verifying_key() + self.assertEqual(len(pub.to_string()), default.verifying_key_length) + sig = priv.sign(b("data")) + self.assertEqual(len(sig), default.signature_length) + if BENCH: + print_() + for curve in (NIST192p, NIST224p, NIST256p, NIST384p, NIST521p): + start = time.time() + priv = SigningKey.generate(curve=curve) + pub1 = priv.get_verifying_key() + keygen_time = time.time() - start + pub2 = VerifyingKey.from_string(pub1.to_string(), curve) + self.assertEqual(pub1.to_string(), pub2.to_string()) + self.assertEqual(len(pub1.to_string()), + curve.verifying_key_length) + start = time.time() + sig = priv.sign(b("data")) + sign_time = time.time() - start + self.assertEqual(len(sig), curve.signature_length) + if BENCH: + start = time.time() + pub1.verify(sig, b("data")) + verify_time = time.time() - start + print_("%s: siglen=%d, keygen=%0.3fs, sign=%0.3f, verify=%0.3f" \ + % (curve.name, curve.signature_length, + keygen_time, sign_time, verify_time)) + + def test_serialize(self): + seed = b("secret") + curve = NIST192p + secexp1 = util.randrange_from_seed__trytryagain(seed, curve.order) + secexp2 = util.randrange_from_seed__trytryagain(seed, curve.order) + self.assertEqual(secexp1, secexp2) + priv1 = SigningKey.from_secret_exponent(secexp1, curve) + priv2 = SigningKey.from_secret_exponent(secexp2, curve) + self.assertEqual(hexlify(priv1.to_string()), + hexlify(priv2.to_string())) + self.assertEqual(priv1.to_pem(), priv2.to_pem()) + pub1 = priv1.get_verifying_key() + pub2 = priv2.get_verifying_key() + data = b("data") + sig1 = priv1.sign(data) + sig2 = priv2.sign(data) + self.assertTrue(pub1.verify(sig1, data)) + self.assertTrue(pub2.verify(sig1, data)) + self.assertTrue(pub1.verify(sig2, data)) + self.assertTrue(pub2.verify(sig2, data)) + self.assertEqual(hexlify(pub1.to_string()), + hexlify(pub2.to_string())) + + def test_nonrandom(self): + s = b("all the entropy in the entire world, compressed into one line") + def not_much_entropy(numbytes): + return s[:numbytes] + # we control the entropy source, these two keys should be identical: + priv1 = SigningKey.generate(entropy=not_much_entropy) + priv2 = SigningKey.generate(entropy=not_much_entropy) + self.assertEqual(hexlify(priv1.get_verifying_key().to_string()), + hexlify(priv2.get_verifying_key().to_string())) + # likewise, signatures should be identical. Obviously you'd never + # want to do this with keys you care about, because the secrecy of + # the private key depends upon using different random numbers for + # each signature + sig1 = priv1.sign(b("data"), entropy=not_much_entropy) + sig2 = priv2.sign(b("data"), entropy=not_much_entropy) + self.assertEqual(hexlify(sig1), hexlify(sig2)) + + def assertTruePrivkeysEqual(self, priv1, priv2): + self.assertEqual(priv1.privkey.secret_multiplier, + priv2.privkey.secret_multiplier) + self.assertEqual(priv1.privkey.public_key.generator, + priv2.privkey.public_key.generator) + + def failIfPrivkeysEqual(self, priv1, priv2): + self.failIfEqual(priv1.privkey.secret_multiplier, + priv2.privkey.secret_multiplier) + + def test_privkey_creation(self): + s = b("all the entropy in the entire world, compressed into one line") + def not_much_entropy(numbytes): + return s[:numbytes] + priv1 = SigningKey.generate() + self.assertEqual(priv1.baselen, NIST192p.baselen) + + priv1 = SigningKey.generate(curve=NIST224p) + self.assertEqual(priv1.baselen, NIST224p.baselen) + + priv1 = SigningKey.generate(entropy=not_much_entropy) + self.assertEqual(priv1.baselen, NIST192p.baselen) + priv2 = SigningKey.generate(entropy=not_much_entropy) + self.assertEqual(priv2.baselen, NIST192p.baselen) + self.assertTruePrivkeysEqual(priv1, priv2) + + priv1 = SigningKey.from_secret_exponent(secexp=3) + self.assertEqual(priv1.baselen, NIST192p.baselen) + priv2 = SigningKey.from_secret_exponent(secexp=3) + self.assertTruePrivkeysEqual(priv1, priv2) + + priv1 = SigningKey.from_secret_exponent(secexp=4, curve=NIST224p) + self.assertEqual(priv1.baselen, NIST224p.baselen) + + def test_privkey_strings(self): + priv1 = SigningKey.generate() + s1 = priv1.to_string() + self.assertEqual(type(s1), binary_type) + self.assertEqual(len(s1), NIST192p.baselen) + priv2 = SigningKey.from_string(s1) + self.assertTruePrivkeysEqual(priv1, priv2) + + s1 = priv1.to_pem() + self.assertEqual(type(s1), binary_type) + self.assertTrue(s1.startswith(b("-----BEGIN EC PRIVATE KEY-----"))) + self.assertTrue(s1.strip().endswith(b("-----END EC PRIVATE KEY-----"))) + priv2 = SigningKey.from_pem(s1) + self.assertTruePrivkeysEqual(priv1, priv2) + + s1 = priv1.to_der() + self.assertEqual(type(s1), binary_type) + priv2 = SigningKey.from_der(s1) + self.assertTruePrivkeysEqual(priv1, priv2) + + priv1 = SigningKey.generate(curve=NIST256p) + s1 = priv1.to_pem() + self.assertEqual(type(s1), binary_type) + self.assertTrue(s1.startswith(b("-----BEGIN EC PRIVATE KEY-----"))) + self.assertTrue(s1.strip().endswith(b("-----END EC PRIVATE KEY-----"))) + priv2 = SigningKey.from_pem(s1) + self.assertTruePrivkeysEqual(priv1, priv2) + + s1 = priv1.to_der() + self.assertEqual(type(s1), binary_type) + priv2 = SigningKey.from_der(s1) + self.assertTruePrivkeysEqual(priv1, priv2) + + def assertTruePubkeysEqual(self, pub1, pub2): + self.assertEqual(pub1.pubkey.point, pub2.pubkey.point) + self.assertEqual(pub1.pubkey.generator, pub2.pubkey.generator) + self.assertEqual(pub1.curve, pub2.curve) + + def test_pubkey_strings(self): + priv1 = SigningKey.generate() + pub1 = priv1.get_verifying_key() + s1 = pub1.to_string() + self.assertEqual(type(s1), binary_type) + self.assertEqual(len(s1), NIST192p.verifying_key_length) + pub2 = VerifyingKey.from_string(s1) + self.assertTruePubkeysEqual(pub1, pub2) + + priv1 = SigningKey.generate(curve=NIST256p) + pub1 = priv1.get_verifying_key() + s1 = pub1.to_string() + self.assertEqual(type(s1), binary_type) + self.assertEqual(len(s1), NIST256p.verifying_key_length) + pub2 = VerifyingKey.from_string(s1, curve=NIST256p) + self.assertTruePubkeysEqual(pub1, pub2) + + pub1_der = pub1.to_der() + self.assertEqual(type(pub1_der), binary_type) + pub2 = VerifyingKey.from_der(pub1_der) + self.assertTruePubkeysEqual(pub1, pub2) + + self.assertRaises(der.UnexpectedDER, + VerifyingKey.from_der, pub1_der+b("junk")) + badpub = VerifyingKey.from_der(pub1_der) + class FakeGenerator: + def order(self): return 123456789 + badcurve = Curve("unknown", None, FakeGenerator(), (1,2,3,4,5,6)) + badpub.curve = badcurve + badder = badpub.to_der() + self.assertRaises(UnknownCurveError, VerifyingKey.from_der, badder) + + pem = pub1.to_pem() + self.assertEqual(type(pem), binary_type) + self.assertTrue(pem.startswith(b("-----BEGIN PUBLIC KEY-----")), pem) + self.assertTrue(pem.strip().endswith(b("-----END PUBLIC KEY-----")), pem) + pub2 = VerifyingKey.from_pem(pem) + self.assertTruePubkeysEqual(pub1, pub2) + + def test_signature_strings(self): + priv1 = SigningKey.generate() + pub1 = priv1.get_verifying_key() + data = b("data") + + sig = priv1.sign(data) + self.assertEqual(type(sig), binary_type) + self.assertEqual(len(sig), NIST192p.signature_length) + self.assertTrue(pub1.verify(sig, data)) + + sig = priv1.sign(data, sigencode=sigencode_strings) + self.assertEqual(type(sig), tuple) + self.assertEqual(len(sig), 2) + self.assertEqual(type(sig[0]), binary_type) + self.assertEqual(type(sig[1]), binary_type) + self.assertEqual(len(sig[0]), NIST192p.baselen) + self.assertEqual(len(sig[1]), NIST192p.baselen) + self.assertTrue(pub1.verify(sig, data, sigdecode=sigdecode_strings)) + + sig_der = priv1.sign(data, sigencode=sigencode_der) + self.assertEqual(type(sig_der), binary_type) + self.assertTrue(pub1.verify(sig_der, data, sigdecode=sigdecode_der)) + + def test_hashfunc(self): + sk = SigningKey.generate(curve=NIST256p, hashfunc=sha256) + data = b("security level is 128 bits") + sig = sk.sign(data) + vk = VerifyingKey.from_string(sk.get_verifying_key().to_string(), + curve=NIST256p, hashfunc=sha256) + self.assertTrue(vk.verify(sig, data)) + + sk2 = SigningKey.generate(curve=NIST256p) + sig2 = sk2.sign(data, hashfunc=sha256) + vk2 = VerifyingKey.from_string(sk2.get_verifying_key().to_string(), + curve=NIST256p, hashfunc=sha256) + self.assertTrue(vk2.verify(sig2, data)) + + vk3 = VerifyingKey.from_string(sk.get_verifying_key().to_string(), + curve=NIST256p) + self.assertTrue(vk3.verify(sig, data, hashfunc=sha256)) + + +class OpenSSL(unittest.TestCase): + # test interoperability with OpenSSL tools. Note that openssl's ECDSA + # sign/verify arguments changed between 0.9.8 and 1.0.0: the early + # versions require "-ecdsa-with-SHA1", the later versions want just + # "-SHA1" (or to leave out that argument entirely, which means the + # signature will use some default digest algorithm, probably determined + # by the key, probably always SHA1). + # + # openssl ecparam -name secp224r1 -genkey -out privkey.pem + # openssl ec -in privkey.pem -text -noout # get the priv/pub keys + # openssl dgst -ecdsa-with-SHA1 -sign privkey.pem -out data.sig data.txt + # openssl asn1parse -in data.sig -inform DER + # data.sig is 64 bytes, probably 56b plus ASN1 overhead + # openssl dgst -ecdsa-with-SHA1 -prverify privkey.pem -signature data.sig data.txt ; echo $? + # openssl ec -in privkey.pem -pubout -out pubkey.pem + # openssl ec -in privkey.pem -pubout -outform DER -out pubkey.der + + def get_openssl_messagedigest_arg(self): + v = run_openssl("version") + # e.g. "OpenSSL 1.0.0 29 Mar 2010", or "OpenSSL 1.0.0a 1 Jun 2010", + # or "OpenSSL 0.9.8o 01 Jun 2010" + vs = v.split()[1].split(".") + if vs >= ["1","0","0"]: + return "-SHA1" + else: + return "-ecdsa-with-SHA1" + + # sk: 1:OpenSSL->python 2:python->OpenSSL + # vk: 3:OpenSSL->python 4:python->OpenSSL + # sig: 5:OpenSSL->python 6:python->OpenSSL + + def test_from_openssl_nist192p(self): + return self.do_test_from_openssl(NIST192p, "prime192v1") + def test_from_openssl_nist224p(self): + return self.do_test_from_openssl(NIST224p, "secp224r1") + def test_from_openssl_nist384p(self): + return self.do_test_from_openssl(NIST384p, "secp384r1") + def test_from_openssl_nist521p(self): + return self.do_test_from_openssl(NIST521p, "secp521r1") + + def do_test_from_openssl(self, curve, curvename): + # OpenSSL: create sk, vk, sign. + # Python: read vk(3), checksig(5), read sk(1), sign, check + mdarg = self.get_openssl_messagedigest_arg() + if os.path.isdir("t"): + shutil.rmtree("t") + os.mkdir("t") + run_openssl("ecparam -name %s -genkey -out t/privkey.pem" % curvename) + run_openssl("ec -in t/privkey.pem -pubout -out t/pubkey.pem") + data = b("data") + with open("t/data.txt","wb") as e: e.write(data) + run_openssl("dgst %s -sign t/privkey.pem -out t/data.sig t/data.txt" % mdarg) + run_openssl("dgst %s -verify t/pubkey.pem -signature t/data.sig t/data.txt" % mdarg) + with open("t/pubkey.pem","rb") as e: pubkey_pem = e.read() + vk = VerifyingKey.from_pem(pubkey_pem) # 3 + with open("t/data.sig","rb") as e: sig_der = e.read() + self.assertTrue(vk.verify(sig_der, data, # 5 + hashfunc=sha1, sigdecode=sigdecode_der)) + + with open("t/privkey.pem") as e: fp = e.read() + sk = SigningKey.from_pem(fp) # 1 + sig = sk.sign(data) + self.assertTrue(vk.verify(sig, data)) + + def test_to_openssl_nist192p(self): + self.do_test_to_openssl(NIST192p, "prime192v1") + def test_to_openssl_nist224p(self): + self.do_test_to_openssl(NIST224p, "secp224r1") + def test_to_openssl_nist384p(self): + self.do_test_to_openssl(NIST384p, "secp384r1") + def test_to_openssl_nist521p(self): + self.do_test_to_openssl(NIST521p, "secp521r1") + + def do_test_to_openssl(self, curve, curvename): + # Python: create sk, vk, sign. + # OpenSSL: read vk(4), checksig(6), read sk(2), sign, check + mdarg = self.get_openssl_messagedigest_arg() + if os.path.isdir("t"): + shutil.rmtree("t") + os.mkdir("t") + sk = SigningKey.generate(curve=curve) + vk = sk.get_verifying_key() + data = b("data") + with open("t/pubkey.der","wb") as e: e.write(vk.to_der()) # 4 + with open("t/pubkey.pem","wb") as e: e.write(vk.to_pem()) # 4 + sig_der = sk.sign(data, hashfunc=sha1, sigencode=sigencode_der) + + with open("t/data.sig","wb") as e: e.write(sig_der) # 6 + with open("t/data.txt","wb") as e: e.write(data) + with open("t/baddata.txt","wb") as e: e.write(data+b("corrupt")) + + self.assertRaises(SubprocessError, run_openssl, + "dgst %s -verify t/pubkey.der -keyform DER -signature t/data.sig t/baddata.txt" % mdarg) + run_openssl("dgst %s -verify t/pubkey.der -keyform DER -signature t/data.sig t/data.txt" % mdarg) + + with open("t/privkey.pem","wb") as e: e.write(sk.to_pem()) # 2 + run_openssl("dgst %s -sign t/privkey.pem -out t/data.sig2 t/data.txt" % mdarg) + run_openssl("dgst %s -verify t/pubkey.pem -signature t/data.sig2 t/data.txt" % mdarg) + +class DER(unittest.TestCase): + def test_oids(self): + oid_ecPublicKey = der.encode_oid(1, 2, 840, 10045, 2, 1) + self.assertEqual(hexlify(oid_ecPublicKey), b("06072a8648ce3d0201")) + self.assertEqual(hexlify(NIST224p.encoded_oid), b("06052b81040021")) + self.assertEqual(hexlify(NIST256p.encoded_oid), + b("06082a8648ce3d030107")) + x = oid_ecPublicKey + b("more") + x1, rest = der.remove_object(x) + self.assertEqual(x1, (1, 2, 840, 10045, 2, 1)) + self.assertEqual(rest, b("more")) + + def test_integer(self): + self.assertEqual(der.encode_integer(0), b("\x02\x01\x00")) + self.assertEqual(der.encode_integer(1), b("\x02\x01\x01")) + self.assertEqual(der.encode_integer(127), b("\x02\x01\x7f")) + self.assertEqual(der.encode_integer(128), b("\x02\x02\x00\x80")) + self.assertEqual(der.encode_integer(256), b("\x02\x02\x01\x00")) + #self.assertEqual(der.encode_integer(-1), b("\x02\x01\xff")) + + def s(n): return der.remove_integer(der.encode_integer(n) + b("junk")) + self.assertEqual(s(0), (0, b("junk"))) + self.assertEqual(s(1), (1, b("junk"))) + self.assertEqual(s(127), (127, b("junk"))) + self.assertEqual(s(128), (128, b("junk"))) + self.assertEqual(s(256), (256, b("junk"))) + self.assertEqual(s(1234567890123456789012345678901234567890), + (1234567890123456789012345678901234567890,b("junk"))) + + def test_number(self): + self.assertEqual(der.encode_number(0), b("\x00")) + self.assertEqual(der.encode_number(127), b("\x7f")) + self.assertEqual(der.encode_number(128), b("\x81\x00")) + self.assertEqual(der.encode_number(3*128+7), b("\x83\x07")) + #self.assertEqual(der.read_number("\x81\x9b"+"more"), (155, 2)) + #self.assertEqual(der.encode_number(155), b("\x81\x9b")) + for n in (0, 1, 2, 127, 128, 3*128+7, 840, 10045): #, 155): + x = der.encode_number(n) + b("more") + n1, llen = der.read_number(x) + self.assertEqual(n1, n) + self.assertEqual(x[llen:], b("more")) + + def test_length(self): + self.assertEqual(der.encode_length(0), b("\x00")) + self.assertEqual(der.encode_length(127), b("\x7f")) + self.assertEqual(der.encode_length(128), b("\x81\x80")) + self.assertEqual(der.encode_length(255), b("\x81\xff")) + self.assertEqual(der.encode_length(256), b("\x82\x01\x00")) + self.assertEqual(der.encode_length(3*256+7), b("\x82\x03\x07")) + self.assertEqual(der.read_length(b("\x81\x9b")+b("more")), (155, 2)) + self.assertEqual(der.encode_length(155), b("\x81\x9b")) + for n in (0, 1, 2, 127, 128, 255, 256, 3*256+7, 155): + x = der.encode_length(n) + b("more") + n1, llen = der.read_length(x) + self.assertEqual(n1, n) + self.assertEqual(x[llen:], b("more")) + + def test_sequence(self): + x = der.encode_sequence(b("ABC"), b("DEF")) + b("GHI") + self.assertEqual(x, b("\x30\x06ABCDEFGHI")) + x1, rest = der.remove_sequence(x) + self.assertEqual(x1, b("ABCDEF")) + self.assertEqual(rest, b("GHI")) + + def test_constructed(self): + x = der.encode_constructed(0, NIST224p.encoded_oid) + self.assertEqual(hexlify(x), b("a007") + b("06052b81040021")) + x = der.encode_constructed(1, unhexlify(b("0102030a0b0c"))) + self.assertEqual(hexlify(x), b("a106") + b("0102030a0b0c")) + +class Util(unittest.TestCase): + def test_trytryagain(self): + tta = util.randrange_from_seed__trytryagain + for i in range(1000): + seed = "seed-%d" % i + for order in (2**8-2, 2**8-1, 2**8, 2**8+1, 2**8+2, + 2**16-1, 2**16+1): + n = tta(seed, order) + self.assertTrue(1 <= n < order, (1, n, order)) + # this trytryagain *does* provide long-term stability + self.assertEqual(("%x"%(tta("seed", NIST224p.order))).encode(), + b("6fa59d73bf0446ae8743cf748fc5ac11d5585a90356417e97155c3bc")) + + def test_randrange(self): + # util.randrange does not provide long-term stability: we might + # change the algorithm in the future. + for i in range(1000): + entropy = util.PRNG("seed-%d" % i) + for order in (2**8-2, 2**8-1, 2**8, + 2**16-1, 2**16+1, + ): + # that oddball 2**16+1 takes half our runtime + n = util.randrange(order, entropy=entropy) + self.assertTrue(1 <= n < order, (1, n, order)) + + def OFF_test_prove_uniformity(self): + order = 2**8-2 + counts = dict([(i, 0) for i in range(1, order)]) + assert 0 not in counts + assert order not in counts + for i in range(1000000): + seed = "seed-%d" % i + n = util.randrange_from_seed__trytryagain(seed, order) + counts[n] += 1 + # this technique should use the full range + self.assertTrue(counts[order-1]) + for i in range(1, order): + print_("%3d: %s" % (i, "*"*(counts[i]//100))) + +class RFC6979(unittest.TestCase): + # https://tools.ietf.org/html/rfc6979#appendix-A.1 + def _do(self, generator, secexp, hsh, hash_func, expected): + actual = rfc6979.generate_k(generator, secexp, hash_func, hsh) + self.assertEqual(expected, actual) + + def test_SECP256k1(self): + '''RFC doesn't contain test vectors for SECP256k1 used in bitcoin. + This vector has been computed by Golang reference implementation instead.''' + self._do( + generator = SECP256k1.generator, + secexp = int("9d0219792467d7d37b4d43298a7d0c05", 16), + hsh = sha256(b("sample")).digest(), + hash_func = sha256, + expected = int("8fa1f95d514760e498f28957b824ee6ec39ed64826ff4fecc2b5739ec45b91cd", 16)) + + def test_SECP256k1_2(self): + self._do( + generator=SECP256k1.generator, + secexp=int("cca9fbcc1b41e5a95d369eaa6ddcff73b61a4efaa279cfc6567e8daa39cbaf50", 16), + hsh=sha256(b("sample")).digest(), + hash_func=sha256, + expected=int("2df40ca70e639d89528a6b670d9d48d9165fdc0febc0974056bdce192b8e16a3", 16)) + + def test_SECP256k1_3(self): + self._do( + generator=SECP256k1.generator, + secexp=0x1, + hsh=sha256(b("Satoshi Nakamoto")).digest(), + hash_func=sha256, + expected=0x8F8A276C19F4149656B280621E358CCE24F5F52542772691EE69063B74F15D15) + + def test_SECP256k1_4(self): + self._do( + generator=SECP256k1.generator, + secexp=0x1, + hsh=sha256(b("All those moments will be lost in time, like tears in rain. Time to die...")).digest(), + hash_func=sha256, + expected=0x38AA22D72376B4DBC472E06C3BA403EE0A394DA63FC58D88686C611ABA98D6B3) + + def test_SECP256k1_5(self): + self._do( + generator=SECP256k1.generator, + secexp=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140, + hsh=sha256(b("Satoshi Nakamoto")).digest(), + hash_func=sha256, + expected=0x33A19B60E25FB6F4435AF53A3D42D493644827367E6453928554F43E49AA6F90) + + def test_SECP256k1_6(self): + self._do( + generator=SECP256k1.generator, + secexp=0xf8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181, + hsh=sha256(b("Alan Turing")).digest(), + hash_func=sha256, + expected=0x525A82B70E67874398067543FD84C83D30C175FDC45FDEEE082FE13B1D7CFDF1) + + def test_1(self): + # Basic example of the RFC, it also tests 'try-try-again' from Step H of rfc6979 + self._do( + generator = Point(None, 0, 0, int("4000000000000000000020108A2E0CC0D99F8A5EF", 16)), + secexp = int("09A4D6792295A7F730FC3F2B49CBC0F62E862272F", 16), + hsh = unhexlify(b("AF2BDBE1AA9B6EC1E2ADE1D694F41FC71A831D0268E9891562113D8A62ADD1BF")), + hash_func = sha256, + expected = int("23AF4074C90A02B3FE61D286D5C87F425E6BDD81B", 16)) + + def test_2(self): + self._do( + generator=NIST192p.generator, + secexp = int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), + hsh = sha1(b("sample")).digest(), + hash_func = sha1, + expected = int("37D7CA00D2C7B0E5E412AC03BD44BA837FDD5B28CD3B0021", 16)) + + def test_3(self): + self._do( + generator=NIST192p.generator, + secexp = int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), + hsh = sha256(b("sample")).digest(), + hash_func = sha256, + expected = int("32B1B6D7D42A05CB449065727A84804FB1A3E34D8F261496", 16)) + + def test_4(self): + self._do( + generator=NIST192p.generator, + secexp = int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), + hsh = sha512(b("sample")).digest(), + hash_func = sha512, + expected = int("A2AC7AB055E4F20692D49209544C203A7D1F2C0BFBC75DB1", 16)) + + def test_5(self): + self._do( + generator=NIST192p.generator, + secexp = int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), + hsh = sha1(b("test")).digest(), + hash_func = sha1, + expected = int("D9CF9C3D3297D3260773A1DA7418DB5537AB8DD93DE7FA25", 16)) + + def test_6(self): + self._do( + generator=NIST192p.generator, + secexp = int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), + hsh = sha256(b("test")).digest(), + hash_func = sha256, + expected = int("5C4CE89CF56D9E7C77C8585339B006B97B5F0680B4306C6C", 16)) + + def test_7(self): + self._do( + generator=NIST192p.generator, + secexp = int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), + hsh = sha512(b("test")).digest(), + hash_func = sha512, + expected = int("0758753A5254759C7CFBAD2E2D9B0792EEE44136C9480527", 16)) + + def test_8(self): + self._do( + generator=NIST521p.generator, + secexp = int("0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538", 16), + hsh = sha1(b("sample")).digest(), + hash_func = sha1, + expected = int("089C071B419E1C2820962321787258469511958E80582E95D8378E0C2CCDB3CB42BEDE42F50E3FA3C71F5A76724281D31D9C89F0F91FC1BE4918DB1C03A5838D0F9", 16)) + + def test_9(self): + self._do( + generator=NIST521p.generator, + secexp = int("0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538", 16), + hsh = sha256(b("sample")).digest(), + hash_func = sha256, + expected = int("0EDF38AFCAAECAB4383358B34D67C9F2216C8382AAEA44A3DAD5FDC9C32575761793FEF24EB0FC276DFC4F6E3EC476752F043CF01415387470BCBD8678ED2C7E1A0", 16)) + + def test_10(self): + self._do( + generator=NIST521p.generator, + secexp = int("0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538", 16), + hsh = sha512(b("test")).digest(), + hash_func = sha512, + expected = int("16200813020EC986863BEDFC1B121F605C1215645018AEA1A7B215A564DE9EB1B38A67AA1128B80CE391C4FB71187654AAA3431027BFC7F395766CA988C964DC56D", 16)) + +def __main__(): + unittest.main() +if __name__ == "__main__": + __main__() diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/util.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/util.py new file mode 100644 index 0000000..06e4629 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/ecdsa/util.py @@ -0,0 +1,247 @@ +from __future__ import division + +import os +import math +import binascii +from hashlib import sha256 +from . import der +from .curves import orderlen +from .six import PY3, int2byte, b, next + +# RFC5480: +# The "unrestricted" algorithm identifier is: +# id-ecPublicKey OBJECT IDENTIFIER ::= { +# iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 } + +oid_ecPublicKey = (1, 2, 840, 10045, 2, 1) +encoded_oid_ecPublicKey = der.encode_oid(*oid_ecPublicKey) + +def randrange(order, entropy=None): + """Return a random integer k such that 1 <= k < order, uniformly + distributed across that range. For simplicity, this only behaves well if + 'order' is fairly close (but below) a power of 256. The try-try-again + algorithm we use takes longer and longer time (on average) to complete as + 'order' falls, rising to a maximum of avg=512 loops for the worst-case + (256**k)+1 . All of the standard curves behave well. There is a cutoff at + 10k loops (which raises RuntimeError) to prevent an infinite loop when + something is really broken like the entropy function not working. + + Note that this function is not declared to be forwards-compatible: we may + change the behavior in future releases. The entropy= argument (which + should get a callable that behaves like os.entropy) can be used to + achieve stability within a given release (for repeatable unit tests), but + should not be used as a long-term-compatible key generation algorithm. + """ + # we could handle arbitrary orders (even 256**k+1) better if we created + # candidates bit-wise instead of byte-wise, which would reduce the + # worst-case behavior to avg=2 loops, but that would be more complex. The + # change would be to round the order up to a power of 256, subtract one + # (to get 0xffff..), use that to get a byte-long mask for the top byte, + # generate the len-1 entropy bytes, generate one extra byte and mask off + # the top bits, then combine it with the rest. Requires jumping back and + # forth between strings and integers a lot. + + if entropy is None: + entropy = os.urandom + assert order > 1 + bytes = orderlen(order) + dont_try_forever = 10000 # gives about 2**-60 failures for worst case + while dont_try_forever > 0: + dont_try_forever -= 1 + candidate = string_to_number(entropy(bytes)) + 1 + if 1 <= candidate < order: + return candidate + continue + raise RuntimeError("randrange() tried hard but gave up, either something" + " is very wrong or you got realllly unlucky. Order was" + " %x" % order) + +class PRNG: + # this returns a callable which, when invoked with an integer N, will + # return N pseudorandom bytes. Note: this is a short-term PRNG, meant + # primarily for the needs of randrange_from_seed__trytryagain(), which + # only needs to run it a few times per seed. It does not provide + # protection against state compromise (forward security). + def __init__(self, seed): + self.generator = self.block_generator(seed) + + def __call__(self, numbytes): + a = [next(self.generator) for i in range(numbytes)] + + if PY3: + return bytes(a) + else: + return "".join(a) + + + def block_generator(self, seed): + counter = 0 + while True: + for byte in sha256(("prng-%d-%s" % (counter, seed)).encode()).digest(): + yield byte + counter += 1 + +def randrange_from_seed__overshoot_modulo(seed, order): + # hash the data, then turn the digest into a number in [1,order). + # + # We use David-Sarah Hopwood's suggestion: turn it into a number that's + # sufficiently larger than the group order, then modulo it down to fit. + # This should give adequate (but not perfect) uniformity, and simple + # code. There are other choices: try-try-again is the main one. + base = PRNG(seed)(2*orderlen(order)) + number = (int(binascii.hexlify(base), 16) % (order-1)) + 1 + assert 1 <= number < order, (1, number, order) + return number + +def lsb_of_ones(numbits): + return (1 << numbits) - 1 +def bits_and_bytes(order): + bits = int(math.log(order-1, 2)+1) + bytes = bits // 8 + extrabits = bits % 8 + return bits, bytes, extrabits + +# the following randrange_from_seed__METHOD() functions take an +# arbitrarily-sized secret seed and turn it into a number that obeys the same +# range limits as randrange() above. They are meant for deriving consistent +# signing keys from a secret rather than generating them randomly, for +# example a protocol in which three signing keys are derived from a master +# secret. You should use a uniformly-distributed unguessable seed with about +# curve.baselen bytes of entropy. To use one, do this: +# seed = os.urandom(curve.baselen) # or other starting point +# secexp = ecdsa.util.randrange_from_seed__trytryagain(sed, curve.order) +# sk = SigningKey.from_secret_exponent(secexp, curve) + +def randrange_from_seed__truncate_bytes(seed, order, hashmod=sha256): + # hash the seed, then turn the digest into a number in [1,order), but + # don't worry about trying to uniformly fill the range. This will lose, + # on average, four bits of entropy. + bits, bytes, extrabits = bits_and_bytes(order) + if extrabits: + bytes += 1 + base = hashmod(seed).digest()[:bytes] + base = "\x00"*(bytes-len(base)) + base + number = 1+int(binascii.hexlify(base), 16) + assert 1 <= number < order + return number + +def randrange_from_seed__truncate_bits(seed, order, hashmod=sha256): + # like string_to_randrange_truncate_bytes, but only lose an average of + # half a bit + bits = int(math.log(order-1, 2)+1) + maxbytes = (bits+7) // 8 + base = hashmod(seed).digest()[:maxbytes] + base = "\x00"*(maxbytes-len(base)) + base + topbits = 8*maxbytes - bits + if topbits: + base = int2byte(ord(base[0]) & lsb_of_ones(topbits)) + base[1:] + number = 1+int(binascii.hexlify(base), 16) + assert 1 <= number < order + return number + +def randrange_from_seed__trytryagain(seed, order): + # figure out exactly how many bits we need (rounded up to the nearest + # bit), so we can reduce the chance of looping to less than 0.5 . This is + # specified to feed from a byte-oriented PRNG, and discards the + # high-order bits of the first byte as necessary to get the right number + # of bits. The average number of loops will range from 1.0 (when + # order=2**k-1) to 2.0 (when order=2**k+1). + assert order > 1 + bits, bytes, extrabits = bits_and_bytes(order) + generate = PRNG(seed) + while True: + extrabyte = b("") + if extrabits: + extrabyte = int2byte(ord(generate(1)) & lsb_of_ones(extrabits)) + guess = string_to_number(extrabyte + generate(bytes)) + 1 + if 1 <= guess < order: + return guess + + +def number_to_string(num, order): + l = orderlen(order) + fmt_str = "%0" + str(2*l) + "x" + string = binascii.unhexlify((fmt_str % num).encode()) + assert len(string) == l, (len(string), l) + return string + +def number_to_string_crop(num, order): + l = orderlen(order) + fmt_str = "%0" + str(2*l) + "x" + string = binascii.unhexlify((fmt_str % num).encode()) + return string[:l] + +def string_to_number(string): + return int(binascii.hexlify(string), 16) + +def string_to_number_fixedlen(string, order): + l = orderlen(order) + assert len(string) == l, (len(string), l) + return int(binascii.hexlify(string), 16) + +# these methods are useful for the sigencode= argument to SK.sign() and the +# sigdecode= argument to VK.verify(), and control how the signature is packed +# or unpacked. + +def sigencode_strings(r, s, order): + r_str = number_to_string(r, order) + s_str = number_to_string(s, order) + return (r_str, s_str) + +def sigencode_string(r, s, order): + # for any given curve, the size of the signature numbers is + # fixed, so just use simple concatenation + r_str, s_str = sigencode_strings(r, s, order) + return r_str + s_str + +def sigencode_der(r, s, order): + return der.encode_sequence(der.encode_integer(r), der.encode_integer(s)) + +# canonical versions of sigencode methods +# these enforce low S values, by negating the value (modulo the order) if above order/2 +# see CECKey::Sign() https://github.com/bitcoin/bitcoin/blob/master/src/key.cpp#L214 +def sigencode_strings_canonize(r, s, order): + if s > order / 2: + s = order - s + return sigencode_strings(r, s, order) + +def sigencode_string_canonize(r, s, order): + if s > order / 2: + s = order - s + return sigencode_string(r, s, order) + +def sigencode_der_canonize(r, s, order): + if s > order / 2: + s = order - s + return sigencode_der(r, s, order) + + +def sigdecode_string(signature, order): + l = orderlen(order) + assert len(signature) == 2*l, (len(signature), 2*l) + r = string_to_number_fixedlen(signature[:l], order) + s = string_to_number_fixedlen(signature[l:], order) + return r, s + +def sigdecode_strings(rs_strings, order): + (r_str, s_str) = rs_strings + l = orderlen(order) + assert len(r_str) == l, (len(r_str), l) + assert len(s_str) == l, (len(s_str), l) + r = string_to_number_fixedlen(r_str, order) + s = string_to_number_fixedlen(s_str, order) + return r, s + +def sigdecode_der(sig_der, order): + #return der.encode_sequence(der.encode_integer(r), der.encode_integer(s)) + rs_strings, empty = der.remove_sequence(sig_der) + if empty != b(""): + raise der.UnexpectedDER("trailing junk after DER sig: %s" % + binascii.hexlify(empty)) + r, rest = der.remove_integer(rs_strings) + s, empty = der.remove_integer(rest) + if empty != b(""): + raise der.UnexpectedDER("trailing junk after DER numbers: %s" % + binascii.hexlify(empty)) + return r, s + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabfile/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabfile/__init__.py new file mode 100644 index 0000000..41c0969 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabfile/__init__.py @@ -0,0 +1,45 @@ +""" +Fabric's own fabfile. +""" + +from __future__ import with_statement + +import nose + +from fabric.api import abort, local, task + +import tag +from utils import msg + + +@task(default=True) +def test(args=None): + """ + Run all unit tests and doctests. + + Specify string argument ``args`` for additional args to ``nosetests``. + """ + # Default to explicitly targeting the 'tests' folder, but only if nothing + # is being overridden. + tests = "" if args else " tests" + default_args = "-sv --with-doctest --nologcapture --with-color %s" % tests + default_args += (" " + args) if args else "" + nose.core.run_exit(argv=[''] + default_args.split()) + + +@task +def upload(): + """ + Build, register and upload to PyPI + """ + with msg("Uploading to PyPI"): + local('python setup.py sdist register upload') + + +@task +def release(force='no'): + """ + Tag, push tag to Github, & upload new version to PyPI. + """ + tag.tag(force=force, push='yes') + upload() diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabfile/tag.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabfile/tag.py new file mode 100644 index 0000000..5f6e33e --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabfile/tag.py @@ -0,0 +1,119 @@ +from __future__ import with_statement + +from contextlib import nested + +from fabric.api import abort, hide, local, settings, task + +# Need to import this as fabric.version for reload() purposes +import fabric.version +# But nothing is stopping us from making a convenient binding! +_version = fabric.version.get_version + +from utils import msg + + +def _seek_version(cmd, txt): + with nested(hide('running'), msg(txt)): + cmd = cmd % _version('short') + return local(cmd, capture=True) + + +def current_version_is_tagged(): + return _seek_version( + 'git tag | egrep "^%s$"', + "Searching for existing tag" + ) + + +def current_version_is_changelogged(filename): + return _seek_version( + 'egrep "^\* :release:\`%%s " %s' % filename, + "Looking for changelog entry" + ) + + +def update_code(filename, force): + """ + Update version data structure in-code and commit that change to git. + + Normally, if the version file has not been modified, we abort assuming the + user quit without saving. Specify ``force=yes`` to override this. + """ + raw_input("Version update in %r required! Press Enter to load $EDITOR." % filename) + with hide('running'): + local("$EDITOR %s" % filename) + # Try to detect whether user bailed out of the edit + with hide('running'): + has_diff = local("git diff -- %s" % filename, capture=True) + if not has_diff and not force: + abort("You seem to have aborted the file edit, so I'm aborting too.") + return filename + + +def commits_since_last_tag(): + """ + Has any work been done since the last tag? + """ + with hide('running'): + return local("git log %s.." % _version('short'), capture=True) + + +@task(default=True) +def tag(force='no', push='no'): + """ + Tag a new release. + + Normally, if a Git tag exists matching the current version, and no Git + commits appear after that tag, we abort assuming the user is making a + mistake or forgot to commit their work. + + To override this -- i.e. to re-tag and re-upload -- specify ``force=yes``. + We assume you know what you're doing if you use this. + + By default we do not push the tag remotely; specify ``push=yes`` to force a + ``git push origin ``. + """ + force = force.lower() in ['y', 'yes'] + with settings(warn_only=True): + changed = [] + # Does the current in-code version exist as a Git tag already? + # If so, this means we haven't updated the in-code version specifier + # yet, and need to do so. + if current_version_is_tagged(): + # That is, if any work has been done since. Sanity check! + if not commits_since_last_tag() and not force: + abort("No work done since last tag!") + # Open editor, update version + version_file = "fabric/version.py" + changed.append(update_code(version_file, force)) + # If the tag doesn't exist, the user has already updated version info + # and we can just move on. + else: + print("Version has already been updated, no need to edit...") + # Similar process but for the changelog. + changelog = "docs/changelog.rst" + if not current_version_is_changelogged(changelog): + changed.append(update_code(changelog, force)) + else: + print("Changelog already updated, no need to edit...") + # Commit any changes + if changed: + with msg("Committing updated version and/or changelog"): + reload(fabric.version) + local("git add %s" % " ".join(changed)) + local("git commit -m \"Cut %s\"" % _version('verbose')) + local("git push") + + # At this point, we've incremented the in-code version and just need to + # tag it in Git. + f = 'f' if force else '' + with msg("Tagging"): + local("git tag -%sam \"Fabric %s\" %s" % ( + f, + _version('normal'), + _version('short') + )) + # And push to the central server, if we were told to + if push.lower() in ['y', 'yes']: + with msg("Pushing"): + local("git push origin %s" % _version('short')) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabfile/utils.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabfile/utils.py new file mode 100644 index 0000000..94dee59 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabfile/utils.py @@ -0,0 +1,13 @@ +from __future__ import with_statement + +from contextlib import contextmanager + +from fabric.api import hide, puts + + +@contextmanager +def msg(txt): + puts(txt + "...", end='', flush=True) + with hide('everything'): + yield + puts("done.", show_prefix=False, flush=True) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/__init__.py new file mode 100644 index 0000000..f95214a --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/__init__.py @@ -0,0 +1,3 @@ +""" +See `fabric.api` for the publically importable API. +""" diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/api.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/api.py new file mode 100644 index 0000000..6552613 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/api.py @@ -0,0 +1,17 @@ +""" +Non-init module for doing convenient * imports from. + +Necessary because if we did this in __init__, one would be unable to import +anything else inside the package -- like, say, the version number used in +setup.py -- without triggering loads of most of the code. Which doesn't work so +well when you're using setup.py to install e.g. ssh! +""" +from fabric.context_managers import (cd, hide, settings, show, path, prefix, + lcd, quiet, warn_only, remote_tunnel, shell_env) +from fabric.decorators import (hosts, roles, runs_once, with_settings, task, + serial, parallel) +from fabric.operations import (require, prompt, put, get, run, sudo, local, + reboot, open_shell) +from fabric.state import env, output +from fabric.utils import abort, warn, puts, fastprint +from fabric.tasks import execute diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/auth.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/auth.py new file mode 100644 index 0000000..50ded27 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/auth.py @@ -0,0 +1,17 @@ +""" +Common authentication subroutines. Primarily for internal use. +""" + + +def get_password(user, host, port): + from fabric.state import env + from fabric.network import join_host_strings + host_string = join_host_strings(user, host, port) + return env.passwords.get(host_string, env.password) + + +def set_password(user, host, port, password): + from fabric.state import env + from fabric.network import join_host_strings + host_string = join_host_strings(user, host, port) + env.password = env.passwords[host_string] = password diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/colors.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/colors.py new file mode 100644 index 0000000..88736fb --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/colors.py @@ -0,0 +1,43 @@ +""" +.. versionadded:: 0.9.2 + +Functions for wrapping strings in ANSI color codes. + +Each function within this module returns the input string ``text``, wrapped +with ANSI color codes for the appropriate color. + +For example, to print some text as green on supporting terminals:: + + from fabric.colors import green + + print(green("This text is green!")) + +Because these functions simply return modified strings, you can nest them:: + + from fabric.colors import red, green + + print(red("This sentence is red, except for " + \ + green("these words, which are green") + ".")) + +If ``bold`` is set to ``True``, the ANSI flag for bolding will be flipped on +for that particular invocation, which usually shows up as a bold or brighter +version of the original color on most terminals. +""" + + +def _wrap_with(code): + + def inner(text, bold=False): + c = code + if bold: + c = "1;%s" % c + return "\033[%sm%s\033[0m" % (c, text) + return inner + +red = _wrap_with('31') +green = _wrap_with('32') +yellow = _wrap_with('33') +blue = _wrap_with('34') +magenta = _wrap_with('35') +cyan = _wrap_with('36') +white = _wrap_with('37') diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/context_managers.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/context_managers.py new file mode 100644 index 0000000..876a851 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/context_managers.py @@ -0,0 +1,602 @@ +""" +Context managers for use with the ``with`` statement. + +.. note:: When using Python 2.5, you will need to start your fabfile + with ``from __future__ import with_statement`` in order to make use of + the ``with`` statement (which is a regular, non ``__future__`` feature of + Python 2.6+.) + +.. note:: If you are using multiple directly nested ``with`` statements, it can + be convenient to use multiple context expressions in one single with + statement. Instead of writing:: + + with cd('/path/to/app'): + with prefix('workon myvenv'): + run('./manage.py syncdb') + run('./manage.py loaddata myfixture') + + you can write:: + + with cd('/path/to/app'), prefix('workon myvenv'): + run('./manage.py syncdb') + run('./manage.py loaddata myfixture') + + Note that you need Python 2.7+ for this to work. On Python 2.5 or 2.6, you + can do the following:: + + from contextlib import nested + + with nested(cd('/path/to/app'), prefix('workon myvenv')): + ... + + Finally, note that `~fabric.context_managers.settings` implements + ``nested`` itself -- see its API doc for details. +""" + +from contextlib import contextmanager, nested +import sys +import socket +import select + +from fabric.thread_handling import ThreadHandler +from fabric.state import output, win32, connections, env +from fabric import state + +if not win32: + import termios + import tty + + +def _set_output(groups, which): + """ + Refactored subroutine used by ``hide`` and ``show``. + """ + try: + # Preserve original values, pull in new given value to use + previous = {} + for group in output.expand_aliases(groups): + previous[group] = output[group] + output[group] = which + # Yield control + yield + finally: + # Restore original values + output.update(previous) + + +def documented_contextmanager(func): + wrapper = contextmanager(func) + wrapper.undecorated = func + return wrapper + + +@documented_contextmanager +def show(*groups): + """ + Context manager for setting the given output ``groups`` to True. + + ``groups`` must be one or more strings naming the output groups defined in + `~fabric.state.output`. The given groups will be set to True for the + duration of the enclosed block, and restored to their previous value + afterwards. + + For example, to turn on debug output (which is typically off by default):: + + def my_task(): + with show('debug'): + run('ls /var/www') + + As almost all output groups are displayed by default, `show` is most useful + for turning on the normally-hidden ``debug`` group, or when you know or + suspect that code calling your own code is trying to hide output with + `hide`. + """ + return _set_output(groups, True) + + +@documented_contextmanager +def hide(*groups): + """ + Context manager for setting the given output ``groups`` to False. + + ``groups`` must be one or more strings naming the output groups defined in + `~fabric.state.output`. The given groups will be set to False for the + duration of the enclosed block, and restored to their previous value + afterwards. + + For example, to hide the "[hostname] run:" status lines, as well as + preventing printout of stdout and stderr, one might use `hide` as follows:: + + def my_task(): + with hide('running', 'stdout', 'stderr'): + run('ls /var/www') + """ + return _set_output(groups, False) + + +@documented_contextmanager +def _setenv(variables): + """ + Context manager temporarily overriding ``env`` with given key/value pairs. + + A callable that returns a dict can also be passed. This is necessary when + new values are being calculated from current values, in order to ensure that + the "current" value is current at the time that the context is entered, not + when the context manager is initialized. (See Issue #736.) + + This context manager is used internally by `settings` and is not intended + to be used directly. + """ + if callable(variables): + variables = variables() + clean_revert = variables.pop('clean_revert', False) + previous = {} + new = [] + for key, value in variables.iteritems(): + if key in state.env: + previous[key] = state.env[key] + else: + new.append(key) + state.env[key] = value + try: + yield + finally: + if clean_revert: + for key, value in variables.iteritems(): + # If the current env value for this key still matches the + # value we set it to beforehand, we are OK to revert it to the + # pre-block value. + if key in state.env and value == state.env[key]: + if key in previous: + state.env[key] = previous[key] + else: + del state.env[key] + else: + state.env.update(previous) + for key in new: + del state.env[key] + + +def settings(*args, **kwargs): + """ + Nest context managers and/or override ``env`` variables. + + `settings` serves two purposes: + + * Most usefully, it allows temporary overriding/updating of ``env`` with + any provided keyword arguments, e.g. ``with settings(user='foo'):``. + Original values, if any, will be restored once the ``with`` block closes. + + * The keyword argument ``clean_revert`` has special meaning for + ``settings`` itself (see below) and will be stripped out before + execution. + + * In addition, it will use `contextlib.nested`_ to nest any given + non-keyword arguments, which should be other context managers, e.g. + ``with settings(hide('stderr'), show('stdout')):``. + + .. _contextlib.nested: http://docs.python.org/library/contextlib.html#contextlib.nested + + These behaviors may be specified at the same time if desired. An example + will hopefully illustrate why this is considered useful:: + + def my_task(): + with settings( + hide('warnings', 'running', 'stdout', 'stderr'), + warn_only=True + ): + if run('ls /etc/lsb-release'): + return 'Ubuntu' + elif run('ls /etc/redhat-release'): + return 'RedHat' + + The above task executes a `run` statement, but will warn instead of + aborting if the ``ls`` fails, and all output -- including the warning + itself -- is prevented from printing to the user. The end result, in this + scenario, is a completely silent task that allows the caller to figure out + what type of system the remote host is, without incurring the handful of + output that would normally occur. + + Thus, `settings` may be used to set any combination of environment + variables in tandem with hiding (or showing) specific levels of output, or + in tandem with any other piece of Fabric functionality implemented as a + context manager. + + If ``clean_revert`` is set to ``True``, ``settings`` will **not** revert + keys which are altered within the nested block, instead only reverting keys + whose values remain the same as those given. More examples will make this + clear; below is how ``settings`` operates normally:: + + # Before the block, env.parallel defaults to False, host_string to None + with settings(parallel=True, host_string='myhost'): + # env.parallel is True + # env.host_string is 'myhost' + env.host_string = 'otherhost' + # env.host_string is now 'otherhost' + # Outside the block: + # * env.parallel is False again + # * env.host_string is None again + + The internal modification of ``env.host_string`` is nullified -- not always + desirable. That's where ``clean_revert`` comes in:: + + # Before the block, env.parallel defaults to False, host_string to None + with settings(parallel=True, host_string='myhost', clean_revert=True): + # env.parallel is True + # env.host_string is 'myhost' + env.host_string = 'otherhost' + # env.host_string is now 'otherhost' + # Outside the block: + # * env.parallel is False again + # * env.host_string remains 'otherhost' + + Brand new keys which did not exist in ``env`` prior to using ``settings`` + are also preserved if ``clean_revert`` is active. When ``False``, such keys + are removed when the block exits. + + .. versionadded:: 1.4.1 + The ``clean_revert`` kwarg. + """ + managers = list(args) + if kwargs: + managers.append(_setenv(kwargs)) + return nested(*managers) + + +def cd(path): + """ + Context manager that keeps directory state when calling remote operations. + + Any calls to `run`, `sudo`, `get`, or `put` within the wrapped block will + implicitly have a string similar to ``"cd && "`` prefixed in order + to give the sense that there is actually statefulness involved. + + .. note:: + `cd` only affects *remote* paths -- to modify *local* paths, use + `~fabric.context_managers.lcd`. + + Because use of `cd` affects all such invocations, any code making use of + those operations, such as much of the ``contrib`` section, will also be + affected by use of `cd`. + + Like the actual 'cd' shell builtin, `cd` may be called with relative paths + (keep in mind that your default starting directory is your remote user's + ``$HOME``) and may be nested as well. + + Below is a "normal" attempt at using the shell 'cd', which doesn't work due + to how shell-less SSH connections are implemented -- state is **not** kept + between invocations of `run` or `sudo`:: + + run('cd /var/www') + run('ls') + + The above snippet will list the contents of the remote user's ``$HOME`` + instead of ``/var/www``. With `cd`, however, it will work as expected:: + + with cd('/var/www'): + run('ls') # Turns into "cd /var/www && ls" + + Finally, a demonstration (see inline comments) of nesting:: + + with cd('/var/www'): + run('ls') # cd /var/www && ls + with cd('website1'): + run('ls') # cd /var/www/website1 && ls + + .. note:: + + This context manager is currently implemented by appending to (and, as + always, restoring afterwards) the current value of an environment + variable, ``env.cwd``. However, this implementation may change in the + future, so we do not recommend manually altering ``env.cwd`` -- only + the *behavior* of `cd` will have any guarantee of backwards + compatibility. + + .. note:: + + Space characters will be escaped automatically to make dealing with + such directory names easier. + + .. versionchanged:: 1.0 + Applies to `get` and `put` in addition to the command-running + operations. + + .. seealso:: `~fabric.context_managers.lcd` + """ + return _change_cwd('cwd', path) + + +def lcd(path): + """ + Context manager for updating local current working directory. + + This context manager is identical to `~fabric.context_managers.cd`, except + that it changes a different env var (`lcwd`, instead of `cwd`) and thus + only affects the invocation of `~fabric.operations.local` and the local + arguments to `~fabric.operations.get`/`~fabric.operations.put`. + + Relative path arguments are relative to the local user's current working + directory, which will vary depending on where Fabric (or Fabric-using code) + was invoked. You can check what this is with `os.getcwd + `_. It may be + useful to pin things relative to the location of the fabfile in use, which + may be found in :ref:`env.real_fabfile ` + + .. versionadded:: 1.0 + """ + return _change_cwd('lcwd', path) + + +def _change_cwd(which, path): + path = path.replace(' ', '\ ') + if state.env.get(which) and not path.startswith('/') and not path.startswith('~'): + new_cwd = state.env.get(which) + '/' + path + else: + new_cwd = path + return _setenv({which: new_cwd}) + + +def path(path, behavior='append'): + """ + Append the given ``path`` to the PATH used to execute any wrapped commands. + + Any calls to `run` or `sudo` within the wrapped block will implicitly have + a string similar to ``"PATH=$PATH: "`` prepended before the given + command. + + You may customize the behavior of `path` by specifying the optional + ``behavior`` keyword argument, as follows: + + * ``'append'``: append given path to the current ``$PATH``, e.g. + ``PATH=$PATH:``. This is the default behavior. + * ``'prepend'``: prepend given path to the current ``$PATH``, e.g. + ``PATH=:$PATH``. + * ``'replace'``: ignore previous value of ``$PATH`` altogether, e.g. + ``PATH=``. + + .. note:: + + This context manager is currently implemented by modifying (and, as + always, restoring afterwards) the current value of environment + variables, ``env.path`` and ``env.path_behavior``. However, this + implementation may change in the future, so we do not recommend + manually altering them directly. + + .. versionadded:: 1.0 + """ + return _setenv({'path': path, 'path_behavior': behavior}) + + +def prefix(command): + """ + Prefix all wrapped `run`/`sudo` commands with given command plus ``&&``. + + This is nearly identical to `~fabric.operations.cd`, except that nested + invocations append to a list of command strings instead of modifying a + single string. + + Most of the time, you'll want to be using this alongside a shell script + which alters shell state, such as ones which export or alter shell + environment variables. + + For example, one of the most common uses of this tool is with the + ``workon`` command from `virtualenvwrapper + `_:: + + with prefix('workon myvenv'): + run('./manage.py syncdb') + + In the above snippet, the actual shell command run would be this:: + + $ workon myvenv && ./manage.py syncdb + + This context manager is compatible with `~fabric.context_managers.cd`, so + if your virtualenv doesn't ``cd`` in its ``postactivate`` script, you could + do the following:: + + with cd('/path/to/app'): + with prefix('workon myvenv'): + run('./manage.py syncdb') + run('./manage.py loaddata myfixture') + + Which would result in executions like so:: + + $ cd /path/to/app && workon myvenv && ./manage.py syncdb + $ cd /path/to/app && workon myvenv && ./manage.py loaddata myfixture + + Finally, as alluded to near the beginning, + `~fabric.context_managers.prefix` may be nested if desired, e.g.:: + + with prefix('workon myenv'): + run('ls') + with prefix('source /some/script'): + run('touch a_file') + + The result:: + + $ workon myenv && ls + $ workon myenv && source /some/script && touch a_file + + Contrived, but hopefully illustrative. + """ + return _setenv(lambda: {'command_prefixes': state.env.command_prefixes + [command]}) + + +@documented_contextmanager +def char_buffered(pipe): + """ + Force local terminal ``pipe`` be character, not line, buffered. + + Only applies on Unix-based systems; on Windows this is a no-op. + """ + if win32 or not pipe.isatty(): + yield + else: + old_settings = termios.tcgetattr(pipe) + tty.setcbreak(pipe) + try: + yield + finally: + termios.tcsetattr(pipe, termios.TCSADRAIN, old_settings) + + +def shell_env(**kw): + """ + Set shell environment variables for wrapped commands. + + For example, the below shows how you might set a ZeroMQ related environment + variable when installing a Python ZMQ library:: + + with shell_env(ZMQ_DIR='/home/user/local'): + run('pip install pyzmq') + + As with `~fabric.context_managers.prefix`, this effectively turns the + ``run`` command into:: + + $ export ZMQ_DIR='/home/user/local' && pip install pyzmq + + Multiple key-value pairs may be given simultaneously. + + .. note:: + If used to affect the behavior of `~fabric.operations.local` when + running from a Windows localhost, ``SET`` commands will be used to + implement this feature. + """ + return _setenv({'shell_env': kw}) + + +def _forwarder(chan, sock): + # Bidirectionally forward data between a socket and a Paramiko channel. + while True: + r, w, x = select.select([sock, chan], [], []) + if sock in r: + data = sock.recv(1024) + if len(data) == 0: + break + chan.send(data) + if chan in r: + data = chan.recv(1024) + if len(data) == 0: + break + sock.send(data) + chan.close() + sock.close() + + +@documented_contextmanager +def remote_tunnel(remote_port, local_port=None, local_host="localhost", + remote_bind_address="127.0.0.1"): + """ + Create a tunnel forwarding a locally-visible port to the remote target. + + For example, you can let the remote host access a database that is + installed on the client host:: + + # Map localhost:6379 on the server to localhost:6379 on the client, + # so that the remote 'redis-cli' program ends up speaking to the local + # redis-server. + with remote_tunnel(6379): + run("redis-cli -i") + + The database might be installed on a client only reachable from the client + host (as opposed to *on* the client itself):: + + # Map localhost:6379 on the server to redis.internal:6379 on the client + with remote_tunnel(6379, local_host="redis.internal") + run("redis-cli -i") + + ``remote_tunnel`` accepts up to four arguments: + + * ``remote_port`` (mandatory) is the remote port to listen to. + * ``local_port`` (optional) is the local port to connect to; the default is + the same port as the remote one. + * ``local_host`` (optional) is the locally-reachable computer (DNS name or + IP address) to connect to; the default is ``localhost`` (that is, the + same computer Fabric is running on). + * ``remote_bind_address`` (optional) is the remote IP address to bind to + for listening, on the current target. It should be an IP address assigned + to an interface on the target (or a DNS name that resolves to such IP). + You can use "0.0.0.0" to bind to all interfaces. + + .. note:: + By default, most SSH servers only allow remote tunnels to listen to the + localhost interface (127.0.0.1). In these cases, `remote_bind_address` + is ignored by the server, and the tunnel will listen only to 127.0.0.1. + + .. versionadded: 1.6 + """ + if local_port is None: + local_port = remote_port + + sockets = [] + channels = [] + threads = [] + + def accept(channel, (src_addr, src_port), (dest_addr, dest_port)): + channels.append(channel) + sock = socket.socket() + sockets.append(sock) + + try: + sock.connect((local_host, local_port)) + except Exception, e: + print "[%s] rtunnel: cannot connect to %s:%d (from local)" % (env.host_string, local_host, local_port) + chan.close() + return + + print "[%s] rtunnel: opened reverse tunnel: %r -> %r -> %r"\ + % (env.host_string, channel.origin_addr, + channel.getpeername(), (local_host, local_port)) + + th = ThreadHandler('fwd', _forwarder, channel, sock) + threads.append(th) + + transport = connections[env.host_string].get_transport() + transport.request_port_forward(remote_bind_address, remote_port, handler=accept) + + try: + yield + finally: + for sock, chan, th in zip(sockets, channels, threads): + sock.close() + chan.close() + th.thread.join() + th.raise_if_needed() + transport.cancel_port_forward(remote_bind_address, remote_port) + + + +quiet = lambda: settings(hide('everything'), warn_only=True) +quiet.__doc__ = """ + Alias to ``settings(hide('everything'), warn_only=True)``. + + Useful for wrapping remote interrogative commands which you expect to fail + occasionally, and/or which you want to silence. + + Example:: + + with quiet(): + have_build_dir = run("test -e /tmp/build").succeeded + + When used in a task, the above snippet will not produce any ``run: test -e + /tmp/build`` line, nor will any stdout/stderr display, and command failure + is ignored. + + .. seealso:: + :ref:`env.warn_only `, + `~fabric.context_managers.settings`, + `~fabric.context_managers.hide` + + .. versionadded:: 1.5 +""" + + +warn_only = lambda: settings(warn_only=True) +warn_only.__doc__ = """ + Alias to ``settings(warn_only=True)``. + + .. seealso:: + :ref:`env.warn_only `, + `~fabric.context_managers.settings`, + `~fabric.context_managers.quiet` +""" diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/contrib/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/contrib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/contrib/console.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/contrib/console.py new file mode 100644 index 0000000..5a0d5fd --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/contrib/console.py @@ -0,0 +1,38 @@ +""" +Console/terminal user interface functionality. +""" + +from fabric.api import prompt + + +def confirm(question, default=True): + """ + Ask user a yes/no question and return their response as True or False. + + ``question`` should be a simple, grammatically complete question such as + "Do you wish to continue?", and will have a string similar to " [Y/n] " + appended automatically. This function will *not* append a question mark for + you. + + By default, when the user presses Enter without typing anything, "yes" is + assumed. This can be changed by specifying ``default=False``. + """ + # Set up suffix + if default: + suffix = "Y/n" + else: + suffix = "y/N" + # Loop till we get something we like + while True: + response = prompt("%s [%s] " % (question, suffix)).lower() + # Default + if not response: + return default + # Yes + if response in ['y', 'yes']: + return True + # No + if response in ['n', 'no']: + return False + # Didn't get empty, yes or no, so complain and loop + print("I didn't understand you. Please specify '(y)es' or '(n)o'.") diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/contrib/django.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/contrib/django.py new file mode 100644 index 0000000..d256cbf --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/contrib/django.py @@ -0,0 +1,94 @@ +""" +.. versionadded:: 0.9.2 + +These functions streamline the process of initializing Django's settings module +environment variable. Once this is done, your fabfile may import from your +Django project, or Django itself, without requiring the use of ``manage.py`` +plugins or having to set the environment variable yourself every time you use +your fabfile. + +Currently, these functions only allow Fabric to interact with +local-to-your-fabfile Django installations. This is not as limiting as it +sounds; for example, you can use Fabric as a remote "build" tool as well as +using it locally. Imagine the following fabfile:: + + from fabric.api import run, local, hosts, cd + from fabric.contrib import django + + django.project('myproject') + from myproject.myapp.models import MyModel + + def print_instances(): + for instance in MyModel.objects.all(): + print(instance) + + @hosts('production-server') + def print_production_instances(): + with cd('/path/to/myproject'): + run('fab print_instances') + +With Fabric installed on both ends, you could execute +``print_production_instances`` locally, which would trigger ``print_instances`` +on the production server -- which would then be interacting with your +production Django database. + +As another example, if your local and remote settings are similar, you can use +it to obtain e.g. your database settings, and then use those when executing a +remote (non-Fabric) command. This would allow you some degree of freedom even +if Fabric is only installed locally:: + + from fabric.api import run + from fabric.contrib import django + + django.settings_module('myproject.settings') + from django.conf import settings + + def dump_production_database(): + run('mysqldump -u %s -p=%s %s > /tmp/prod-db.sql' % ( + settings.DATABASE_USER, + settings.DATABASE_PASSWORD, + settings.DATABASE_NAME + )) + +The above snippet will work if run from a local, development environment, again +provided your local ``settings.py`` mirrors your remote one in terms of +database connection info. +""" + +import os + + +def settings_module(module): + """ + Set ``DJANGO_SETTINGS_MODULE`` shell environment variable to ``module``. + + Due to how Django works, imports from Django or a Django project will fail + unless the shell environment variable ``DJANGO_SETTINGS_MODULE`` is + correctly set (see `the Django settings docs + `_.) + + This function provides a shortcut for doing so; call it near the top of + your fabfile or Fabric-using code, after which point any Django imports + should work correctly. + + .. note:: + + This function sets a **shell** environment variable (via + ``os.environ``) and is unrelated to Fabric's own internal "env" + variables. + """ + os.environ['DJANGO_SETTINGS_MODULE'] = module + + +def project(name): + """ + Sets ``DJANGO_SETTINGS_MODULE`` to ``'.settings'``. + + This function provides a handy shortcut for the common case where one is + using the Django default naming convention for their settings file and + location. + + Uses `settings_module` -- see its documentation for details on why and how + to use this functionality. + """ + settings_module('%s.settings' % name) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/contrib/files.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/contrib/files.py new file mode 100644 index 0000000..0ff6aff --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/contrib/files.py @@ -0,0 +1,423 @@ +""" +Module providing easy API for working with remote files and folders. +""" + +from __future__ import with_statement + +import hashlib +import tempfile +import re +import os +from StringIO import StringIO +from functools import partial + +from fabric.api import * +from fabric.utils import apply_lcwd + + +def exists(path, use_sudo=False, verbose=False): + """ + Return True if given path exists on the current remote host. + + If ``use_sudo`` is True, will use `sudo` instead of `run`. + + `exists` will, by default, hide all output (including the run line, stdout, + stderr and any warning resulting from the file not existing) in order to + avoid cluttering output. You may specify ``verbose=True`` to change this + behavior. + """ + func = use_sudo and sudo or run + cmd = 'test -e %s' % _expand_path(path) + # If verbose, run normally + if verbose: + with settings(warn_only=True): + return not func(cmd).failed + # Otherwise, be quiet + with settings(hide('everything'), warn_only=True): + return not func(cmd).failed + + +def is_link(path, use_sudo=False, verbose=False): + """ + Return True if the given path is a symlink on the current remote host. + + If ``use_sudo`` is True, will use `.sudo` instead of `.run`. + + `.is_link` will, by default, hide all output. Give ``verbose=True`` to change this. + """ + func = sudo if use_sudo else run + cmd = 'test -L "$(echo %s)"' % path + args, kwargs = [], {'warn_only': True} + if not verbose: + opts = [hide('everything')] + with settings(*args, **kwargs): + return func(cmd).succeeded + + +def first(*args, **kwargs): + """ + Given one or more file paths, returns first one found, or None if none + exist. May specify ``use_sudo`` and ``verbose`` which are passed to `exists`. + """ + for directory in args: + if exists(directory, **kwargs): + return directory + + +def upload_template(filename, destination, context=None, use_jinja=False, + template_dir=None, use_sudo=False, backup=True, mirror_local_mode=False, + mode=None, pty=None): + """ + Render and upload a template text file to a remote host. + + Returns the result of the inner call to `~fabric.operations.put` -- see its + documentation for details. + + ``filename`` should be the path to a text file, which may contain `Python + string interpolation formatting + `_ and will + be rendered with the given context dictionary ``context`` (if given.) + + Alternately, if ``use_jinja`` is set to True and you have the Jinja2 + templating library available, Jinja will be used to render the template + instead. Templates will be loaded from the invoking user's current working + directory by default, or from ``template_dir`` if given. + + The resulting rendered file will be uploaded to the remote file path + ``destination``. If the destination file already exists, it will be + renamed with a ``.bak`` extension unless ``backup=False`` is specified. + + By default, the file will be copied to ``destination`` as the logged-in + user; specify ``use_sudo=True`` to use `sudo` instead. + + The ``mirror_local_mode`` and ``mode`` kwargs are passed directly to an + internal `~fabric.operations.put` call; please see its documentation for + details on these two options. + + The ``pty`` kwarg will be passed verbatim to any internal + `~fabric.operations.run`/`~fabric.operations.sudo` calls, such as those + used for testing directory-ness, making backups, etc. + + .. versionchanged:: 1.1 + Added the ``backup``, ``mirror_local_mode`` and ``mode`` kwargs. + .. versionchanged:: 1.9 + Added the ``pty`` kwarg. + """ + func = use_sudo and sudo or run + if pty is not None: + func = partial(func, pty=pty) + # Normalize destination to be an actual filename, due to using StringIO + with settings(hide('everything'), warn_only=True): + if func('test -d %s' % _expand_path(destination)).succeeded: + sep = "" if destination.endswith('/') else "/" + destination += sep + os.path.basename(filename) + + # Use mode kwarg to implement mirror_local_mode, again due to using + # StringIO + if mirror_local_mode and mode is None: + mode = os.stat(filename).st_mode + # To prevent put() from trying to do this + # logic itself + mirror_local_mode = False + + # Process template + text = None + if use_jinja: + try: + template_dir = template_dir or os.getcwd() + template_dir = apply_lcwd(template_dir, env) + from jinja2 import Environment, FileSystemLoader + jenv = Environment(loader=FileSystemLoader(template_dir)) + text = jenv.get_template(filename).render(**context or {}) + # Force to a byte representation of Unicode, or str()ification + # within Paramiko's SFTP machinery may cause decode issues for + # truly non-ASCII characters. + text = text.encode('utf-8') + except ImportError: + import traceback + tb = traceback.format_exc() + abort(tb + "\nUnable to import Jinja2 -- see above.") + else: + filename = apply_lcwd(filename, env) + with open(os.path.expanduser(filename)) as inputfile: + text = inputfile.read() + if context: + text = text % context + + # Back up original file + if backup and exists(destination): + func("cp %s{,.bak}" % _expand_path(destination)) + + # Upload the file. + return put( + local_path=StringIO(text), + remote_path=destination, + use_sudo=use_sudo, + mirror_local_mode=mirror_local_mode, + mode=mode + ) + + +def sed(filename, before, after, limit='', use_sudo=False, backup='.bak', + flags='', shell=False): + """ + Run a search-and-replace on ``filename`` with given regex patterns. + + Equivalent to ``sed -i -r -e "// s///g" + ``. Setting ``backup`` to an empty string will, disable backup + file creation. + + For convenience, ``before`` and ``after`` will automatically escape forward + slashes, single quotes and parentheses for you, so you don't need to + specify e.g. ``http:\/\/foo\.com``, instead just using ``http://foo\.com`` + is fine. + + If ``use_sudo`` is True, will use `sudo` instead of `run`. + + The ``shell`` argument will be eventually passed to `run`/`sudo`. It + defaults to False in order to avoid problems with many nested levels of + quotes and backslashes. However, setting it to True may help when using + ``~fabric.operations.cd`` to wrap explicit or implicit ``sudo`` calls. + (``cd`` by it's nature is a shell built-in, not a standalone command, so it + should be called within a shell.) + + Other options may be specified with sed-compatible regex flags -- for + example, to make the search and replace case insensitive, specify + ``flags="i"``. The ``g`` flag is always specified regardless, so you do not + need to remember to include it when overriding this parameter. + + .. versionadded:: 1.1 + The ``flags`` parameter. + .. versionadded:: 1.6 + Added the ``shell`` keyword argument. + """ + func = use_sudo and sudo or run + # Characters to be escaped in both + for char in "/'": + before = before.replace(char, r'\%s' % char) + after = after.replace(char, r'\%s' % char) + # Characters to be escaped in replacement only (they're useful in regexen + # in the 'before' part) + for char in "()": + after = after.replace(char, r'\%s' % char) + if limit: + limit = r'/%s/ ' % limit + context = { + 'script': r"'%ss/%s/%s/%sg'" % (limit, before, after, flags), + 'filename': _expand_path(filename), + 'backup': backup + } + # Test the OS because of differences between sed versions + + with hide('running', 'stdout'): + platform = run("uname") + if platform in ('NetBSD', 'OpenBSD', 'QNX'): + # Attempt to protect against failures/collisions + hasher = hashlib.sha1() + hasher.update(env.host_string) + hasher.update(filename) + context['tmp'] = "/tmp/%s" % hasher.hexdigest() + # Use temp file to work around lack of -i + expr = r"""cp -p %(filename)s %(tmp)s \ +&& sed -r -e %(script)s %(filename)s > %(tmp)s \ +&& cp -p %(filename)s %(filename)s%(backup)s \ +&& mv %(tmp)s %(filename)s""" + else: + context['extended_regex'] = '-E' if platform == 'Darwin' else '-r' + expr = r"sed -i%(backup)s %(extended_regex)s -e %(script)s %(filename)s" + command = expr % context + return func(command, shell=shell) + + +def uncomment(filename, regex, use_sudo=False, char='#', backup='.bak', + shell=False): + """ + Attempt to uncomment all lines in ``filename`` matching ``regex``. + + The default comment delimiter is `#` and may be overridden by the ``char`` + argument. + + This function uses the `sed` function, and will accept the same + ``use_sudo``, ``shell`` and ``backup`` keyword arguments that `sed` does. + + `uncomment` will remove a single whitespace character following the comment + character, if it exists, but will preserve all preceding whitespace. For + example, ``# foo`` would become ``foo`` (the single space is stripped) but + `` # foo`` would become `` foo`` (the single space is still stripped, + but the preceding 4 spaces are not.) + + .. versionchanged:: 1.6 + Added the ``shell`` keyword argument. + """ + return sed( + filename, + before=r'^([[:space:]]*)%s[[:space:]]?' % char, + after=r'\1', + limit=regex, + use_sudo=use_sudo, + backup=backup, + shell=shell + ) + + +def comment(filename, regex, use_sudo=False, char='#', backup='.bak', + shell=False): + """ + Attempt to comment out all lines in ``filename`` matching ``regex``. + + The default commenting character is `#` and may be overridden by the + ``char`` argument. + + This function uses the `sed` function, and will accept the same + ``use_sudo``, ``shell`` and ``backup`` keyword arguments that `sed` does. + + `comment` will prepend the comment character to the beginning of the line, + so that lines end up looking like so:: + + this line is uncommented + #this line is commented + # this line is indented and commented + + In other words, comment characters will not "follow" indentation as they + sometimes do when inserted by hand. Neither will they have a trailing space + unless you specify e.g. ``char='# '``. + + .. note:: + + In order to preserve the line being commented out, this function will + wrap your ``regex`` argument in parentheses, so you don't need to. It + will ensure that any preceding/trailing ``^`` or ``$`` characters are + correctly moved outside the parentheses. For example, calling + ``comment(filename, r'^foo$')`` will result in a `sed` call with the + "before" regex of ``r'^(foo)$'`` (and the "after" regex, naturally, of + ``r'#\\1'``.) + + .. versionadded:: 1.5 + Added the ``shell`` keyword argument. + """ + carot, dollar = '', '' + if regex.startswith('^'): + carot = '^' + regex = regex[1:] + if regex.endswith('$'): + dollar = '$' + regex = regex[:-1] + regex = "%s(%s)%s" % (carot, regex, dollar) + return sed( + filename, + before=regex, + after=r'%s\1' % char, + use_sudo=use_sudo, + backup=backup, + shell=shell + ) + + +def contains(filename, text, exact=False, use_sudo=False, escape=True, + shell=False): + """ + Return True if ``filename`` contains ``text`` (which may be a regex.) + + By default, this function will consider a partial line match (i.e. where + ``text`` only makes up part of the line it's on). Specify ``exact=True`` to + change this behavior so that only a line containing exactly ``text`` + results in a True return value. + + This function leverages ``egrep`` on the remote end (so it may not follow + Python regular expression syntax perfectly), and skips ``env.shell`` + wrapper by default. + + If ``use_sudo`` is True, will use `sudo` instead of `run`. + + If ``escape`` is False, no extra regular expression related escaping is + performed (this includes overriding ``exact`` so that no ``^``/``$`` is + added.) + + The ``shell`` argument will be eventually passed to ``run/sudo``. See + description of the same argumnet in ``~fabric.contrib.sed`` for details. + + .. versionchanged:: 1.0 + Swapped the order of the ``filename`` and ``text`` arguments to be + consistent with other functions in this module. + .. versionchanged:: 1.4 + Updated the regular expression related escaping to try and solve + various corner cases. + .. versionchanged:: 1.4 + Added ``escape`` keyword argument. + .. versionadded:: 1.6 + Added the ``shell`` keyword argument. + """ + func = use_sudo and sudo or run + if escape: + text = _escape_for_regex(text) + if exact: + text = "^%s$" % text + with settings(hide('everything'), warn_only=True): + egrep_cmd = 'egrep "%s" %s' % (text, _expand_path(filename)) + return func(egrep_cmd, shell=shell).succeeded + + +def append(filename, text, use_sudo=False, partial=False, escape=True, + shell=False): + """ + Append string (or list of strings) ``text`` to ``filename``. + + When a list is given, each string inside is handled independently (but in + the order given.) + + If ``text`` is already found in ``filename``, the append is not run, and + None is returned immediately. Otherwise, the given text is appended to the + end of the given ``filename`` via e.g. ``echo '$text' >> $filename``. + + The test for whether ``text`` already exists defaults to a full line match, + e.g. ``^$``, as this seems to be the most sensible approach for the + "append lines to a file" use case. You may override this and force partial + searching (e.g. ``^``) by specifying ``partial=True``. + + Because ``text`` is single-quoted, single quotes will be transparently + backslash-escaped. This can be disabled with ``escape=False``. + + If ``use_sudo`` is True, will use `sudo` instead of `run`. + + The ``shell`` argument will be eventually passed to ``run/sudo``. See + description of the same argumnet in ``~fabric.contrib.sed`` for details. + + .. versionchanged:: 0.9.1 + Added the ``partial`` keyword argument. + .. versionchanged:: 1.0 + Swapped the order of the ``filename`` and ``text`` arguments to be + consistent with other functions in this module. + .. versionchanged:: 1.0 + Changed default value of ``partial`` kwarg to be ``False``. + .. versionchanged:: 1.4 + Updated the regular expression related escaping to try and solve + various corner cases. + .. versionadded:: 1.6 + Added the ``shell`` keyword argument. + """ + func = use_sudo and sudo or run + # Normalize non-list input to be a list + if isinstance(text, basestring): + text = [text] + for line in text: + regex = '^' + _escape_for_regex(line) + ('' if partial else '$') + if (exists(filename, use_sudo=use_sudo) and line + and contains(filename, regex, use_sudo=use_sudo, escape=False, + shell=shell)): + continue + line = line.replace("'", r"'\\''") if escape else line + func("echo '%s' >> %s" % (line, _expand_path(filename))) + +def _escape_for_regex(text): + """Escape ``text`` to allow literal matching using egrep""" + regex = re.escape(text) + # Seems like double escaping is needed for \ + regex = regex.replace('\\\\', '\\\\\\') + # Triple-escaping seems to be required for $ signs + regex = regex.replace(r'\$', r'\\\$') + # Whereas single quotes should not be escaped + regex = regex.replace(r"\'", "'") + return regex + +def _expand_path(path): + return '"$(echo %s)"' % path diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/contrib/project.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/contrib/project.py new file mode 100644 index 0000000..7d1bf13 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/contrib/project.py @@ -0,0 +1,200 @@ +""" +Useful non-core functionality, e.g. functions composing multiple operations. +""" +from __future__ import with_statement + +from os import getcwd, sep +import os.path +from datetime import datetime +from tempfile import mkdtemp + +from fabric.network import needs_host, key_filenames, normalize +from fabric.operations import local, run, sudo, put +from fabric.state import env, output +from fabric.context_managers import cd + +__all__ = ['rsync_project', 'upload_project'] + +@needs_host +def rsync_project( + remote_dir, + local_dir=None, + exclude=(), + delete=False, + extra_opts='', + ssh_opts='', + capture=False, + upload=True, + default_opts='-pthrvz' +): + """ + Synchronize a remote directory with the current project directory via rsync. + + Where ``upload_project()`` makes use of ``scp`` to copy one's entire + project every time it is invoked, ``rsync_project()`` uses the ``rsync`` + command-line utility, which only transfers files newer than those on the + remote end. + + ``rsync_project()`` is thus a simple wrapper around ``rsync``; for + details on how ``rsync`` works, please see its manpage. ``rsync`` must be + installed on both your local and remote systems in order for this operation + to work correctly. + + This function makes use of Fabric's ``local()`` operation, and returns the + output of that function call; thus it will return the stdout, if any, of + the resultant ``rsync`` call. + + ``rsync_project()`` takes the following parameters: + + * ``remote_dir``: the only required parameter, this is the path to the + directory on the remote server. Due to how ``rsync`` is implemented, the + exact behavior depends on the value of ``local_dir``: + + * If ``local_dir`` ends with a trailing slash, the files will be + dropped inside of ``remote_dir``. E.g. + ``rsync_project("/home/username/project", "foldername/")`` will drop + the contents of ``foldername`` inside of ``/home/username/project``. + * If ``local_dir`` does **not** end with a trailing slash (and this + includes the default scenario, when ``local_dir`` is not specified), + ``remote_dir`` is effectively the "parent" directory, and a new + directory named after ``local_dir`` will be created inside of it. So + ``rsync_project("/home/username", "foldername")`` would create a new + directory ``/home/username/foldername`` (if needed) and place the + files there. + + * ``local_dir``: by default, ``rsync_project`` uses your current working + directory as the source directory. This may be overridden by specifying + ``local_dir``, which is a string passed verbatim to ``rsync``, and thus + may be a single directory (``"my_directory"``) or multiple directories + (``"dir1 dir2"``). See the ``rsync`` documentation for details. + * ``exclude``: optional, may be a single string, or an iterable of strings, + and is used to pass one or more ``--exclude`` options to ``rsync``. + * ``delete``: a boolean controlling whether ``rsync``'s ``--delete`` option + is used. If True, instructs ``rsync`` to remove remote files that no + longer exist locally. Defaults to False. + * ``extra_opts``: an optional, arbitrary string which you may use to pass + custom arguments or options to ``rsync``. + * ``ssh_opts``: Like ``extra_opts`` but specifically for the SSH options + string (rsync's ``--rsh`` flag.) + * ``capture``: Sent directly into an inner `~fabric.operations.local` call. + * ``upload``: a boolean controlling whether file synchronization is + performed up or downstream. Upstream by default. + * ``default_opts``: the default rsync options ``-pthrvz``, override if + desired (e.g. to remove verbosity, etc). + + Furthermore, this function transparently honors Fabric's port and SSH key + settings. Calling this function when the current host string contains a + nonstandard port, or when ``env.key_filename`` is non-empty, will use the + specified port and/or SSH key filename(s). + + For reference, the approximate ``rsync`` command-line call that is + constructed by this function is the following:: + + rsync [--delete] [--exclude exclude[0][, --exclude[1][, ...]]] \\ + [default_opts] [extra_opts] : + + .. versionadded:: 1.4.0 + The ``ssh_opts`` keyword argument. + .. versionadded:: 1.4.1 + The ``capture`` keyword argument. + .. versionadded:: 1.8.0 + The ``default_opts`` keyword argument. + """ + # Turn single-string exclude into a one-item list for consistency + if not hasattr(exclude, '__iter__'): + exclude = (exclude,) + # Create --exclude options from exclude list + exclude_opts = ' --exclude "%s"' * len(exclude) + # Double-backslash-escape + exclusions = tuple([str(s).replace('"', '\\\\"') for s in exclude]) + # Honor SSH key(s) + key_string = "" + keys = key_filenames() + if keys: + key_string = "-i " + " -i ".join(keys) + # Port + user, host, port = normalize(env.host_string) + port_string = "-p %s" % port + # RSH + rsh_string = "" + rsh_parts = [key_string, port_string, ssh_opts] + if any(rsh_parts): + rsh_string = "--rsh='ssh %s'" % " ".join(rsh_parts) + # Set up options part of string + options_map = { + 'delete': '--delete' if delete else '', + 'exclude': exclude_opts % exclusions, + 'rsh': rsh_string, + 'default': default_opts, + 'extra': extra_opts, + } + options = "%(delete)s%(exclude)s %(default)s %(extra)s %(rsh)s" % options_map + # Get local directory + if local_dir is None: + local_dir = '../' + getcwd().split(sep)[-1] + # Create and run final command string + if host.count(':') > 1: + # Square brackets are mandatory for IPv6 rsync address, + # even if port number is not specified + remote_prefix = "[%s@%s]" % (user, host) + else: + remote_prefix = "%s@%s" % (user, host) + if upload: + cmd = "rsync %s %s %s:%s" % (options, local_dir, remote_prefix, remote_dir) + else: + cmd = "rsync %s %s:%s %s" % (options, remote_prefix, remote_dir, local_dir) + + if output.running: + print("[%s] rsync_project: %s" % (env.host_string, cmd)) + return local(cmd, capture=capture) + + +def upload_project(local_dir=None, remote_dir="", use_sudo=False): + """ + Upload the current project to a remote system via ``tar``/``gzip``. + + ``local_dir`` specifies the local project directory to upload, and defaults + to the current working directory. + + ``remote_dir`` specifies the target directory to upload into (meaning that + a copy of ``local_dir`` will appear as a subdirectory of ``remote_dir``) + and defaults to the remote user's home directory. + + ``use_sudo`` specifies which method should be used when executing commands + remotely. ``sudo`` will be used if use_sudo is True, otherwise ``run`` will + be used. + + This function makes use of the ``tar`` and ``gzip`` programs/libraries, + thus it will not work too well on Win32 systems unless one is using Cygwin + or something similar. It will attempt to clean up the local and remote + tarfiles when it finishes executing, even in the event of a failure. + + .. versionchanged:: 1.1 + Added the ``local_dir`` and ``remote_dir`` kwargs. + + .. versionchanged:: 1.7 + Added the ``use_sudo`` kwarg. + """ + runner = use_sudo and sudo or run + + local_dir = local_dir or os.getcwd() + + # Remove final '/' in local_dir so that basename() works + local_dir = local_dir.rstrip(os.sep) + + local_path, local_name = os.path.split(local_dir) + tar_file = "%s.tar.gz" % local_name + target_tar = os.path.join(remote_dir, tar_file) + tmp_folder = mkdtemp() + + try: + tar_path = os.path.join(tmp_folder, tar_file) + local("tar -czf %s -C %s %s" % (tar_path, local_path, local_name)) + put(tar_path, target_tar, use_sudo=use_sudo) + with cd(remote_dir): + try: + runner("tar -xzf %s" % tar_file) + finally: + runner("rm -f %s" % tar_file) + finally: + local("rm -rf %s" % tmp_folder) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/decorators.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/decorators.py new file mode 100644 index 0000000..78e5bfd --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/decorators.py @@ -0,0 +1,218 @@ +""" +Convenience decorators for use in fabfiles. +""" +from __future__ import with_statement + +import types +from functools import wraps + +from Crypto import Random + +from fabric import tasks +from .context_managers import settings + + +def task(*args, **kwargs): + """ + Decorator declaring the wrapped function to be a new-style task. + + May be invoked as a simple, argument-less decorator (i.e. ``@task``) or + with arguments customizing its behavior (e.g. ``@task(alias='myalias')``). + + Please see the :ref:`new-style task ` documentation for + details on how to use this decorator. + + .. versionchanged:: 1.2 + Added the ``alias``, ``aliases``, ``task_class`` and ``default`` + keyword arguments. See :ref:`task-decorator-arguments` for details. + .. versionchanged:: 1.5 + Added the ``name`` keyword argument. + + .. seealso:: `~fabric.docs.unwrap_tasks`, `~fabric.tasks.WrappedCallableTask` + """ + invoked = bool(not args or kwargs) + task_class = kwargs.pop("task_class", tasks.WrappedCallableTask) + if not invoked: + func, args = args[0], () + + def wrapper(func): + return task_class(func, *args, **kwargs) + + return wrapper if invoked else wrapper(func) + +def _wrap_as_new(original, new): + if isinstance(original, tasks.Task): + return tasks.WrappedCallableTask(new) + return new + + +def _list_annotating_decorator(attribute, *values): + def attach_list(func): + @wraps(func) + def inner_decorator(*args, **kwargs): + return func(*args, **kwargs) + _values = values + # Allow for single iterable argument as well as *args + if len(_values) == 1 and not isinstance(_values[0], basestring): + _values = _values[0] + setattr(inner_decorator, attribute, list(_values)) + # Don't replace @task new-style task objects with inner_decorator by + # itself -- wrap in a new Task object first. + inner_decorator = _wrap_as_new(func, inner_decorator) + return inner_decorator + return attach_list + + +def hosts(*host_list): + """ + Decorator defining which host or hosts to execute the wrapped function on. + + For example, the following will ensure that, barring an override on the + command line, ``my_func`` will be run on ``host1``, ``host2`` and + ``host3``, and with specific users on ``host1`` and ``host3``:: + + @hosts('user1@host1', 'host2', 'user2@host3') + def my_func(): + pass + + `~fabric.decorators.hosts` may be invoked with either an argument list + (``@hosts('host1')``, ``@hosts('host1', 'host2')``) or a single, iterable + argument (``@hosts(['host1', 'host2'])``). + + Note that this decorator actually just sets the function's ``.hosts`` + attribute, which is then read prior to executing the function. + + .. versionchanged:: 0.9.2 + Allow a single, iterable argument (``@hosts(iterable)``) to be used + instead of requiring ``@hosts(*iterable)``. + """ + return _list_annotating_decorator('hosts', *host_list) + + +def roles(*role_list): + """ + Decorator defining a list of role names, used to look up host lists. + + A role is simply defined as a key in `env` whose value is a list of one or + more host connection strings. For example, the following will ensure that, + barring an override on the command line, ``my_func`` will be executed + against the hosts listed in the ``webserver`` and ``dbserver`` roles:: + + env.roledefs.update({ + 'webserver': ['www1', 'www2'], + 'dbserver': ['db1'] + }) + + @roles('webserver', 'dbserver') + def my_func(): + pass + + As with `~fabric.decorators.hosts`, `~fabric.decorators.roles` may be + invoked with either an argument list or a single, iterable argument. + Similarly, this decorator uses the same mechanism as + `~fabric.decorators.hosts` and simply sets ``.roles``. + + .. versionchanged:: 0.9.2 + Allow a single, iterable argument to be used (same as + `~fabric.decorators.hosts`). + """ + return _list_annotating_decorator('roles', *role_list) + + +def runs_once(func): + """ + Decorator preventing wrapped function from running more than once. + + By keeping internal state, this decorator allows you to mark a function + such that it will only run once per Python interpreter session, which in + typical use means "once per invocation of the ``fab`` program". + + Any function wrapped with this decorator will silently fail to execute the + 2nd, 3rd, ..., Nth time it is called, and will return the value of the + original run. + + .. note:: ``runs_once`` does not work with parallel task execution. + """ + @wraps(func) + def decorated(*args, **kwargs): + if not hasattr(decorated, 'return_value'): + decorated.return_value = func(*args, **kwargs) + return decorated.return_value + decorated = _wrap_as_new(func, decorated) + # Mark as serial (disables parallelism) and return + return serial(decorated) + + +def serial(func): + """ + Forces the wrapped function to always run sequentially, never in parallel. + + This decorator takes precedence over the global value of :ref:`env.parallel + `. However, if a task is decorated with both + `~fabric.decorators.serial` *and* `~fabric.decorators.parallel`, + `~fabric.decorators.parallel` wins. + + .. versionadded:: 1.3 + """ + if not getattr(func, 'parallel', False): + func.serial = True + return _wrap_as_new(func, func) + + +def parallel(pool_size=None): + """ + Forces the wrapped function to run in parallel, instead of sequentially. + + This decorator takes precedence over the global value of :ref:`env.parallel + `. It also takes precedence over `~fabric.decorators.serial` + if a task is decorated with both. + + .. versionadded:: 1.3 + """ + called_without_args = type(pool_size) == types.FunctionType + + def real_decorator(func): + @wraps(func) + def inner(*args, **kwargs): + # Required for ssh/PyCrypto to be happy in multiprocessing + # (as far as we can tell, this is needed even with the extra such + # calls in newer versions of paramiko.) + Random.atfork() + return func(*args, **kwargs) + inner.parallel = True + inner.serial = False + inner.pool_size = None if called_without_args else pool_size + return _wrap_as_new(func, inner) + + # Allow non-factory-style decorator use (@decorator vs @decorator()) + if called_without_args: + return real_decorator(pool_size) + + return real_decorator + + +def with_settings(*arg_settings, **kw_settings): + """ + Decorator equivalent of ``fabric.context_managers.settings``. + + Allows you to wrap an entire function as if it was called inside a block + with the ``settings`` context manager. This may be useful if you know you + want a given setting applied to an entire function body, or wish to + retrofit old code without indenting everything. + + For example, to turn aborts into warnings for an entire task function:: + + @with_settings(warn_only=True) + def foo(): + ... + + .. seealso:: `~fabric.context_managers.settings` + .. versionadded:: 1.1 + """ + def outer(func): + @wraps(func) + def inner(*args, **kwargs): + with settings(*arg_settings, **kw_settings): + return func(*args, **kwargs) + return _wrap_as_new(func, inner) + return outer diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/docs.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/docs.py new file mode 100644 index 0000000..1f6ad6e --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/docs.py @@ -0,0 +1,57 @@ +from fabric.tasks import WrappedCallableTask + + +def unwrap_tasks(module, hide_nontasks=False): + """ + Replace task objects on ``module`` with their wrapped functions instead. + + Specifically, look for instances of `~fabric.tasks.WrappedCallableTask` and + replace them with their ``.wrapped`` attribute (the original decorated + function.) + + This is intended for use with the Sphinx autodoc tool, to be run near the + bottom of a project's ``conf.py``. It ensures that the autodoc extension + will have full access to the "real" function, in terms of function + signature and so forth. Without use of ``unwrap_tasks``, autodoc is unable + to access the function signature (though it is able to see e.g. + ``__doc__``.) + + For example, at the bottom of your ``conf.py``:: + + from fabric.docs import unwrap_tasks + import my_package.my_fabfile + unwrap_tasks(my_package.my_fabfile) + + You can go above and beyond, and explicitly **hide** all non-task + functions, by saying ``hide_nontasks=True``. This renames all objects + failing the "is it a task?" check so they appear to be private, which will + then cause autodoc to skip over them. + + ``hide_nontasks`` is thus useful when you have a fabfile mixing in + subroutines with real tasks and want to document *just* the real tasks. + + If you run this within an actual Fabric-code-using session (instead of + within a Sphinx ``conf.py``), please seek immediate medical attention. + + .. versionadded: 1.5 + + .. seealso:: `~fabric.tasks.WrappedCallableTask`, `~fabric.decorators.task` + """ + set_tasks = [] + for name, obj in vars(module).items(): + if isinstance(obj, WrappedCallableTask): + setattr(module, obj.name, obj.wrapped) + # Handle situation where a task's real name shadows a builtin. + # If the builtin comes after the task in vars().items(), the object + # we just setattr'd above will get re-hidden :( + set_tasks.append(obj.name) + # In the same vein, "privately" named wrapped functions whose task + # name is public, needs to get renamed so autodoc picks it up. + obj.wrapped.func_name = obj.name + else: + if name in set_tasks: + continue + has_docstring = getattr(obj, '__doc__', False) + if hide_nontasks and has_docstring and not name.startswith('_'): + setattr(module, '_%s' % name, obj) + delattr(module, name) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/exceptions.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/exceptions.py new file mode 100644 index 0000000..a0142b6 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/exceptions.py @@ -0,0 +1,27 @@ +""" +Custom Fabric exception classes. + +Most are simply distinct Exception subclasses for purposes of message-passing +(though typically still in actual error situations.) +""" + + +class NetworkError(Exception): + # Must allow for calling with zero args/kwargs, since pickle is apparently + # stupid with exceptions and tries to call it as such when passed around in + # a multiprocessing.Queue. + def __init__(self, message=None, wrapped=None): + self.message = message + self.wrapped = wrapped + + def __str__(self): + return self.message + + def __repr__(self): + return "%s(%s) => %r" % ( + self.__class__.__name__, self.message, self.wrapped + ) + + +class CommandTimeout(Exception): + pass diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/io.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/io.py new file mode 100644 index 0000000..5c97463 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/io.py @@ -0,0 +1,243 @@ +from __future__ import with_statement + +import sys +import time +import re +import socket +from select import select + +from fabric.state import env, output, win32 +from fabric.auth import get_password, set_password +import fabric.network +from fabric.network import ssh, normalize +from fabric.utils import RingBuffer +from fabric.exceptions import CommandTimeout + + +if win32: + import msvcrt + + +def _endswith(char_list, substring): + tail = char_list[-1 * len(substring):] + substring = list(substring) + return tail == substring + + +def _has_newline(bytelist): + return '\r' in bytelist or '\n' in bytelist + + +def output_loop(*args, **kwargs): + OutputLooper(*args, **kwargs).loop() + + +class OutputLooper(object): + def __init__(self, chan, attr, stream, capture, timeout): + self.chan = chan + self.stream = stream + self.capture = capture + self.timeout = timeout + self.read_func = getattr(chan, attr) + self.prefix = "[%s] %s: " % ( + env.host_string, + "out" if attr == 'recv' else "err" + ) + self.printing = getattr(output, 'stdout' if (attr == 'recv') else 'stderr') + self.linewise = (env.linewise or env.parallel) + self.reprompt = False + self.read_size = 4096 + self.write_buffer = RingBuffer([], maxlen=len(self.prefix)) + + def _flush(self, text): + self.stream.write(text) + # Actually only flush if not in linewise mode. + # When linewise is set (e.g. in parallel mode) flushing makes + # doubling-up of line prefixes, and other mixed output, more likely. + if not env.linewise: + self.stream.flush() + self.write_buffer.extend(text) + + def loop(self): + """ + Loop, reading from .(), writing to and buffering to . + + Will raise `~fabric.exceptions.CommandTimeout` if network timeouts + continue to be seen past the defined ``self.timeout`` threshold. + (Timeouts before then are considered part of normal short-timeout fast + network reading; see Fabric issue #733 for background.) + """ + # Internal capture-buffer-like buffer, used solely for state keeping. + # Unlike 'capture', nothing is ever purged from this. + _buffer = [] + + # Initialize loop variables + initial_prefix_printed = False + seen_cr = False + line = [] + + # Allow prefix to be turned off. + if not env.output_prefix: + self.prefix = "" + + start = time.time() + while True: + # Handle actual read + try: + bytelist = self.read_func(self.read_size) + except socket.timeout: + elapsed = time.time() - start + if self.timeout is not None and elapsed > self.timeout: + raise CommandTimeout + continue + # Empty byte == EOS + if bytelist == '': + # If linewise, ensure we flush any leftovers in the buffer. + if self.linewise and line: + self._flush(self.prefix) + self._flush("".join(line)) + break + # A None capture variable implies that we're in open_shell() + if self.capture is None: + # Just print directly -- no prefixes, no capturing, nada + # And since we know we're using a pty in this mode, just go + # straight to stdout. + self._flush(bytelist) + # Otherwise, we're in run/sudo and need to handle capturing and + # prompts. + else: + # Print to user + if self.printing: + printable_bytes = bytelist + # Small state machine to eat \n after \r + if printable_bytes[-1] == "\r": + seen_cr = True + if printable_bytes[0] == "\n" and seen_cr: + printable_bytes = printable_bytes[1:] + seen_cr = False + + while _has_newline(printable_bytes) and printable_bytes != "": + # at most 1 split ! + cr = re.search("(\r\n|\r|\n)", printable_bytes) + if cr is None: + break + end_of_line = printable_bytes[:cr.start(0)] + printable_bytes = printable_bytes[cr.end(0):] + + if not initial_prefix_printed: + self._flush(self.prefix) + + if _has_newline(end_of_line): + end_of_line = '' + + if self.linewise: + self._flush("".join(line) + end_of_line + "\n") + line = [] + else: + self._flush(end_of_line + "\n") + initial_prefix_printed = False + + if self.linewise: + line += [printable_bytes] + else: + if not initial_prefix_printed: + self._flush(self.prefix) + initial_prefix_printed = True + self._flush(printable_bytes) + + # Now we have handled printing, handle interactivity + read_lines = re.split(r"(\r|\n|\r\n)", bytelist) + for fragment in read_lines: + # Store in capture buffer + self.capture += fragment + # Store in internal buffer + _buffer += fragment + # Handle prompts + expected, response = self._get_prompt_response() + if expected: + del self.capture[-1 * len(expected):] + self.chan.sendall(str(response) + '\n') + else: + prompt = _endswith(self.capture, env.sudo_prompt) + try_again = (_endswith(self.capture, env.again_prompt + '\n') + or _endswith(self.capture, env.again_prompt + '\r\n')) + if prompt: + self.prompt() + elif try_again: + self.try_again() + + # Print trailing new line if the last thing we printed was our line + # prefix. + if self.prefix and "".join(self.write_buffer) == self.prefix: + self._flush('\n') + + def prompt(self): + # Obtain cached password, if any + password = get_password(*normalize(env.host_string)) + # Remove the prompt itself from the capture buffer. This is + # backwards compatible with Fabric 0.9.x behavior; the user + # will still see the prompt on their screen (no way to avoid + # this) but at least it won't clutter up the captured text. + del self.capture[-1 * len(env.sudo_prompt):] + # If the password we just tried was bad, prompt the user again. + if (not password) or self.reprompt: + # Print the prompt and/or the "try again" notice if + # output is being hidden. In other words, since we need + # the user's input, they need to see why we're + # prompting them. + if not self.printing: + self._flush(self.prefix) + if self.reprompt: + self._flush(env.again_prompt + '\n' + self.prefix) + self._flush(env.sudo_prompt) + # Prompt for, and store, password. Give empty prompt so the + # initial display "hides" just after the actually-displayed + # prompt from the remote end. + self.chan.input_enabled = False + password = fabric.network.prompt_for_password( + prompt=" ", no_colon=True, stream=self.stream + ) + self.chan.input_enabled = True + # Update env.password, env.passwords if necessary + user, host, port = normalize(env.host_string) + set_password(user, host, port, password) + # Reset reprompt flag + self.reprompt = False + # Send current password down the pipe + self.chan.sendall(password + '\n') + + def try_again(self): + # Remove text from capture buffer + self.capture = self.capture[:len(env.again_prompt)] + # Set state so we re-prompt the user at the next prompt. + self.reprompt = True + + def _get_prompt_response(self): + """ + Iterate through the request prompts dict and return the response and + original request if we find a match + """ + for tup in env.prompts.iteritems(): + if _endswith(self.capture, tup[0]): + return tup + return None, None + + +def input_loop(chan, using_pty): + while not chan.exit_status_ready(): + if win32: + have_char = msvcrt.kbhit() + else: + r, w, x = select([sys.stdin], [], [], 0.0) + have_char = (r and r[0] == sys.stdin) + if have_char and chan.input_enabled: + # Send all local stdin to remote end's stdin + byte = msvcrt.getch() if win32 else sys.stdin.read(1) + chan.sendall(byte) + # Optionally echo locally, if needed. + if not using_pty and env.echo_stdin: + # Not using fastprint() here -- it prints as 'user' + # output level, don't want it to be accidentally hidden + sys.stdout.write(byte) + sys.stdout.flush() + time.sleep(ssh.io_sleep) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/job_queue.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/job_queue.py new file mode 100644 index 0000000..121d900 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/job_queue.py @@ -0,0 +1,234 @@ +""" +Sliding-window-based job/task queue class (& example of use.) + +May use ``multiprocessing.Process`` or ``threading.Thread`` objects as queue +items, though within Fabric itself only ``Process`` objects are used/supported. +""" + +from __future__ import with_statement +import time +import Queue + +from fabric.state import env +from fabric.network import ssh +from fabric.context_managers import settings + + +class JobQueue(object): + """ + The goal of this class is to make a queue of processes to run, and go + through them running X number at any given time. + + So if the bubble is 5 start with 5 running and move the bubble of running + procs along the queue looking something like this: + + Start + ........................... + [~~~~~].................... + ___[~~~~~]................. + _________[~~~~~]........... + __________________[~~~~~].. + ____________________[~~~~~] + ___________________________ + End + """ + def __init__(self, max_running, comms_queue): + """ + Setup the class to resonable defaults. + """ + self._queued = [] + self._running = [] + self._completed = [] + self._num_of_jobs = 0 + self._max = max_running + self._comms_queue = comms_queue + self._finished = False + self._closed = False + self._debug = False + + def _all_alive(self): + """ + Simply states if all procs are alive or not. Needed to determine when + to stop looping, and pop dead procs off and add live ones. + """ + if self._running: + return all([x.is_alive() for x in self._running]) + else: + return False + + def __len__(self): + """ + Just going to use number of jobs as the JobQueue length. + """ + return self._num_of_jobs + + def close(self): + """ + A sanity check, so that the need to care about new jobs being added in + the last throws of the job_queue's run are negated. + """ + if self._debug: + print("job queue closed.") + + self._closed = True + + def append(self, process): + """ + Add the Process() to the queue, so that later it can be checked up on. + That is if the JobQueue is still open. + + If the queue is closed, this will just silently do nothing. + + To get data back out of this process, give ``process`` access to a + ``multiprocessing.Queue`` object, and give it here as ``queue``. Then + ``JobQueue.run`` will include the queue's contents in its return value. + """ + if not self._closed: + self._queued.append(process) + self._num_of_jobs += 1 + if self._debug: + print("job queue appended %s." % process.name) + + def run(self): + """ + This is the workhorse. It will take the intial jobs from the _queue, + start them, add them to _running, and then go into the main running + loop. + + This loop will check for done procs, if found, move them out of + _running into _completed. It also checks for a _running queue with open + spots, which it will then fill as discovered. + + To end the loop, there have to be no running procs, and no more procs + to be run in the queue. + + This function returns an iterable of all its children's exit codes. + """ + def _advance_the_queue(): + """ + Helper function to do the job of poping a new proc off the queue + start it, then add it to the running queue. This will eventually + depleate the _queue, which is a condition of stopping the running + while loop. + + It also sets the env.host_string from the job.name, so that fabric + knows that this is the host to be making connections on. + """ + job = self._queued.pop() + if self._debug: + print("Popping '%s' off the queue and starting it" % job.name) + with settings(clean_revert=True, host_string=job.name, host=job.name): + job.start() + self._running.append(job) + + # Prep return value so we can start filling it during main loop + results = {} + for job in self._queued: + results[job.name] = dict.fromkeys(('exit_code', 'results')) + + if not self._closed: + raise Exception("Need to close() before starting.") + + if self._debug: + print("Job queue starting.") + + while len(self._running) < self._max: + _advance_the_queue() + + # Main loop! + while not self._finished: + while len(self._running) < self._max and self._queued: + _advance_the_queue() + + if not self._all_alive(): + for id, job in enumerate(self._running): + if not job.is_alive(): + if self._debug: + print("Job queue found finished proc: %s." % + job.name) + done = self._running.pop(id) + self._completed.append(done) + + if self._debug: + print("Job queue has %d running." % len(self._running)) + + if not (self._queued or self._running): + if self._debug: + print("Job queue finished.") + + for job in self._completed: + job.join() + + self._finished = True + + # Each loop pass, try pulling results off the queue to keep its + # size down. At this point, we don't actually care if any results + # have arrived yet; they will be picked up after the main loop. + self._fill_results(results) + + time.sleep(ssh.io_sleep) + + # Consume anything left in the results queue. Note that there is no + # need to block here, as the main loop ensures that all workers will + # already have finished. + self._fill_results(results) + + # Attach exit codes now that we're all done & have joined all jobs + for job in self._completed: + results[job.name]['exit_code'] = job.exitcode + + return results + + def _fill_results(self, results): + """ + Attempt to pull data off self._comms_queue and add to 'results' dict. + If no data is available (i.e. the queue is empty), bail immediately. + """ + while True: + try: + datum = self._comms_queue.get_nowait() + results[datum['name']]['results'] = datum['result'] + except Queue.Empty: + break + + +#### Sample + +def try_using(parallel_type): + """ + This will run the queue through it's paces, and show a simple way of using + the job queue. + """ + + def print_number(number): + """ + Simple function to give a simple task to execute. + """ + print(number) + + if parallel_type == "multiprocessing": + from multiprocessing import Process as Bucket + + elif parallel_type == "threading": + from threading import Thread as Bucket + + # Make a job_queue with a bubble of len 5, and have it print verbosely + jobs = JobQueue(5) + jobs._debug = True + + # Add 20 procs onto the stack + for x in range(20): + jobs.append(Bucket( + target=print_number, + args=[x], + kwargs={}, + )) + + # Close up the queue and then start it's execution + jobs.close() + jobs.run() + + +if __name__ == '__main__': + try_using("multiprocessing") + try_using("threading") diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/main.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/main.py new file mode 100644 index 0000000..86a9a79 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/main.py @@ -0,0 +1,761 @@ +""" +This module contains Fab's `main` method plus related subroutines. + +`main` is executed as the command line ``fab`` program and takes care of +parsing options and commands, loading the user settings file, loading a +fabfile, and executing the commands given. + +The other callables defined in this module are internal only. Anything useful +to individuals leveraging Fabric as a library, should be kept elsewhere. +""" +import getpass +from operator import isMappingType +from optparse import OptionParser +import os +import sys +import types + +# For checking callables against the API, & easy mocking +from fabric import api, state, colors +from fabric.contrib import console, files, project + +from fabric.network import disconnect_all, ssh +from fabric.state import env_options +from fabric.tasks import Task, execute, get_task_details +from fabric.task_utils import _Dict, crawl +from fabric.utils import abort, indent, warn, _pty_size + + +# One-time calculation of "all internal callables" to avoid doing this on every +# check of a given fabfile callable (in is_classic_task()). +_modules = [api, project, files, console, colors] +_internals = reduce(lambda x, y: x + filter(callable, vars(y).values()), + _modules, + [] +) + + +# Module recursion cache +class _ModuleCache(object): + """ + Set-like object operating on modules and storing __name__s internally. + """ + def __init__(self): + self.cache = set() + + def __contains__(self, value): + return value.__name__ in self.cache + + def add(self, value): + return self.cache.add(value.__name__) + + def clear(self): + return self.cache.clear() + +_seen = _ModuleCache() + + +def load_settings(path): + """ + Take given file path and return dictionary of any key=value pairs found. + + Usage docs are in sites/docs/usage/fab.rst, in "Settings files." + """ + if os.path.exists(path): + comments = lambda s: s and not s.startswith("#") + settings = filter(comments, open(path, 'r')) + return dict((k.strip(), v.strip()) for k, _, v in + [s.partition('=') for s in settings]) + # Handle nonexistent or empty settings file + return {} + + +def _is_package(path): + """ + Is the given path a Python package? + """ + return ( + os.path.isdir(path) + and os.path.exists(os.path.join(path, '__init__.py')) + ) + + +def find_fabfile(names=None): + """ + Attempt to locate a fabfile, either explicitly or by searching parent dirs. + + Usage docs are in sites/docs/usage/fabfiles.rst, in "Fabfile discovery." + """ + # Obtain env value if not given specifically + if names is None: + names = [state.env.fabfile] + # Create .py version if necessary + if not names[0].endswith('.py'): + names += [names[0] + '.py'] + # Does the name contain path elements? + if os.path.dirname(names[0]): + # If so, expand home-directory markers and test for existence + for name in names: + expanded = os.path.expanduser(name) + if os.path.exists(expanded): + if name.endswith('.py') or _is_package(expanded): + return os.path.abspath(expanded) + else: + # Otherwise, start in cwd and work downwards towards filesystem root + path = '.' + # Stop before falling off root of filesystem (should be platform + # agnostic) + while os.path.split(os.path.abspath(path))[1]: + for name in names: + joined = os.path.join(path, name) + if os.path.exists(joined): + if name.endswith('.py') or _is_package(joined): + return os.path.abspath(joined) + path = os.path.join('..', path) + # Implicit 'return None' if nothing was found + + +def is_classic_task(tup): + """ + Takes (name, object) tuple, returns True if it's a non-Fab public callable. + """ + name, func = tup + try: + is_classic = ( + callable(func) + and (func not in _internals) + and not name.startswith('_') + ) + # Handle poorly behaved __eq__ implementations + except (ValueError, TypeError): + is_classic = False + return is_classic + + +def load_fabfile(path, importer=None): + """ + Import given fabfile path and return (docstring, callables). + + Specifically, the fabfile's ``__doc__`` attribute (a string) and a + dictionary of ``{'name': callable}`` containing all callables which pass + the "is a Fabric task" test. + """ + if importer is None: + importer = __import__ + # Get directory and fabfile name + directory, fabfile = os.path.split(path) + # If the directory isn't in the PYTHONPATH, add it so our import will work + added_to_path = False + index = None + if directory not in sys.path: + sys.path.insert(0, directory) + added_to_path = True + # If the directory IS in the PYTHONPATH, move it to the front temporarily, + # otherwise other fabfiles -- like Fabric's own -- may scoop the intended + # one. + else: + i = sys.path.index(directory) + if i != 0: + # Store index for later restoration + index = i + # Add to front, then remove from original position + sys.path.insert(0, directory) + del sys.path[i + 1] + # Perform the import (trimming off the .py) + imported = importer(os.path.splitext(fabfile)[0]) + # Remove directory from path if we added it ourselves (just to be neat) + if added_to_path: + del sys.path[0] + # Put back in original index if we moved it + if index is not None: + sys.path.insert(index + 1, directory) + del sys.path[0] + + # Actually load tasks + docstring, new_style, classic, default = load_tasks_from_module(imported) + tasks = new_style if state.env.new_style_tasks else classic + # Clean up after ourselves + _seen.clear() + return docstring, tasks, default + + +def load_tasks_from_module(imported): + """ + Handles loading all of the tasks for a given `imported` module + """ + # Obey the use of .__all__ if it is present + imported_vars = vars(imported) + if "__all__" in imported_vars: + imported_vars = [(name, imported_vars[name]) for name in \ + imported_vars if name in imported_vars["__all__"]] + else: + imported_vars = imported_vars.items() + # Return a two-tuple value. First is the documentation, second is a + # dictionary of callables only (and don't include Fab operations or + # underscored callables) + new_style, classic, default = extract_tasks(imported_vars) + return imported.__doc__, new_style, classic, default + + +def extract_tasks(imported_vars): + """ + Handle extracting tasks from a given list of variables + """ + new_style_tasks = _Dict() + classic_tasks = {} + default_task = None + if 'new_style_tasks' not in state.env: + state.env.new_style_tasks = False + for tup in imported_vars: + name, obj = tup + if is_task_object(obj): + state.env.new_style_tasks = True + # Use instance.name if defined + if obj.name and obj.name != 'undefined': + new_style_tasks[obj.name] = obj + else: + obj.name = name + new_style_tasks[name] = obj + # Handle aliasing + if obj.aliases is not None: + for alias in obj.aliases: + new_style_tasks[alias] = obj + # Handle defaults + if obj.is_default: + default_task = obj + elif is_classic_task(tup): + classic_tasks[name] = obj + elif is_task_module(obj): + docs, newstyle, classic, default = load_tasks_from_module(obj) + for task_name, task in newstyle.items(): + if name not in new_style_tasks: + new_style_tasks[name] = _Dict() + new_style_tasks[name][task_name] = task + if default is not None: + new_style_tasks[name].default = default + return new_style_tasks, classic_tasks, default_task + + +def is_task_module(a): + """ + Determine if the provided value is a task module + """ + #return (type(a) is types.ModuleType and + # any(map(is_task_object, vars(a).values()))) + if isinstance(a, types.ModuleType) and a not in _seen: + # Flag module as seen + _seen.add(a) + # Signal that we need to check it out + return True + + +def is_task_object(a): + """ + Determine if the provided value is a ``Task`` object. + + This returning True signals that all tasks within the fabfile + module must be Task objects. + """ + return isinstance(a, Task) and a.use_task_objects + + +def parse_options(): + """ + Handle command-line options with optparse.OptionParser. + + Return list of arguments, largely for use in `parse_arguments`. + """ + # + # Initialize + # + + parser = OptionParser( + usage=("fab [options] " + "[:arg1,arg2=val2,host=foo,hosts='h1;h2',...] ...")) + + # + # Define options that don't become `env` vars (typically ones which cause + # Fabric to do something other than its normal execution, such as + # --version) + # + + # Display info about a specific command + parser.add_option('-d', '--display', + metavar='NAME', + help="print detailed info about command NAME" + ) + + # Control behavior of --list + LIST_FORMAT_OPTIONS = ('short', 'normal', 'nested') + parser.add_option('-F', '--list-format', + choices=LIST_FORMAT_OPTIONS, + default='normal', + metavar='FORMAT', + help="formats --list, choices: %s" % ", ".join(LIST_FORMAT_OPTIONS) + ) + + parser.add_option('-I', '--initial-password-prompt', + action='store_true', + default=False, + help="Force password prompt up-front" + ) + + # List Fab commands found in loaded fabfiles/source files + parser.add_option('-l', '--list', + action='store_true', + dest='list_commands', + default=False, + help="print list of possible commands and exit" + ) + + # Allow setting of arbitrary env vars at runtime. + parser.add_option('--set', + metavar="KEY=VALUE,...", + dest='env_settings', + default="", + help="comma separated KEY=VALUE pairs to set Fab env vars" + ) + + # Like --list, but text processing friendly + parser.add_option('--shortlist', + action='store_true', + dest='shortlist', + default=False, + help="alias for -F short --list" + ) + + # Version number (optparse gives you --version but we have to do it + # ourselves to get -V too. sigh) + parser.add_option('-V', '--version', + action='store_true', + dest='show_version', + default=False, + help="show program's version number and exit" + ) + + # + # Add in options which are also destined to show up as `env` vars. + # + + for option in env_options: + parser.add_option(option) + + # + # Finalize + # + + # Return three-tuple of parser + the output from parse_args (opt obj, args) + opts, args = parser.parse_args() + return parser, opts, args + + +def _is_task(name, value): + """ + Is the object a task as opposed to e.g. a dict or int? + """ + return is_classic_task((name, value)) or is_task_object(value) + + +def _sift_tasks(mapping): + tasks, collections = [], [] + for name, value in mapping.iteritems(): + if _is_task(name, value): + tasks.append(name) + elif isMappingType(value): + collections.append(name) + tasks = sorted(tasks) + collections = sorted(collections) + return tasks, collections + + +def _task_names(mapping): + """ + Flatten & sort task names in a breadth-first fashion. + + Tasks are always listed before submodules at the same level, but within + those two groups, sorting is alphabetical. + """ + tasks, collections = _sift_tasks(mapping) + for collection in collections: + module = mapping[collection] + if hasattr(module, 'default'): + tasks.append(collection) + join = lambda x: ".".join((collection, x)) + tasks.extend(map(join, _task_names(module))) + return tasks + + +def _print_docstring(docstrings, name): + if not docstrings: + return False + docstring = crawl(name, state.commands).__doc__ + if isinstance(docstring, basestring): + return docstring + + +def _normal_list(docstrings=True): + result = [] + task_names = _task_names(state.commands) + # Want separator between name, description to be straight col + max_len = reduce(lambda a, b: max(a, len(b)), task_names, 0) + sep = ' ' + trail = '...' + max_width = _pty_size()[1] - 1 - len(trail) + for name in task_names: + output = None + docstring = _print_docstring(docstrings, name) + if docstring: + lines = filter(None, docstring.splitlines()) + first_line = lines[0].strip() + # Truncate it if it's longer than N chars + size = max_width - (max_len + len(sep) + len(trail)) + if len(first_line) > size: + first_line = first_line[:size] + trail + output = name.ljust(max_len) + sep + first_line + # Or nothing (so just the name) + else: + output = name + result.append(indent(output)) + return result + + +def _nested_list(mapping, level=1): + result = [] + tasks, collections = _sift_tasks(mapping) + # Tasks come first + result.extend(map(lambda x: indent(x, spaces=level * 4), tasks)) + for collection in collections: + module = mapping[collection] + # Section/module "header" + result.append(indent(collection + ":", spaces=level * 4)) + # Recurse + result.extend(_nested_list(module, level + 1)) + return result + +COMMANDS_HEADER = "Available commands" +NESTED_REMINDER = " (remember to call as module.[...].task)" + + +def list_commands(docstring, format_): + """ + Print all found commands/tasks, then exit. Invoked with ``-l/--list.`` + + If ``docstring`` is non-empty, it will be printed before the task list. + + ``format_`` should conform to the options specified in + ``LIST_FORMAT_OPTIONS``, e.g. ``"short"``, ``"normal"``. + """ + # Short-circuit with simple short output + if format_ == "short": + return _task_names(state.commands) + # Otherwise, handle more verbose modes + result = [] + # Docstring at top, if applicable + if docstring: + trailer = "\n" if not docstring.endswith("\n") else "" + result.append(docstring + trailer) + header = COMMANDS_HEADER + if format_ == "nested": + header += NESTED_REMINDER + result.append(header + ":\n") + c = _normal_list() if format_ == "normal" else _nested_list(state.commands) + result.extend(c) + return result + + +def display_command(name): + """ + Print command function's docstring, then exit. Invoked with -d/--display. + """ + # Sanity check + command = crawl(name, state.commands) + if command is None: + msg = "Task '%s' does not appear to exist. Valid task names:\n%s" + abort(msg % (name, "\n".join(_normal_list(False)))) + # Print out nicely presented docstring if found + if hasattr(command, '__details__'): + task_details = command.__details__() + else: + task_details = get_task_details(command) + if task_details: + print("Displaying detailed information for task '%s':" % name) + print('') + print(indent(task_details, strip=True)) + print('') + # Or print notice if not + else: + print("No detailed information available for task '%s':" % name) + sys.exit(0) + + +def _escape_split(sep, argstr): + """ + Allows for escaping of the separator: e.g. task:arg='foo\, bar' + + It should be noted that the way bash et. al. do command line parsing, those + single quotes are required. + """ + escaped_sep = r'\%s' % sep + + if escaped_sep not in argstr: + return argstr.split(sep) + + before, _, after = argstr.partition(escaped_sep) + startlist = before.split(sep) # a regular split is fine here + unfinished = startlist[-1] + startlist = startlist[:-1] + + # recurse because there may be more escaped separators + endlist = _escape_split(sep, after) + + # finish building the escaped value. we use endlist[0] becaue the first + # part of the string sent in recursion is the rest of the escaped value. + unfinished += sep + endlist[0] + + return startlist + [unfinished] + endlist[1:] # put together all the parts + + +def parse_arguments(arguments): + """ + Parse string list into list of tuples: command, args, kwargs, hosts, roles. + + See sites/docs/usage/fab.rst, section on "per-task arguments" for details. + """ + cmds = [] + for cmd in arguments: + args = [] + kwargs = {} + hosts = [] + roles = [] + exclude_hosts = [] + if ':' in cmd: + cmd, argstr = cmd.split(':', 1) + for pair in _escape_split(',', argstr): + result = _escape_split('=', pair) + if len(result) > 1: + k, v = result + # Catch, interpret host/hosts/role/roles/exclude_hosts + # kwargs + if k in ['host', 'hosts', 'role', 'roles', 'exclude_hosts']: + if k == 'host': + hosts = [v.strip()] + elif k == 'hosts': + hosts = [x.strip() for x in v.split(';')] + elif k == 'role': + roles = [v.strip()] + elif k == 'roles': + roles = [x.strip() for x in v.split(';')] + elif k == 'exclude_hosts': + exclude_hosts = [x.strip() for x in v.split(';')] + # Otherwise, record as usual + else: + kwargs[k] = v + else: + args.append(result[0]) + cmds.append((cmd, args, kwargs, hosts, roles, exclude_hosts)) + return cmds + + +def parse_remainder(arguments): + """ + Merge list of "remainder arguments" into a single command string. + """ + return ' '.join(arguments) + + +def update_output_levels(show, hide): + """ + Update state.output values as per given comma-separated list of key names. + + For example, ``update_output_levels(show='debug,warnings')`` is + functionally equivalent to ``state.output['debug'] = True ; + state.output['warnings'] = True``. Conversely, anything given to ``hide`` + sets the values to ``False``. + """ + if show: + for key in show.split(','): + state.output[key] = True + if hide: + for key in hide.split(','): + state.output[key] = False + + +def show_commands(docstring, format, code=0): + print("\n".join(list_commands(docstring, format))) + sys.exit(code) + + +def main(fabfile_locations=None): + """ + Main command-line execution loop. + """ + try: + # Parse command line options + parser, options, arguments = parse_options() + + # Handle regular args vs -- args + arguments = parser.largs + remainder_arguments = parser.rargs + + # Allow setting of arbitrary env keys. + # This comes *before* the "specific" env_options so that those may + # override these ones. Specific should override generic, if somebody + # was silly enough to specify the same key in both places. + # E.g. "fab --set shell=foo --shell=bar" should have env.shell set to + # 'bar', not 'foo'. + for pair in _escape_split(',', options.env_settings): + pair = _escape_split('=', pair) + # "--set x" => set env.x to True + # "--set x=" => set env.x to "" + key = pair[0] + value = True + if len(pair) == 2: + value = pair[1] + state.env[key] = value + + # Update env with any overridden option values + # NOTE: This needs to remain the first thing that occurs + # post-parsing, since so many things hinge on the values in env. + for option in env_options: + state.env[option.dest] = getattr(options, option.dest) + + # Handle --hosts, --roles, --exclude-hosts (comma separated string => + # list) + for key in ['hosts', 'roles', 'exclude_hosts']: + if key in state.env and isinstance(state.env[key], basestring): + state.env[key] = state.env[key].split(',') + + # Feed the env.tasks : tasks that are asked to be executed. + state.env['tasks'] = arguments + + # Handle output control level show/hide + update_output_levels(show=options.show, hide=options.hide) + + # Handle version number option + if options.show_version: + print("Fabric %s" % state.env.version) + print("Paramiko %s" % ssh.__version__) + sys.exit(0) + + # Load settings from user settings file, into shared env dict. + state.env.update(load_settings(state.env.rcfile)) + + # Find local fabfile path or abort + fabfile = find_fabfile(fabfile_locations) + if not fabfile and not remainder_arguments: + abort("""Couldn't find any fabfiles! + +Remember that -f can be used to specify fabfile path, and use -h for help.""") + + # Store absolute path to fabfile in case anyone needs it + state.env.real_fabfile = fabfile + + # Load fabfile (which calls its module-level code, including + # tweaks to env values) and put its commands in the shared commands + # dict + default = None + if fabfile: + docstring, callables, default = load_fabfile(fabfile) + state.commands.update(callables) + + # Handle case where we were called bare, i.e. just "fab", and print + # a help message. + actions = (options.list_commands, options.shortlist, options.display, + arguments, remainder_arguments, default) + if not any(actions): + parser.print_help() + sys.exit(1) + + # Abort if no commands found + if not state.commands and not remainder_arguments: + abort("Fabfile didn't contain any commands!") + + # Now that we're settled on a fabfile, inform user. + if state.output.debug: + if fabfile: + print("Using fabfile '%s'" % fabfile) + else: + print("No fabfile loaded -- remainder command only") + + # Shortlist is now just an alias for the "short" list format; + # it overrides use of --list-format if somebody were to specify both + if options.shortlist: + options.list_format = 'short' + options.list_commands = True + + # List available commands + if options.list_commands: + show_commands(docstring, options.list_format) + + # Handle show (command-specific help) option + if options.display: + display_command(options.display) + + # If user didn't specify any commands to run, show help + if not (arguments or remainder_arguments or default): + parser.print_help() + sys.exit(0) # Or should it exit with error (1)? + + # Parse arguments into commands to run (plus args/kwargs/hosts) + commands_to_run = parse_arguments(arguments) + + # Parse remainders into a faux "command" to execute + remainder_command = parse_remainder(remainder_arguments) + + # Figure out if any specified task names are invalid + unknown_commands = [] + for tup in commands_to_run: + if crawl(tup[0], state.commands) is None: + unknown_commands.append(tup[0]) + + # Abort if any unknown commands were specified + if unknown_commands: + warn("Command(s) not found:\n%s" \ + % indent(unknown_commands)) + show_commands(None, options.list_format, 1) + + # Generate remainder command and insert into commands, commands_to_run + if remainder_command: + r = '' + state.commands[r] = lambda: api.run(remainder_command) + commands_to_run.append((r, [], {}, [], [], [])) + + # Ditto for a default, if found + if not commands_to_run and default: + commands_to_run.append((default.name, [], {}, [], [], [])) + + # Initial password prompt, if requested + if options.initial_password_prompt: + prompt = "Initial value for env.password: " + state.env.password = getpass.getpass(prompt) + + if state.output.debug: + names = ", ".join(x[0] for x in commands_to_run) + print("Commands to run: %s" % names) + + # At this point all commands must exist, so execute them in order. + for name, args, kwargs, arg_hosts, arg_roles, arg_exclude_hosts in commands_to_run: + execute( + name, + hosts=arg_hosts, + roles=arg_roles, + exclude_hosts=arg_exclude_hosts, + *args, **kwargs + ) + # If we got here, no errors occurred, so print a final note. + if state.output.status: + print("\nDone.") + except SystemExit: + # a number of internal functions might raise this one. + raise + except KeyboardInterrupt: + if state.output.status: + sys.stderr.write("\nStopped.\n") + sys.exit(1) + except: + sys.excepthook(*sys.exc_info()) + # we might leave stale threads if we don't explicitly exit() + sys.exit(1) + finally: + disconnect_all() + sys.exit(0) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/network.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/network.py new file mode 100644 index 0000000..b69a9b4 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/network.py @@ -0,0 +1,661 @@ +""" +Classes and subroutines dealing with network connections and related topics. +""" + +from __future__ import with_statement + +from functools import wraps +import getpass +import os +import re +import time +import socket +import sys +from StringIO import StringIO + + +from fabric.auth import get_password, set_password +from fabric.utils import abort, handle_prompt_abort, warn +from fabric.exceptions import NetworkError + +try: + import warnings + warnings.simplefilter('ignore', DeprecationWarning) + import paramiko as ssh +except ImportError, e: + import traceback + traceback.print_exc() + msg = """ +There was a problem importing our SSH library (see traceback above). +Please make sure all dependencies are installed and importable. +""".rstrip() + sys.stderr.write(msg + '\n') + sys.exit(1) + + +ipv6_regex = re.compile('^\[?(?P[0-9A-Fa-f:]+)\]?(:(?P\d+))?$') + + +def direct_tcpip(client, host, port): + return client.get_transport().open_channel( + 'direct-tcpip', + (host, int(port)), + ('', 0) + ) + + +def is_key_load_error(e): + return ( + e.__class__ is ssh.SSHException + and 'Unable to parse key file' in str(e) + ) + + +def _tried_enough(tries): + from fabric.state import env + return tries >= env.connection_attempts + + +def get_gateway(host, port, cache, replace=False): + """ + Create and return a gateway socket, if one is needed. + + This function checks ``env`` for gateway or proxy-command settings and + returns the necessary socket-like object for use by a final host + connection. + + :param host: + Hostname of target server. + + :param port: + Port to connect to on target server. + + :param cache: + A ``HostConnectionCache`` object, in which gateway ``SSHClient`` + objects are to be retrieved/cached. + + :param replace: + Whether to forcibly replace a cached gateway client object. + + :returns: + A ``socket.socket``-like object, or ``None`` if none was created. + """ + from fabric.state import env, output + sock = None + proxy_command = ssh_config().get('proxycommand', None) + if env.gateway: + gateway = normalize_to_string(env.gateway) + # ensure initial gateway connection + if replace or gateway not in cache: + if output.debug: + print "Creating new gateway connection to %r" % gateway + cache[gateway] = connect(*normalize(gateway) + (cache, False)) + # now we should have an open gw connection and can ask it for a + # direct-tcpip channel to the real target. (bypass cache's own + # __getitem__ override to avoid hilarity - this is usually called + # within that method.) + sock = direct_tcpip(dict.__getitem__(cache, gateway), host, port) + elif proxy_command: + sock = ssh.ProxyCommand(proxy_command) + return sock + + +class HostConnectionCache(dict): + """ + Dict subclass allowing for caching of host connections/clients. + + This subclass will intelligently create new client connections when keys + are requested, or return previously created connections instead. + + It also handles creating new socket-like objects when required to implement + gateway connections and `ProxyCommand`, and handing them to the inner + connection methods. + + Key values are the same as host specifiers throughout Fabric: optional + username + ``@``, mandatory hostname, optional ``:`` + port number. + Examples: + + * ``example.com`` - typical Internet host address. + * ``firewall`` - atypical, but still legal, local host address. + * ``user@example.com`` - with specific username attached. + * ``bob@smith.org:222`` - with specific nonstandard port attached. + + When the username is not given, ``env.user`` is used. ``env.user`` + defaults to the currently running user at startup but may be overwritten by + user code or by specifying a command-line flag. + + Note that differing explicit usernames for the same hostname will result in + multiple client connections being made. For example, specifying + ``user1@example.com`` will create a connection to ``example.com``, logged + in as ``user1``; later specifying ``user2@example.com`` will create a new, + 2nd connection as ``user2``. + + The same applies to ports: specifying two different ports will result in + two different connections to the same host being made. If no port is given, + 22 is assumed, so ``example.com`` is equivalent to ``example.com:22``. + """ + def connect(self, key): + """ + Force a new connection to ``key`` host string. + """ + user, host, port = normalize(key) + key = normalize_to_string(key) + self[key] = connect(user, host, port, cache=self) + + def __getitem__(self, key): + """ + Autoconnect + return connection object + """ + key = normalize_to_string(key) + if key not in self: + self.connect(key) + return dict.__getitem__(self, key) + + # + # Dict overrides that normalize input keys + # + + def __setitem__(self, key, value): + return dict.__setitem__(self, normalize_to_string(key), value) + + def __delitem__(self, key): + return dict.__delitem__(self, normalize_to_string(key)) + + def __contains__(self, key): + return dict.__contains__(self, normalize_to_string(key)) + + +def ssh_config(host_string=None): + """ + Return ssh configuration dict for current env.host_string host value. + + Memoizes the loaded SSH config file, but not the specific per-host results. + + This function performs the necessary "is SSH config enabled?" checks and + will simply return an empty dict if not. If SSH config *is* enabled and the + value of env.ssh_config_path is not a valid file, it will abort. + + May give an explicit host string as ``host_string``. + """ + from fabric.state import env + dummy = {} + if not env.use_ssh_config: + return dummy + if '_ssh_config' not in env: + try: + conf = ssh.SSHConfig() + path = os.path.expanduser(env.ssh_config_path) + with open(path) as fd: + conf.parse(fd) + env._ssh_config = conf + except IOError: + warn("Unable to load SSH config file '%s'" % path) + return dummy + host = parse_host_string(host_string or env.host_string)['host'] + return env._ssh_config.lookup(host) + + +def key_filenames(): + """ + Returns list of SSH key filenames for the current env.host_string. + + Takes into account ssh_config and env.key_filename, including normalization + to a list. Also performs ``os.path.expanduser`` expansion on any key + filenames. + """ + from fabric.state import env + keys = env.key_filename + # For ease of use, coerce stringish key filename into list + if isinstance(env.key_filename, basestring) or env.key_filename is None: + keys = [keys] + # Strip out any empty strings (such as the default value...meh) + keys = filter(bool, keys) + # Honor SSH config + conf = ssh_config() + if 'identityfile' in conf: + # Assume a list here as we require Paramiko 1.10+ + keys.extend(conf['identityfile']) + return map(os.path.expanduser, keys) + + +def key_from_env(passphrase=None): + """ + Returns a paramiko-ready key from a text string of a private key + """ + from fabric.state import env, output + + if 'key' in env: + if output.debug: + # NOTE: this may not be the most secure thing; OTOH anybody running + # the process must by definition have access to the key value, + # so only serious problem is if they're logging the output. + sys.stderr.write("Trying to honor in-memory key %r\n" % env.key) + for pkey_class in (ssh.rsakey.RSAKey, ssh.dsskey.DSSKey): + if output.debug: + sys.stderr.write("Trying to load it as %s\n" % pkey_class) + try: + return pkey_class.from_private_key(StringIO(env.key), passphrase) + except Exception, e: + # File is valid key, but is encrypted: raise it, this will + # cause cxn loop to prompt for passphrase & retry + if 'Private key file is encrypted' in e: + raise + # Otherwise, it probably means it wasn't a valid key of this + # type, so try the next one. + else: + pass + + +def parse_host_string(host_string): + # Split host_string to user (optional) and host/port + user_hostport = host_string.rsplit('@', 1) + hostport = user_hostport.pop() + user = user_hostport[0] if user_hostport and user_hostport[0] else None + + # Split host/port string to host and optional port + # For IPv6 addresses square brackets are mandatory for host/port separation + if hostport.count(':') > 1: + # Looks like IPv6 address + r = ipv6_regex.match(hostport).groupdict() + host = r['host'] or None + port = r['port'] or None + else: + # Hostname or IPv4 address + host_port = hostport.rsplit(':', 1) + host = host_port.pop(0) or None + port = host_port[0] if host_port and host_port[0] else None + + return {'user': user, 'host': host, 'port': port} + + +def normalize(host_string, omit_port=False): + """ + Normalizes a given host string, returning explicit host, user, port. + + If ``omit_port`` is given and is True, only the host and user are returned. + + This function will process SSH config files if Fabric is configured to do + so, and will use them to fill in some default values or swap in hostname + aliases. + """ + from fabric.state import env + # Gracefully handle "empty" input by returning empty output + if not host_string: + return ('', '') if omit_port else ('', '', '') + # Parse host string (need this early on to look up host-specific ssh_config + # values) + r = parse_host_string(host_string) + host = r['host'] + # Env values (using defaults if somehow earlier defaults were replaced with + # empty values) + user = env.user or env.local_user + port = env.port or env.default_port + # SSH config data + conf = ssh_config(host_string) + # Only use ssh_config values if the env value appears unmodified from + # the true defaults. If the user has tweaked them, that new value + # takes precedence. + if user == env.local_user and 'user' in conf: + user = conf['user'] + if port == env.default_port and 'port' in conf: + port = conf['port'] + # Also override host if needed + if 'hostname' in conf: + host = conf['hostname'] + # Merge explicit user/port values with the env/ssh_config derived ones + # (Host is already done at this point.) + user = r['user'] or user + port = r['port'] or port + if omit_port: + return user, host + return user, host, port + + +def to_dict(host_string): + user, host, port = normalize(host_string) + return { + 'user': user, 'host': host, 'port': port, 'host_string': host_string + } + + +def from_dict(arg): + return join_host_strings(arg['user'], arg['host'], arg['port']) + + +def denormalize(host_string): + """ + Strips out default values for the given host string. + + If the user part is the default user, it is removed; + if the port is port 22, it also is removed. + """ + from fabric.state import env + + r = parse_host_string(host_string) + user = '' + if r['user'] is not None and r['user'] != env.user: + user = r['user'] + '@' + port = '' + if r['port'] is not None and r['port'] != '22': + port = ':' + r['port'] + host = r['host'] + host = '[%s]' % host if port and host.count(':') > 1 else host + return user + host + port + + +def join_host_strings(user, host, port=None): + """ + Turns user/host/port strings into ``user@host:port`` combined string. + + This function is not responsible for handling missing user/port strings; + for that, see the ``normalize`` function. + + If ``host`` looks like IPv6 address, it will be enclosed in square brackets + + If ``port`` is omitted, the returned string will be of the form + ``user@host``. + """ + if port: + # Square brackets are necessary for IPv6 host/port separation + template = "%s@[%s]:%s" if host.count(':') > 1 else "%s@%s:%s" + return template % (user, host, port) + else: + return "%s@%s" % (user, host) + + +def normalize_to_string(host_string): + """ + normalize() returns a tuple; this returns another valid host string. + """ + return join_host_strings(*normalize(host_string)) + + +def connect(user, host, port, cache, seek_gateway=True): + """ + Create and return a new SSHClient instance connected to given host. + + :param user: Username to connect as. + + :param host: Network hostname. + + :param port: SSH daemon port. + + :param cache: + A ``HostConnectionCache`` instance used to cache/store gateway hosts + when gatewaying is enabled. + + :param seek_gateway: + Whether to try setting up a gateway socket for this connection. Used so + the actual gateway connection can prevent recursion. + """ + from state import env, output + + # + # Initialization + # + + # Init client + client = ssh.SSHClient() + + # Load system hosts file (e.g. /etc/ssh/ssh_known_hosts) + known_hosts = env.get('system_known_hosts') + if known_hosts: + client.load_system_host_keys(known_hosts) + + # Load known host keys (e.g. ~/.ssh/known_hosts) unless user says not to. + if not env.disable_known_hosts: + client.load_system_host_keys() + # Unless user specified not to, accept/add new, unknown host keys + if not env.reject_unknown_hosts: + client.set_missing_host_key_policy(ssh.AutoAddPolicy()) + + # + # Connection attempt loop + # + + # Initialize loop variables + connected = False + password = get_password(user, host, port) + tries = 0 + sock = None + + # Loop until successful connect (keep prompting for new password) + while not connected: + # Attempt connection + try: + tries += 1 + + # (Re)connect gateway socket, if needed. + # Nuke cached client object if not on initial try. + if seek_gateway: + sock = get_gateway(host, port, cache, replace=tries > 0) + + # Ready to connect + client.connect( + hostname=host, + port=int(port), + username=user, + password=password, + pkey=key_from_env(password), + key_filename=key_filenames(), + timeout=env.timeout, + allow_agent=not env.no_agent, + look_for_keys=not env.no_keys, + sock=sock + ) + connected = True + + # set a keepalive if desired + if env.keepalive: + client.get_transport().set_keepalive(env.keepalive) + + return client + # BadHostKeyException corresponds to key mismatch, i.e. what on the + # command line results in the big banner error about man-in-the-middle + # attacks. + except ssh.BadHostKeyException, e: + raise NetworkError("Host key for %s did not match pre-existing key! Server's key was changed recently, or possible man-in-the-middle attack." % host, e) + # Prompt for new password to try on auth failure + except ( + ssh.AuthenticationException, + ssh.PasswordRequiredException, + ssh.SSHException + ), e: + msg = str(e) + # If we get SSHExceptionError and the exception message indicates + # SSH protocol banner read failures, assume it's caused by the + # server load and try again. + if e.__class__ is ssh.SSHException \ + and msg == 'Error reading SSH protocol banner': + if _tried_enough(tries): + raise NetworkError(msg, e) + continue + + # For whatever reason, empty password + no ssh key or agent + # results in an SSHException instead of an + # AuthenticationException. Since it's difficult to do + # otherwise, we must assume empty password + SSHException == + # auth exception. + # + # Conversely: if we get SSHException and there + # *was* a password -- it is probably something non auth + # related, and should be sent upwards. (This is not true if the + # exception message does indicate key parse problems.) + # + # This also holds true for rejected/unknown host keys: we have to + # guess based on other heuristics. + if e.__class__ is ssh.SSHException \ + and (password or msg.startswith('Unknown server')) \ + and not is_key_load_error(e): + raise NetworkError(msg, e) + + # Otherwise, assume an auth exception, and prompt for new/better + # password. + + # Paramiko doesn't handle prompting for locked private + # keys (i.e. keys with a passphrase and not loaded into an agent) + # so we have to detect this and tweak our prompt slightly. + # (Otherwise, however, the logic flow is the same, because + # ssh's connect() method overrides the password argument to be + # either the login password OR the private key passphrase. Meh.) + # + # NOTE: This will come up if you normally use a + # passphrase-protected private key with ssh-agent, and enter an + # incorrect remote username, because ssh.connect: + # * Tries the agent first, which will fail as you gave the wrong + # username, so obviously any loaded keys aren't gonna work for a + # nonexistent remote account; + # * Then tries the on-disk key file, which is passphrased; + # * Realizes there's no password to try unlocking that key with, + # because you didn't enter a password, because you're using + # ssh-agent; + # * In this condition (trying a key file, password is None) + # ssh raises PasswordRequiredException. + text = None + if e.__class__ is ssh.PasswordRequiredException \ + or is_key_load_error(e): + # NOTE: we can't easily say WHICH key's passphrase is needed, + # because ssh doesn't provide us with that info, and + # env.key_filename may be a list of keys, so we can't know + # which one raised the exception. Best not to try. + prompt = "[%s] Passphrase for private key" + text = prompt % env.host_string + password = prompt_for_password(text) + # Update env.password, env.passwords if empty + set_password(user, host, port, password) + # Ctrl-D / Ctrl-C for exit + except (EOFError, TypeError): + # Print a newline (in case user was sitting at prompt) + print('') + sys.exit(0) + # Handle DNS error / name lookup failure + except socket.gaierror, e: + raise NetworkError('Name lookup failed for %s' % host, e) + # Handle timeouts and retries, including generic errors + # NOTE: In 2.6, socket.error subclasses IOError + except socket.error, e: + not_timeout = type(e) is not socket.timeout + giving_up = _tried_enough(tries) + # Baseline error msg for when debug is off + msg = "Timed out trying to connect to %s" % host + # Expanded for debug on + err = msg + " (attempt %s of %s)" % (tries, env.connection_attempts) + if giving_up: + err += ", giving up" + err += ")" + # Debuggin' + if output.debug: + sys.stderr.write(err + '\n') + # Having said our piece, try again + if not giving_up: + # Sleep if it wasn't a timeout, so we still get timeout-like + # behavior + if not_timeout: + time.sleep(env.timeout) + continue + # Override eror msg if we were retrying other errors + if not_timeout: + msg = "Low level socket error connecting to host %s on port %s: %s" % ( + host, port, e[1] + ) + # Here, all attempts failed. Tweak error msg to show # tries. + # TODO: find good humanization module, jeez + s = "s" if env.connection_attempts > 1 else "" + msg += " (tried %s time%s)" % (env.connection_attempts, s) + raise NetworkError(msg, e) + # Ensure that if we terminated without connecting and we were given an + # explicit socket, close it out. + finally: + if not connected and sock is not None: + sock.close() + + +def _password_prompt(prompt, stream): + # NOTE: Using encode-to-ascii to prevent (Windows, at least) getpass from + # choking if given Unicode. + return getpass.getpass(prompt.encode('ascii', 'ignore'), stream) + +def prompt_for_password(prompt=None, no_colon=False, stream=None): + """ + Prompts for and returns a new password if required; otherwise, returns + None. + + A trailing colon is appended unless ``no_colon`` is True. + + If the user supplies an empty password, the user will be re-prompted until + they enter a non-empty password. + + ``prompt_for_password`` autogenerates the user prompt based on the current + host being connected to. To override this, specify a string value for + ``prompt``. + + ``stream`` is the stream the prompt will be printed to; if not given, + defaults to ``sys.stderr``. + """ + from fabric.state import env + handle_prompt_abort("a connection or sudo password") + stream = stream or sys.stderr + # Construct prompt + default = "[%s] Login password for '%s'" % (env.host_string, env.user) + password_prompt = prompt if (prompt is not None) else default + if not no_colon: + password_prompt += ": " + # Get new password value + new_password = _password_prompt(password_prompt, stream) + # Otherwise, loop until user gives us a non-empty password (to prevent + # returning the empty string, and to avoid unnecessary network overhead.) + while not new_password: + print("Sorry, you can't enter an empty password. Please try again.") + new_password = _password_prompt(password_prompt, stream) + return new_password + + +def needs_host(func): + """ + Prompt user for value of ``env.host_string`` when ``env.host_string`` is + empty. + + This decorator is basically a safety net for silly users who forgot to + specify the host/host list in one way or another. It should be used to wrap + operations which require a network connection. + + Due to how we execute commands per-host in ``main()``, it's not possible to + specify multiple hosts at this point in time, so only a single host will be + prompted for. + + Because this decorator sets ``env.host_string``, it will prompt once (and + only once) per command. As ``main()`` clears ``env.host_string`` between + commands, this decorator will also end up prompting the user once per + command (in the case where multiple commands have no hosts set, of course.) + """ + from fabric.state import env + @wraps(func) + def host_prompting_wrapper(*args, **kwargs): + while not env.get('host_string', False): + handle_prompt_abort("the target host connection string") + host_string = raw_input("No hosts found. Please specify (single)" + " host string for connection: ") + env.update(to_dict(host_string)) + return func(*args, **kwargs) + host_prompting_wrapper.undecorated = func + return host_prompting_wrapper + + +def disconnect_all(): + """ + Disconnect from all currently connected servers. + + Used at the end of ``fab``'s main loop, and also intended for use by + library users. + """ + from fabric.state import connections, output + # Explicitly disconnect from all servers + for key in connections.keys(): + if output.status: + # Here we can't use the py3k print(x, end=" ") + # because 2.5 backwards compatibility + sys.stdout.write("Disconnecting from %s... " % denormalize(key)) + connections[key].close() + del connections[key] + if output.status: + sys.stdout.write("done.\n") diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/operations.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/operations.py new file mode 100644 index 0000000..e721194 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/operations.py @@ -0,0 +1,1242 @@ +""" + +Functions to be used in fabfiles and other non-core code, such as run()/sudo(). +""" + +from __future__ import with_statement + +import os +import os.path +import posixpath +import re +import subprocess +import sys +import time +from glob import glob +from contextlib import closing, contextmanager + +from fabric.context_managers import (settings, char_buffered, hide, + quiet as quiet_manager, warn_only as warn_only_manager) +from fabric.io import output_loop, input_loop +from fabric.network import needs_host, ssh, ssh_config +from fabric.sftp import SFTP +from fabric.state import env, connections, output, win32, default_channel +from fabric.thread_handling import ThreadHandler +from fabric.utils import ( + abort, + error, + handle_prompt_abort, + indent, + _pty_size, + warn, + apply_lcwd +) + + +def _shell_escape(string): + """ + Escape double quotes, backticks and dollar signs in given ``string``. + + For example:: + + >>> _shell_escape('abc$') + 'abc\\\\$' + >>> _shell_escape('"') + '\\\\"' + """ + for char in ('"', '$', '`'): + string = string.replace(char, '\%s' % char) + return string + + +class _AttributeString(str): + """ + Simple string subclass to allow arbitrary attribute access. + """ + @property + def stdout(self): + return str(self) + + +class _AttributeList(list): + """ + Like _AttributeString, but for lists. + """ + pass + + +# Can't wait till Python versions supporting 'def func(*args, foo=bar)' become +# widespread :( +def require(*keys, **kwargs): + """ + Check for given keys in the shared environment dict and abort if not found. + + Positional arguments should be strings signifying what env vars should be + checked for. If any of the given arguments do not exist, Fabric will abort + execution and print the names of the missing keys. + + The optional keyword argument ``used_for`` may be a string, which will be + printed in the error output to inform users why this requirement is in + place. ``used_for`` is printed as part of a string similar to:: + + "Th(is|ese) variable(s) (are|is) used for %s" + + so format it appropriately. + + The optional keyword argument ``provided_by`` may be a list of functions or + function names or a single function or function name which the user should + be able to execute in order to set the key or keys; it will be included in + the error output if requirements are not met. + + Note: it is assumed that the keyword arguments apply to all given keys as a + group. If you feel the need to specify more than one ``used_for``, for + example, you should break your logic into multiple calls to ``require()``. + + .. versionchanged:: 1.1 + Allow iterable ``provided_by`` values instead of just single values. + """ + # If all keys exist and are non-empty, we're good, so keep going. + missing_keys = filter(lambda x: x not in env or (x in env and + isinstance(env[x], (dict, list, tuple, set)) and not env[x]), keys) + if not missing_keys: + return + # Pluralization + if len(missing_keys) > 1: + variable = "variables were" + used = "These variables are" + else: + variable = "variable was" + used = "This variable is" + # Regardless of kwargs, print what was missing. (Be graceful if used outside + # of a command.) + if 'command' in env: + prefix = "The command '%s' failed because the " % env.command + else: + prefix = "The " + msg = "%sfollowing required environment %s not defined:\n%s" % ( + prefix, variable, indent(missing_keys) + ) + # Print used_for if given + if 'used_for' in kwargs: + msg += "\n\n%s used for %s" % (used, kwargs['used_for']) + # And print provided_by if given + if 'provided_by' in kwargs: + funcs = kwargs['provided_by'] + # non-iterable is given, treat it as a list of this single item + if not hasattr(funcs, '__iter__'): + funcs = [funcs] + if len(funcs) > 1: + command = "one of the following commands" + else: + command = "the following command" + to_s = lambda obj: getattr(obj, '__name__', str(obj)) + provided_by = [to_s(obj) for obj in funcs] + msg += "\n\nTry running %s prior to this one, to fix the problem:\n%s"\ + % (command, indent(provided_by)) + abort(msg) + + +def prompt(text, key=None, default='', validate=None): + """ + Prompt user with ``text`` and return the input (like ``raw_input``). + + A single space character will be appended for convenience, but nothing + else. Thus, you may want to end your prompt text with a question mark or a + colon, e.g. ``prompt("What hostname?")``. + + If ``key`` is given, the user's input will be stored as ``env.`` in + addition to being returned by `prompt`. If the key already existed in + ``env``, its value will be overwritten and a warning printed to the user. + + If ``default`` is given, it is displayed in square brackets and used if the + user enters nothing (i.e. presses Enter without entering any text). + ``default`` defaults to the empty string. If non-empty, a space will be + appended, so that a call such as ``prompt("What hostname?", + default="foo")`` would result in a prompt of ``What hostname? [foo]`` (with + a trailing space after the ``[foo]``.) + + The optional keyword argument ``validate`` may be a callable or a string: + + * If a callable, it is called with the user's input, and should return the + value to be stored on success. On failure, it should raise an exception + with an exception message, which will be printed to the user. + * If a string, the value passed to ``validate`` is used as a regular + expression. It is thus recommended to use raw strings in this case. Note + that the regular expression, if it is not fully matching (bounded by + ``^`` and ``$``) it will be made so. In other words, the input must fully + match the regex. + + Either way, `prompt` will re-prompt until validation passes (or the user + hits ``Ctrl-C``). + + .. note:: + `~fabric.operations.prompt` honors :ref:`env.abort_on_prompts + ` and will call `~fabric.utils.abort` instead of + prompting if that flag is set to ``True``. If you want to block on user + input regardless, try wrapping with + `~fabric.context_managers.settings`. + + Examples:: + + # Simplest form: + environment = prompt('Please specify target environment: ') + + # With default, and storing as env.dish: + prompt('Specify favorite dish: ', 'dish', default='spam & eggs') + + # With validation, i.e. requiring integer input: + prompt('Please specify process nice level: ', key='nice', validate=int) + + # With validation against a regular expression: + release = prompt('Please supply a release name', + validate=r'^\w+-\d+(\.\d+)?$') + + # Prompt regardless of the global abort-on-prompts setting: + with settings(abort_on_prompts=False): + prompt('I seriously need an answer on this! ') + + """ + handle_prompt_abort("a user-specified prompt() call") + # Store previous env value for later display, if necessary + if key: + previous_value = env.get(key) + # Set up default display + default_str = "" + if default != '': + default_str = " [%s] " % str(default).strip() + else: + default_str = " " + # Construct full prompt string + prompt_str = text.strip() + default_str + # Loop until we pass validation + value = None + while value is None: + # Get input + value = raw_input(prompt_str) or default + # Handle validation + if validate: + # Callable + if callable(validate): + # Callable validate() must raise an exception if validation + # fails. + try: + value = validate(value) + except Exception, e: + # Reset value so we stay in the loop + value = None + print("Validation failed for the following reason:") + print(indent(e.message) + "\n") + # String / regex must match and will be empty if validation fails. + else: + # Need to transform regex into full-matching one if it's not. + if not validate.startswith('^'): + validate = r'^' + validate + if not validate.endswith('$'): + validate += r'$' + result = re.findall(validate, value) + if not result: + print("Regular expression validation failed: '%s' does not match '%s'\n" % (value, validate)) + # Reset value so we stay in the loop + value = None + # At this point, value must be valid, so update env if necessary + if key: + env[key] = value + # Print warning if we overwrote some other value + if key and previous_value is not None and previous_value != value: + warn("overwrote previous env variable '%s'; used to be '%s', is now '%s'." % ( + key, previous_value, value + )) + # And return the value, too, just in case someone finds that useful. + return value + + +@needs_host +def put(local_path=None, remote_path=None, use_sudo=False, + mirror_local_mode=False, mode=None, use_glob=True, temp_dir=""): + """ + Upload one or more files to a remote host. + + `~fabric.operations.put` returns an iterable containing the absolute file + paths of all remote files uploaded. This iterable also exhibits a + ``.failed`` attribute containing any local file paths which failed to + upload (and may thus be used as a boolean test.) You may also check + ``.succeeded`` which is equivalent to ``not .failed``. + + ``local_path`` may be a relative or absolute local file or directory path, + and may contain shell-style wildcards, as understood by the Python ``glob`` + module (give ``use_glob=False`` to disable this behavior). Tilde expansion + (as implemented by ``os.path.expanduser``) is also performed. + + ``local_path`` may alternately be a file-like object, such as the result of + ``open('path')`` or a ``StringIO`` instance. + + .. note:: + In this case, `~fabric.operations.put` will attempt to read the entire + contents of the file-like object by rewinding it using ``seek`` (and + will use ``tell`` afterwards to preserve the previous file position). + + ``remote_path`` may also be a relative or absolute location, but applied to + the remote host. Relative paths are relative to the remote user's home + directory, but tilde expansion (e.g. ``~/.ssh/``) will also be performed if + necessary. + + An empty string, in either path argument, will be replaced by the + appropriate end's current working directory. + + While the SFTP protocol (which `put` uses) has no direct ability to upload + files to locations not owned by the connecting user, you may specify + ``use_sudo=True`` to work around this. When set, this setting causes `put` + to upload the local files to a temporary location on the remote end + (defaults to remote user's ``$HOME``; this may be overridden via + ``temp_dir``), and then use `sudo` to move them to ``remote_path``. + + In some use cases, it is desirable to force a newly uploaded file to match + the mode of its local counterpart (such as when uploading executable + scripts). To do this, specify ``mirror_local_mode=True``. + + Alternately, you may use the ``mode`` kwarg to specify an exact mode, in + the same vein as ``os.chmod`` or the Unix ``chmod`` command. + + `~fabric.operations.put` will honor `~fabric.context_managers.cd`, so + relative values in ``remote_path`` will be prepended by the current remote + working directory, if applicable. Thus, for example, the below snippet + would attempt to upload to ``/tmp/files/test.txt`` instead of + ``~/files/test.txt``:: + + with cd('/tmp'): + put('/path/to/local/test.txt', 'files') + + Use of `~fabric.context_managers.lcd` will affect ``local_path`` in the + same manner. + + Examples:: + + put('bin/project.zip', '/tmp/project.zip') + put('*.py', 'cgi-bin/') + put('index.html', 'index.html', mode=0755) + + .. note:: + If a file-like object such as StringIO has a ``name`` attribute, that + will be used in Fabric's printed output instead of the default + ```` + .. versionchanged:: 1.0 + Now honors the remote working directory as manipulated by + `~fabric.context_managers.cd`, and the local working directory as + manipulated by `~fabric.context_managers.lcd`. + .. versionchanged:: 1.0 + Now allows file-like objects in the ``local_path`` argument. + .. versionchanged:: 1.0 + Directories may be specified in the ``local_path`` argument and will + trigger recursive uploads. + .. versionchanged:: 1.0 + Return value is now an iterable of uploaded remote file paths which + also exhibits the ``.failed`` and ``.succeeded`` attributes. + .. versionchanged:: 1.5 + Allow a ``name`` attribute on file-like objects for log output + .. versionchanged:: 1.7 + Added ``use_glob`` option to allow disabling of globbing. + """ + # Handle empty local path + local_path = local_path or os.getcwd() + + # Test whether local_path is a path or a file-like object + local_is_path = not (hasattr(local_path, 'read') \ + and callable(local_path.read)) + + ftp = SFTP(env.host_string) + + with closing(ftp) as ftp: + home = ftp.normalize('.') + + # Empty remote path implies cwd + remote_path = remote_path or home + + # Expand tildes + if remote_path.startswith('~'): + remote_path = remote_path.replace('~', home, 1) + + # Honor cd() (assumes Unix style file paths on remote end) + if not os.path.isabs(remote_path) and env.get('cwd'): + remote_path = env.cwd.rstrip('/') + '/' + remote_path + + if local_is_path: + # Apply lcwd, expand tildes, etc + local_path = os.path.expanduser(local_path) + local_path = apply_lcwd(local_path, env) + if use_glob: + # Glob local path + names = glob(local_path) + else: + # Check if file exists first so ValueError gets raised + if os.path.exists(local_path): + names = [local_path] + else: + names = [] + else: + names = [local_path] + + # Make sure local arg exists + if local_is_path and not names: + err = "'%s' is not a valid local path or glob." % local_path + raise ValueError(err) + + # Sanity check and wierd cases + if ftp.exists(remote_path): + if local_is_path and len(names) != 1 and not ftp.isdir(remote_path): + raise ValueError("'%s' is not a directory" % remote_path) + + # Iterate over all given local files + remote_paths = [] + failed_local_paths = [] + for lpath in names: + try: + if local_is_path and os.path.isdir(lpath): + p = ftp.put_dir(lpath, remote_path, use_sudo, + mirror_local_mode, mode, temp_dir) + remote_paths.extend(p) + else: + p = ftp.put(lpath, remote_path, use_sudo, mirror_local_mode, + mode, local_is_path, temp_dir) + remote_paths.append(p) + except Exception, e: + msg = "put() encountered an exception while uploading '%s'" + failure = lpath if local_is_path else "" + failed_local_paths.append(failure) + error(message=msg % lpath, exception=e) + + ret = _AttributeList(remote_paths) + ret.failed = failed_local_paths + ret.succeeded = not ret.failed + return ret + + +@needs_host +def get(remote_path, local_path=None): + """ + Download one or more files from a remote host. + + `~fabric.operations.get` returns an iterable containing the absolute paths + to all local files downloaded, which will be empty if ``local_path`` was a + StringIO object (see below for more on using StringIO). This object will + also exhibit a ``.failed`` attribute containing any remote file paths which + failed to download, and a ``.succeeded`` attribute equivalent to ``not + .failed``. + + ``remote_path`` is the remote file or directory path to download, which may + contain shell glob syntax, e.g. ``"/var/log/apache2/*.log"``, and will have + tildes replaced by the remote home directory. Relative paths will be + considered relative to the remote user's home directory, or the current + remote working directory as manipulated by `~fabric.context_managers.cd`. + If the remote path points to a directory, that directory will be downloaded + recursively. + + ``local_path`` is the local file path where the downloaded file or files + will be stored. If relative, it will honor the local current working + directory as manipulated by `~fabric.context_managers.lcd`. It may be + interpolated, using standard Python dict-based interpolation, with the + following variables: + + * ``host``: The value of ``env.host_string``, eg ``myhostname`` or + ``user@myhostname-222`` (the colon between hostname and port is turned + into a dash to maximize filesystem compatibility) + * ``dirname``: The directory part of the remote file path, e.g. the + ``src/projectname`` in ``src/projectname/utils.py``. + * ``basename``: The filename part of the remote file path, e.g. the + ``utils.py`` in ``src/projectname/utils.py`` + * ``path``: The full remote path, e.g. ``src/projectname/utils.py``. + + .. note:: + When ``remote_path`` is an absolute directory path, only the inner + directories will be recreated locally and passed into the above + variables. So for example, ``get('/var/log', '%(path)s')`` would start + writing out files like ``apache2/access.log``, + ``postgresql/8.4/postgresql.log``, etc, in the local working directory. + It would **not** write out e.g. ``var/log/apache2/access.log``. + + Additionally, when downloading a single file, ``%(dirname)s`` and + ``%(path)s`` do not make as much sense and will be empty and equivalent + to ``%(basename)s``, respectively. Thus a call like + ``get('/var/log/apache2/access.log', '%(path)s')`` will save a local + file named ``access.log``, not ``var/log/apache2/access.log``. + + This behavior is intended to be consistent with the command-line + ``scp`` program. + + If left blank, ``local_path`` defaults to ``"%(host)s/%(path)s"`` in order + to be safe for multi-host invocations. + + .. warning:: + If your ``local_path`` argument does not contain ``%(host)s`` and your + `~fabric.operations.get` call runs against multiple hosts, your local + files will be overwritten on each successive run! + + If ``local_path`` does not make use of the above variables (i.e. if it is a + simple, explicit file path) it will act similar to ``scp`` or ``cp``, + overwriting pre-existing files if necessary, downloading into a directory + if given (e.g. ``get('/path/to/remote_file.txt', 'local_directory')`` will + create ``local_directory/remote_file.txt``) and so forth. + + ``local_path`` may alternately be a file-like object, such as the result of + ``open('path', 'w')`` or a ``StringIO`` instance. + + .. note:: + Attempting to `get` a directory into a file-like object is not valid + and will result in an error. + + .. note:: + This function will use ``seek`` and ``tell`` to overwrite the entire + contents of the file-like object, in order to be consistent with the + behavior of `~fabric.operations.put` (which also considers the entire + file). However, unlike `~fabric.operations.put`, the file pointer will + not be restored to its previous location, as that doesn't make as much + sense here and/or may not even be possible. + + .. note:: + If a file-like object such as StringIO has a ``name`` attribute, that + will be used in Fabric's printed output instead of the default + ```` + + .. versionchanged:: 1.0 + Now honors the remote working directory as manipulated by + `~fabric.context_managers.cd`, and the local working directory as + manipulated by `~fabric.context_managers.lcd`. + .. versionchanged:: 1.0 + Now allows file-like objects in the ``local_path`` argument. + .. versionchanged:: 1.0 + ``local_path`` may now contain interpolated path- and host-related + variables. + .. versionchanged:: 1.0 + Directories may be specified in the ``remote_path`` argument and will + trigger recursive downloads. + .. versionchanged:: 1.0 + Return value is now an iterable of downloaded local file paths, which + also exhibits the ``.failed`` and ``.succeeded`` attributes. + .. versionchanged:: 1.5 + Allow a ``name`` attribute on file-like objects for log output + """ + # Handle empty local path / default kwarg value + local_path = local_path or "%(host)s/%(path)s" + + # Test whether local_path is a path or a file-like object + local_is_path = not (hasattr(local_path, 'write') \ + and callable(local_path.write)) + + # Honor lcd() where it makes sense + if local_is_path: + local_path = apply_lcwd(local_path, env) + + ftp = SFTP(env.host_string) + + with closing(ftp) as ftp: + home = ftp.normalize('.') + # Expand home directory markers (tildes, etc) + if remote_path.startswith('~'): + remote_path = remote_path.replace('~', home, 1) + if local_is_path: + local_path = os.path.expanduser(local_path) + + # Honor cd() (assumes Unix style file paths on remote end) + if not os.path.isabs(remote_path): + # Honor cwd if it's set (usually by with cd():) + if env.get('cwd'): + remote_path_escaped = env.cwd.rstrip('/') + remote_path_escaped = remote_path_escaped.replace('\\ ', ' ') + remote_path = remote_path_escaped + '/' + remote_path + # Otherwise, be relative to remote home directory (SFTP server's + # '.') + else: + remote_path = posixpath.join(home, remote_path) + + # Track final local destination files so we can return a list + local_files = [] + failed_remote_files = [] + + try: + # Glob remote path + names = ftp.glob(remote_path) + + # Handle invalid local-file-object situations + if not local_is_path: + if len(names) > 1 or ftp.isdir(names[0]): + error("[%s] %s is a glob or directory, but local_path is a file object!" % (env.host_string, remote_path)) + + for remote_path in names: + if ftp.isdir(remote_path): + result = ftp.get_dir(remote_path, local_path) + local_files.extend(result) + else: + # Perform actual get. If getting to real local file path, + # add result (will be true final path value) to + # local_files. File-like objects are omitted. + result = ftp.get(remote_path, local_path, local_is_path, + os.path.basename(remote_path)) + if local_is_path: + local_files.append(result) + + except Exception, e: + failed_remote_files.append(remote_path) + msg = "get() encountered an exception while downloading '%s'" + error(message=msg % remote_path, exception=e) + + ret = _AttributeList(local_files if local_is_path else []) + ret.failed = failed_remote_files + ret.succeeded = not ret.failed + return ret + + +def _sudo_prefix_argument(argument, value): + if value is None: + return "" + if str(value).isdigit(): + value = "#%s" % value + return ' %s "%s"' % (argument, value) + + +def _sudo_prefix(user, group=None): + """ + Return ``env.sudo_prefix`` with ``user``/``group`` inserted if necessary. + """ + # Insert env.sudo_prompt into env.sudo_prefix + prefix = env.sudo_prefix % env + if user is not None or group is not None: + return "%s%s%s " % (prefix, + _sudo_prefix_argument('-u', user), + _sudo_prefix_argument('-g', group)) + return prefix + + +def _shell_wrap(command, shell_escape, shell=True, sudo_prefix=None): + """ + Conditionally wrap given command in env.shell (while honoring sudo.) + """ + # Honor env.shell, while allowing the 'shell' kwarg to override it (at + # least in terms of turning it off.) + if shell and not env.use_shell: + shell = False + # Sudo plus space, or empty string + if sudo_prefix is None: + sudo_prefix = "" + else: + sudo_prefix += " " + # If we're shell wrapping, prefix shell and space. Next, escape the command + # if requested, and then quote it. Otherwise, empty string. + if shell: + shell = env.shell + " " + if shell_escape: + command = _shell_escape(command) + command = '"%s"' % command + else: + shell = "" + # Resulting string should now have correct formatting + return sudo_prefix + shell + command + + +def _prefix_commands(command, which): + """ + Prefixes ``command`` with all prefixes found in ``env.command_prefixes``. + + ``env.command_prefixes`` is a list of strings which is modified by the + `~fabric.context_managers.prefix` context manager. + + This function also handles a special-case prefix, ``cwd``, used by + `~fabric.context_managers.cd`. The ``which`` kwarg should be a string, + ``"local"`` or ``"remote"``, which will determine whether ``cwd`` or + ``lcwd`` is used. + """ + # Local prefix list (to hold env.command_prefixes + any special cases) + prefixes = list(env.command_prefixes) + # Handle current working directory, which gets its own special case due to + # being a path string that gets grown/shrunk, instead of just a single + # string or lack thereof. + # Also place it at the front of the list, in case user is expecting another + # prefixed command to be "in" the current working directory. + cwd = env.cwd if which == 'remote' else env.lcwd + if cwd: + prefixes.insert(0, 'cd %s' % cwd) + glue = " && " + prefix = (glue.join(prefixes) + glue) if prefixes else "" + return prefix + command + + +def _prefix_env_vars(command, local=False): + """ + Prefixes ``command`` with any shell environment vars, e.g. ``PATH=foo ``. + + Currently, this only applies the PATH updating implemented in + `~fabric.context_managers.path` and environment variables from + `~fabric.context_managers.shell_env`. + + Will switch to using Windows style 'SET' commands when invoked by + ``local()`` and on a Windows localhost. + """ + env_vars = {} + + # path(): local shell env var update, appending/prepending/replacing $PATH + path = env.path + if path: + if env.path_behavior == 'append': + path = '$PATH:\"%s\"' % path + elif env.path_behavior == 'prepend': + path = '\"%s\":$PATH' % path + elif env.path_behavior == 'replace': + path = '\"%s\"' % path + + env_vars['PATH'] = path + + # shell_env() + env_vars.update(env.shell_env) + + if env_vars: + set_cmd, exp_cmd = '', '' + if win32 and local: + set_cmd = 'SET ' + else: + exp_cmd = 'export ' + + exports = ' '.join( + '%s%s="%s"' % (set_cmd, k, v if k == 'PATH' else _shell_escape(v)) + for k, v in env_vars.iteritems() + ) + shell_env_str = '%s%s && ' % (exp_cmd, exports) + else: + shell_env_str = '' + + return shell_env_str + command + + +def _execute(channel, command, pty=True, combine_stderr=None, + invoke_shell=False, stdout=None, stderr=None, timeout=None): + """ + Execute ``command`` over ``channel``. + + ``pty`` controls whether a pseudo-terminal is created. + + ``combine_stderr`` controls whether we call ``channel.set_combine_stderr``. + By default, the global setting for this behavior (:ref:`env.combine_stderr + `) is consulted, but you may specify ``True`` or ``False`` + here to override it. + + ``invoke_shell`` controls whether we use ``exec_command`` or + ``invoke_shell`` (plus a handful of other things, such as always forcing a + pty.) + + Returns a three-tuple of (``stdout``, ``stderr``, ``status``), where + ``stdout``/``stderr`` are captured output strings and ``status`` is the + program's return code, if applicable. + """ + # stdout/stderr redirection + stdout = stdout or sys.stdout + stderr = stderr or sys.stderr + + # Timeout setting control + timeout = env.command_timeout if (timeout is None) else timeout + + # What to do with CTRl-C? + remote_interrupt = env.remote_interrupt + + with char_buffered(sys.stdin): + # Combine stdout and stderr to get around oddball mixing issues + if combine_stderr is None: + combine_stderr = env.combine_stderr + channel.set_combine_stderr(combine_stderr) + + # Assume pty use, and allow overriding of this either via kwarg or env + # var. (invoke_shell always wants a pty no matter what.) + using_pty = True + if not invoke_shell and (not pty or not env.always_use_pty): + using_pty = False + # Request pty with size params (default to 80x24, obtain real + # parameters if on POSIX platform) + if using_pty: + rows, cols = _pty_size() + channel.get_pty(width=cols, height=rows) + + # Use SSH agent forwarding from 'ssh' if enabled by user + config_agent = ssh_config().get('forwardagent', 'no').lower() == 'yes' + forward = None + if env.forward_agent or config_agent: + forward = ssh.agent.AgentRequestHandler(channel) + + # Kick off remote command + if invoke_shell: + channel.invoke_shell() + if command: + channel.sendall(command + "\n") + else: + channel.exec_command(command=command) + + # Init stdout, stderr capturing. Must use lists instead of strings as + # strings are immutable and we're using these as pass-by-reference + stdout_buf, stderr_buf = [], [] + if invoke_shell: + stdout_buf = stderr_buf = None + + workers = ( + ThreadHandler('out', output_loop, channel, "recv", + capture=stdout_buf, stream=stdout, timeout=timeout), + ThreadHandler('err', output_loop, channel, "recv_stderr", + capture=stderr_buf, stream=stderr, timeout=timeout), + ThreadHandler('in', input_loop, channel, using_pty) + ) + + if remote_interrupt is None: + remote_interrupt = invoke_shell + if remote_interrupt and not using_pty: + remote_interrupt = False + + while True: + if channel.exit_status_ready(): + break + else: + # Check for thread exceptions here so we can raise ASAP + # (without chance of getting blocked by, or hidden by an + # exception within, recv_exit_status()) + for worker in workers: + worker.raise_if_needed() + try: + time.sleep(ssh.io_sleep) + except KeyboardInterrupt: + if not remote_interrupt: + raise + channel.send('\x03') + + # Obtain exit code of remote program now that we're done. + status = channel.recv_exit_status() + + # Wait for threads to exit so we aren't left with stale threads + for worker in workers: + worker.thread.join() + worker.raise_if_needed() + + # Close channel + channel.close() + # Close any agent forward proxies + if forward is not None: + forward.close() + + # Update stdout/stderr with captured values if applicable + if not invoke_shell: + stdout_buf = ''.join(stdout_buf).strip() + stderr_buf = ''.join(stderr_buf).strip() + + # Tie off "loose" output by printing a newline. Helps to ensure any + # following print()s aren't on the same line as a trailing line prefix + # or similar. However, don't add an extra newline if we've already + # ended up with one, as that adds a entire blank line instead. + if output.running \ + and (output.stdout and stdout_buf and not stdout_buf.endswith("\n")) \ + or (output.stderr and stderr_buf and not stderr_buf.endswith("\n")): + print("") + + return stdout_buf, stderr_buf, status + + +@needs_host +def open_shell(command=None): + """ + Invoke a fully interactive shell on the remote end. + + If ``command`` is given, it will be sent down the pipe before handing + control over to the invoking user. + + This function is most useful for when you need to interact with a heavily + shell-based command or series of commands, such as when debugging or when + fully interactive recovery is required upon remote program failure. + + It should be considered an easy way to work an interactive shell session + into the middle of a Fabric script and is *not* a drop-in replacement for + `~fabric.operations.run`, which is also capable of interacting with the + remote end (albeit only while its given command is executing) and has much + stronger programmatic abilities such as error handling and stdout/stderr + capture. + + Specifically, `~fabric.operations.open_shell` provides a better interactive + experience than `~fabric.operations.run`, but use of a full remote shell + prevents Fabric from determining whether programs run within the shell have + failed, and pollutes the stdout/stderr stream with shell output such as + login banners, prompts and echoed stdin. + + Thus, this function does not have a return value and will not trigger + Fabric's failure handling if any remote programs result in errors. + + .. versionadded:: 1.0 + """ + _execute(channel=default_channel(), command=command, pty=True, + combine_stderr=True, invoke_shell=True) + + +@contextmanager +def _noop(): + yield + + +def _run_command(command, shell=True, pty=True, combine_stderr=True, + sudo=False, user=None, quiet=False, warn_only=False, stdout=None, + stderr=None, group=None, timeout=None, shell_escape=None): + """ + Underpinnings of `run` and `sudo`. See their docstrings for more info. + """ + manager = _noop + if warn_only: + manager = warn_only_manager + # Quiet's behavior is a superset of warn_only's, so it wins. + if quiet: + manager = quiet_manager + with manager(): + # Set up new var so original argument can be displayed verbatim later. + given_command = command + + # Check if shell_escape has been overridden in env + if shell_escape is None: + shell_escape = env.get('shell_escape', True) + + # Handle context manager modifications, and shell wrapping + wrapped_command = _shell_wrap( + _prefix_commands(_prefix_env_vars(command), 'remote'), + shell_escape, + shell, + _sudo_prefix(user, group) if sudo else None + ) + # Execute info line + which = 'sudo' if sudo else 'run' + if output.debug: + print("[%s] %s: %s" % (env.host_string, which, wrapped_command)) + elif output.running: + print("[%s] %s: %s" % (env.host_string, which, given_command)) + + # Actual execution, stdin/stdout/stderr handling, and termination + result_stdout, result_stderr, status = _execute( + channel=default_channel(), command=wrapped_command, pty=pty, + combine_stderr=combine_stderr, invoke_shell=False, stdout=stdout, + stderr=stderr, timeout=timeout) + + # Assemble output string + out = _AttributeString(result_stdout) + err = _AttributeString(result_stderr) + + # Error handling + out.failed = False + out.command = given_command + out.real_command = wrapped_command + if status not in env.ok_ret_codes: + out.failed = True + msg = "%s() received nonzero return code %s while executing" % ( + which, status + ) + if env.warn_only: + msg += " '%s'!" % given_command + else: + msg += "!\n\nRequested: %s\nExecuted: %s" % ( + given_command, wrapped_command + ) + error(message=msg, stdout=out, stderr=err) + + # Attach return code to output string so users who have set things to + # warn only, can inspect the error code. + out.return_code = status + + # Convenience mirror of .failed + out.succeeded = not out.failed + + # Attach stderr for anyone interested in that. + out.stderr = err + + return out + + +@needs_host +def run(command, shell=True, pty=True, combine_stderr=None, quiet=False, + warn_only=False, stdout=None, stderr=None, timeout=None, shell_escape=None): + """ + Run a shell command on a remote host. + + If ``shell`` is True (the default), `run` will execute the given command + string via a shell interpreter, the value of which may be controlled by + setting ``env.shell`` (defaulting to something similar to ``/bin/bash -l -c + ""``.) Any double-quote (``"``) or dollar-sign (``$``) characters + in ``command`` will be automatically escaped when ``shell`` is True. + + `run` will return the result of the remote program's stdout as a single + (likely multiline) string. This string will exhibit ``failed`` and + ``succeeded`` boolean attributes specifying whether the command failed or + succeeded, and will also include the return code as the ``return_code`` + attribute. Furthermore, it includes a copy of the requested & actual + command strings executed, as ``.command`` and ``.real_command``, + respectively. + + Any text entered in your local terminal will be forwarded to the remote + program as it runs, thus allowing you to interact with password or other + prompts naturally. For more on how this works, see + :doc:`/usage/interactivity`. + + You may pass ``pty=False`` to forego creation of a pseudo-terminal on the + remote end in case the presence of one causes problems for the command in + question. However, this will force Fabric itself to echo any and all input + you type while the command is running, including sensitive passwords. (With + ``pty=True``, the remote pseudo-terminal will echo for you, and will + intelligently handle password-style prompts.) See :ref:`pseudottys` for + details. + + Similarly, if you need to programmatically examine the stderr stream of the + remote program (exhibited as the ``stderr`` attribute on this function's + return value), you may set ``combine_stderr=False``. Doing so has a high + chance of causing garbled output to appear on your terminal (though the + resulting strings returned by `~fabric.operations.run` will be properly + separated). For more info, please read :ref:`combine_streams`. + + To ignore non-zero return codes, specify ``warn_only=True``. To both ignore + non-zero return codes *and* force a command to run silently, specify + ``quiet=True``. + + To override which local streams are used to display remote stdout and/or + stderr, specify ``stdout`` or ``stderr``. (By default, the regular + ``sys.stdout`` and ``sys.stderr`` Python stream objects are used.) + + For example, ``run("command", stderr=sys.stdout)`` would print the remote + standard error to the local standard out, while preserving it as its own + distinct attribute on the return value (as per above.) Alternately, you + could even provide your own stream objects or loggers, e.g. ``myout = + StringIO(); run("command", stdout=myout)``. + + If you want an exception raised when the remote program takes too long to + run, specify ``timeout=N`` where ``N`` is an integer number of seconds, + after which to time out. This will cause ``run`` to raise a + `~fabric.exceptions.CommandTimeout` exception. + + If you want to disable Fabric's automatic attempts at escaping quotes, + dollar signs etc., specify ``shell_escape=False``. + + Examples:: + + run("ls /var/www/") + run("ls /home/myuser", shell=False) + output = run('ls /var/www/site1') + run("take_a_long_time", timeout=5) + + .. versionadded:: 1.0 + The ``succeeded`` and ``stderr`` return value attributes, the + ``combine_stderr`` kwarg, and interactive behavior. + + .. versionchanged:: 1.0 + The default value of ``pty`` is now ``True``. + + .. versionchanged:: 1.0.2 + The default value of ``combine_stderr`` is now ``None`` instead of + ``True``. However, the default *behavior* is unchanged, as the global + setting is still ``True``. + + .. versionadded:: 1.5 + The ``quiet``, ``warn_only``, ``stdout`` and ``stderr`` kwargs. + + .. versionadded:: 1.5 + The return value attributes ``.command`` and ``.real_command``. + + .. versionadded:: 1.6 + The ``timeout`` argument. + + .. versionadded:: 1.7 + The ``shell_escape`` argument. + """ + return _run_command(command, shell, pty, combine_stderr, quiet=quiet, + warn_only=warn_only, stdout=stdout, stderr=stderr, timeout=timeout, + shell_escape=shell_escape) + + +@needs_host +def sudo(command, shell=True, pty=True, combine_stderr=None, user=None, + quiet=False, warn_only=False, stdout=None, stderr=None, group=None, + timeout=None, shell_escape=None): + """ + Run a shell command on a remote host, with superuser privileges. + + `sudo` is identical in every way to `run`, except that it will always wrap + the given ``command`` in a call to the ``sudo`` program to provide + superuser privileges. + + `sudo` accepts additional ``user`` and ``group`` arguments, which are + passed to ``sudo`` and allow you to run as some user and/or group other + than root. On most systems, the ``sudo`` program can take a string + username/group or an integer userid/groupid (uid/gid); ``user`` and + ``group`` may likewise be strings or integers. + + You may set :ref:`env.sudo_user ` at module level or via + `~fabric.context_managers.settings` if you want multiple ``sudo`` calls to + have the same ``user`` value. An explicit ``user`` argument will, of + course, override this global setting. + + Examples:: + + sudo("~/install_script.py") + sudo("mkdir /var/www/new_docroot", user="www-data") + sudo("ls /home/jdoe", user=1001) + result = sudo("ls /tmp/") + with settings(sudo_user='mysql'): + sudo("whoami") # prints 'mysql' + + .. versionchanged:: 1.0 + See the changed and added notes for `~fabric.operations.run`. + + .. versionchanged:: 1.5 + Now honors :ref:`env.sudo_user `. + + .. versionadded:: 1.5 + The ``quiet``, ``warn_only``, ``stdout`` and ``stderr`` kwargs. + + .. versionadded:: 1.5 + The return value attributes ``.command`` and ``.real_command``. + + .. versionadded:: 1.7 + The ``shell_escape`` argument. + """ + return _run_command( + command, shell, pty, combine_stderr, sudo=True, + user=user if user else env.sudo_user, + group=group, quiet=quiet, warn_only=warn_only, stdout=stdout, + stderr=stderr, timeout=timeout, shell_escape=shell_escape, + ) + + +def local(command, capture=False, shell=None): + """ + Run a command on the local system. + + `local` is simply a convenience wrapper around the use of the builtin + Python ``subprocess`` module with ``shell=True`` activated. If you need to + do anything special, consider using the ``subprocess`` module directly. + + ``shell`` is passed directly to `subprocess.Popen + `_'s + ``execute`` argument (which determines the local shell to use.) As per the + linked documentation, on Unix the default behavior is to use ``/bin/sh``, + so this option is useful for setting that value to e.g. ``/bin/bash``. + + `local` is not currently capable of simultaneously printing and + capturing output, as `~fabric.operations.run`/`~fabric.operations.sudo` + do. The ``capture`` kwarg allows you to switch between printing and + capturing as necessary, and defaults to ``False``. + + When ``capture=False``, the local subprocess' stdout and stderr streams are + hooked up directly to your terminal, though you may use the global + :doc:`output controls ` ``output.stdout`` and + ``output.stderr`` to hide one or both if desired. In this mode, the return + value's stdout/stderr values are always empty. + + When ``capture=True``, you will not see any output from the subprocess in + your terminal, but the return value will contain the captured + stdout/stderr. + + In either case, as with `~fabric.operations.run` and + `~fabric.operations.sudo`, this return value exhibits the ``return_code``, + ``stderr``, ``failed``, ``succeeded``, ``command`` and ``real_command`` + attributes. See `run` for details. + + `~fabric.operations.local` will honor the `~fabric.context_managers.lcd` + context manager, allowing you to control its current working directory + independently of the remote end (which honors + `~fabric.context_managers.cd`). + + .. versionchanged:: 1.0 + Added the ``succeeded`` and ``stderr`` attributes. + .. versionchanged:: 1.0 + Now honors the `~fabric.context_managers.lcd` context manager. + .. versionchanged:: 1.0 + Changed the default value of ``capture`` from ``True`` to ``False``. + .. versionadded:: 1.9 + The return value attributes ``.command`` and ``.real_command``. + """ + given_command = command + # Apply cd(), path() etc + with_env = _prefix_env_vars(command, local=True) + wrapped_command = _prefix_commands(with_env, 'local') + if output.debug: + print("[localhost] local: %s" % (wrapped_command)) + elif output.running: + print("[localhost] local: " + given_command) + # Tie in to global output controls as best we can; our capture argument + # takes precedence over the output settings. + dev_null = None + if capture: + out_stream = subprocess.PIPE + err_stream = subprocess.PIPE + else: + dev_null = open(os.devnull, 'w+') + # Non-captured, hidden streams are discarded. + out_stream = None if output.stdout else dev_null + err_stream = None if output.stderr else dev_null + try: + cmd_arg = wrapped_command if win32 else [wrapped_command] + if shell is not None: + p = subprocess.Popen(cmd_arg, shell=True, stdout=out_stream, + stderr=err_stream, executable=shell) + else: + p = subprocess.Popen(cmd_arg, shell=True, stdout=out_stream, + stderr=err_stream) + (stdout, stderr) = p.communicate() + finally: + if dev_null is not None: + dev_null.close() + # Handle error condition (deal with stdout being None, too) + out = _AttributeString(stdout.strip() if stdout else "") + err = _AttributeString(stderr.strip() if stderr else "") + out.command = given_command + out.real_command = wrapped_command + out.failed = False + out.return_code = p.returncode + out.stderr = err + if p.returncode not in env.ok_ret_codes: + out.failed = True + msg = "local() encountered an error (return code %s) while executing '%s'" % (p.returncode, command) + error(message=msg, stdout=out, stderr=err) + out.succeeded = not out.failed + # If we were capturing, this will be a string; otherwise it will be None. + return out + + +@needs_host +def reboot(wait=120, command='reboot'): + """ + Reboot the remote system. + + Will temporarily tweak Fabric's reconnection settings (:ref:`timeout` and + :ref:`connection-attempts`) to ensure that reconnection does not give up + for at least ``wait`` seconds. + + .. note:: + As of Fabric 1.4, the ability to reconnect partway through a session no + longer requires use of internal APIs. While we are not officially + deprecating this function, adding more features to it will not be a + priority. + + Users who want greater control + are encouraged to check out this function's (6 lines long, well + commented) source code and write their own adaptation using different + timeout/attempt values or additional logic. + + .. versionadded:: 0.9.2 + .. versionchanged:: 1.4 + Changed the ``wait`` kwarg to be optional, and refactored to leverage + the new reconnection functionality; it may not actually have to wait + for ``wait`` seconds before reconnecting. + """ + # Shorter timeout for a more granular cycle than the default. + timeout = 5 + # Use 'wait' as max total wait time + attempts = int(round(float(wait) / float(timeout))) + # Don't bleed settings, since this is supposed to be self-contained. + # User adaptations will probably want to drop the "with settings()" and + # just have globally set timeout/attempts values. + with settings( + hide('running'), + timeout=timeout, + connection_attempts=attempts + ): + sudo(command) + # Try to make sure we don't slip in before pre-reboot lockdown + time.sleep(5) + # This is actually an internal-ish API call, but users can simply drop + # it in real fabfile use -- the next run/sudo/put/get/etc call will + # automatically trigger a reconnect. + # We use it here to force the reconnect while this function is still in + # control and has the above timeout settings enabled. + connections.connect(env.host_string) + # At this point we should be reconnected to the newly rebooted server. diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/sftp.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/sftp.py new file mode 100644 index 0000000..bd7c753 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/sftp.py @@ -0,0 +1,289 @@ +from __future__ import with_statement + +import hashlib +import os +import posixpath +import stat +import re +from fnmatch import filter as fnfilter + +from fabric.state import output, connections, env +from fabric.utils import warn +from fabric.context_managers import settings + + +def _format_local(local_path, local_is_path): + """Format a path for log output""" + if local_is_path: + return local_path + else: + # This allows users to set a name attr on their StringIO objects + # just like an open file object would have + return getattr(local_path, 'name', '') + + +class SFTP(object): + """ + SFTP helper class, which is also a facade for ssh.SFTPClient. + """ + def __init__(self, host_string): + self.ftp = connections[host_string].open_sftp() + + # Recall that __getattr__ is the "fallback" attribute getter, and is thus + # pretty safe to use for facade-like behavior as we're doing here. + def __getattr__(self, attr): + return getattr(self.ftp, attr) + + def isdir(self, path): + try: + return stat.S_ISDIR(self.ftp.lstat(path).st_mode) + except IOError: + return False + + def islink(self, path): + try: + return stat.S_ISLNK(self.ftp.lstat(path).st_mode) + except IOError: + return False + + def exists(self, path): + try: + self.ftp.lstat(path).st_mode + except IOError: + return False + return True + + def glob(self, path): + from fabric.state import win32 + dirpart, pattern = os.path.split(path) + rlist = self.ftp.listdir(dirpart) + + names = fnfilter([f for f in rlist if not f[0] == '.'], pattern) + ret = [path] + if len(names): + s = '/' + ret = [dirpart.rstrip(s) + s + name.lstrip(s) for name in names] + if not win32: + ret = [posixpath.join(dirpart, name) for name in names] + return ret + + def walk(self, top, topdown=True, onerror=None, followlinks=False): + from os.path import join + + # We may not have read permission for top, in which case we can't get a + # list of the files the directory contains. os.path.walk always + # suppressed the exception then, rather than blow up for a minor reason + # when (say) a thousand readable directories are still left to visit. + # That logic is copied here. + try: + # Note that listdir and error are globals in this module due to + # earlier import-*. + names = self.ftp.listdir(top) + except Exception, err: + if onerror is not None: + onerror(err) + return + + dirs, nondirs = [], [] + for name in names: + if self.isdir(join(top, name)): + dirs.append(name) + else: + nondirs.append(name) + + if topdown: + yield top, dirs, nondirs + + for name in dirs: + path = join(top, name) + if followlinks or not self.islink(path): + for x in self.walk(path, topdown, onerror, followlinks): + yield x + if not topdown: + yield top, dirs, nondirs + + def mkdir(self, path, use_sudo): + from fabric.api import sudo, hide + if use_sudo: + with hide('everything'): + sudo('mkdir "%s"' % path) + else: + self.ftp.mkdir(path) + + def get(self, remote_path, local_path, local_is_path, rremote=None): + # rremote => relative remote path, so get(/var/log) would result in + # this function being called with + # remote_path=/var/log/apache2/access.log and + # rremote=apache2/access.log + rremote = rremote if rremote is not None else remote_path + # Handle format string interpolation (e.g. %(dirname)s) + path_vars = { + 'host': env.host_string.replace(':', '-'), + 'basename': os.path.basename(rremote), + 'dirname': os.path.dirname(rremote), + 'path': rremote + } + if local_is_path: + # Naive fix to issue #711 + escaped_path = re.sub(r'(%[^()]*\w)', r'%\1', local_path) + local_path = os.path.abspath(escaped_path % path_vars ) + + # Ensure we give ssh.SFTPCLient a file by prepending and/or + # creating local directories as appropriate. + dirpath, filepath = os.path.split(local_path) + if dirpath and not os.path.exists(dirpath): + os.makedirs(dirpath) + if os.path.isdir(local_path): + local_path = os.path.join(local_path, path_vars['basename']) + if output.running: + print("[%s] download: %s <- %s" % ( + env.host_string, + _format_local(local_path, local_is_path), + remote_path + )) + # Warn about overwrites, but keep going + if local_is_path and os.path.exists(local_path): + msg = "Local file %s already exists and is being overwritten." + warn(msg % local_path) + # File-like objects: reset to file seek 0 (to ensure full overwrite) + # and then use Paramiko's getfo() directly + getter = self.ftp.get + if not local_is_path: + local_path.seek(0) + getter = self.ftp.getfo + getter(remote_path, local_path) + # Return local_path object for posterity. (If mutated, caller will want + # to know.) + return local_path + + def get_dir(self, remote_path, local_path): + # Decide what needs to be stripped from remote paths so they're all + # relative to the given remote_path + if os.path.basename(remote_path): + strip = os.path.dirname(remote_path) + else: + strip = os.path.dirname(os.path.dirname(remote_path)) + + # Store all paths gotten so we can return them when done + result = [] + # Use our facsimile of os.walk to find all files within remote_path + for context, dirs, files in self.walk(remote_path): + # Normalize current directory to be relative + # E.g. remote_path of /var/log and current dir of /var/log/apache2 + # would be turned into just 'apache2' + lcontext = rcontext = context.replace(strip, '', 1).lstrip('/') + # Prepend local path to that to arrive at the local mirrored + # version of this directory. So if local_path was 'mylogs', we'd + # end up with 'mylogs/apache2' + lcontext = os.path.join(local_path, lcontext) + + # Download any files in current directory + for f in files: + # Construct full and relative remote paths to this file + rpath = posixpath.join(context, f) + rremote = posixpath.join(rcontext, f) + # If local_path isn't using a format string that expands to + # include its remote path, we need to add it here. + if "%(path)s" not in local_path \ + and "%(dirname)s" not in local_path: + lpath = os.path.join(lcontext, f) + # Otherwise, just passthrough local_path to self.get() + else: + lpath = local_path + # Now we can make a call to self.get() with specific file paths + # on both ends. + result.append(self.get(rpath, lpath, True, rremote)) + return result + + def put(self, local_path, remote_path, use_sudo, mirror_local_mode, mode, + local_is_path, temp_dir): + from fabric.api import sudo, hide + pre = self.ftp.getcwd() + pre = pre if pre else '' + if local_is_path and self.isdir(remote_path): + basename = os.path.basename(local_path) + remote_path = posixpath.join(remote_path, basename) + if output.running: + print("[%s] put: %s -> %s" % ( + env.host_string, + _format_local(local_path, local_is_path), + posixpath.join(pre, remote_path) + )) + # When using sudo, "bounce" the file through a guaranteed-unique file + # path in the default remote CWD (which, typically, the login user will + # have write permissions on) in order to sudo(mv) it later. + if use_sudo: + target_path = remote_path + hasher = hashlib.sha1() + hasher.update(env.host_string) + hasher.update(target_path) + remote_path = posixpath.join(temp_dir, hasher.hexdigest()) + # Read, ensuring we handle file-like objects correct re: seek pointer + putter = self.ftp.put + if not local_is_path: + old_pointer = local_path.tell() + local_path.seek(0) + putter = self.ftp.putfo + rattrs = putter(local_path, remote_path) + if not local_is_path: + local_path.seek(old_pointer) + # Handle modes if necessary + if (local_is_path and mirror_local_mode) or (mode is not None): + lmode = os.stat(local_path).st_mode if mirror_local_mode else mode + # Cast to octal integer in case of string + if isinstance(lmode, basestring): + lmode = int(lmode, 8) + lmode = lmode & 07777 + rmode = rattrs.st_mode + # Only bitshift if we actually got an rmode + if rmode is not None: + rmode = (rmode & 07777) + if lmode != rmode: + if use_sudo: + # Temporarily nuke 'cwd' so sudo() doesn't "cd" its mv + # command. (The target path has already been cwd-ified + # elsewhere.) + with settings(hide('everything'), cwd=""): + sudo('chmod %o \"%s\"' % (lmode, remote_path)) + else: + self.ftp.chmod(remote_path, lmode) + if use_sudo: + # Temporarily nuke 'cwd' so sudo() doesn't "cd" its mv command. + # (The target path has already been cwd-ified elsewhere.) + with settings(hide('everything'), cwd=""): + sudo("mv \"%s\" \"%s\"" % (remote_path, target_path)) + # Revert to original remote_path for return value's sake + remote_path = target_path + return remote_path + + def put_dir(self, local_path, remote_path, use_sudo, mirror_local_mode, + mode, temp_dir): + if os.path.basename(local_path): + strip = os.path.dirname(local_path) + else: + strip = os.path.dirname(os.path.dirname(local_path)) + + remote_paths = [] + + for context, dirs, files in os.walk(local_path): + rcontext = context.replace(strip, '', 1) + # normalize pathname separators with POSIX separator + rcontext = rcontext.replace(os.sep, '/') + rcontext = rcontext.lstrip('/') + rcontext = posixpath.join(remote_path, rcontext) + + if not self.exists(rcontext): + self.mkdir(rcontext, use_sudo) + + for d in dirs: + n = posixpath.join(rcontext, d) + if not self.exists(n): + self.mkdir(n, use_sudo) + + for f in files: + local_path = os.path.join(context, f) + n = posixpath.join(rcontext, f) + p = self.put(local_path, n, use_sudo, mirror_local_mode, mode, + True, temp_dir) + remote_paths.append(p) + return remote_paths diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/state.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/state.py new file mode 100644 index 0000000..fd3a91c --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/state.py @@ -0,0 +1,428 @@ +""" +Internal shared-state variables such as config settings and host lists. +""" + +import os +import sys +from optparse import make_option + +from fabric.network import HostConnectionCache, ssh +from fabric.version import get_version +from fabric.utils import _AliasDict, _AttributeDict + + +# +# Win32 flag +# + +# Impacts a handful of platform specific behaviors. Note that Cygwin's Python +# is actually close enough to "real" UNIXes that it doesn't need (or want!) to +# use PyWin32 -- so we only test for literal Win32 setups (vanilla Python, +# ActiveState etc) here. +win32 = (sys.platform == 'win32') + + +# +# Environment dictionary - support structures +# + +# By default, if the user (including code using Fabric as a library) doesn't +# set the username, we obtain the currently running username and use that. +def _get_system_username(): + """ + Obtain name of current system user, which will be default connection user. + """ + import getpass + username = None + try: + username = getpass.getuser() + # getpass.getuser supported on both Unix and Windows systems. + # getpass.getuser may call pwd.getpwuid which in turns may raise KeyError + # if it cannot find a username for the given UID, e.g. on ep.io + # and similar "non VPS" style services. Rather than error out, just keep + # the 'default' username to None. Can check for this value later if needed. + except KeyError: + pass + except ImportError: + if win32: + import win32api + import win32security + import win32profile + username = win32api.GetUserName() + return username + +def _rc_path(): + """ + Return platform-specific default file path for $HOME/.fabricrc. + """ + rc_file = '.fabricrc' + rc_path = '~/' + rc_file + expanded_rc_path = os.path.expanduser(rc_path) + if expanded_rc_path == rc_path and win32: + from win32com.shell.shell import SHGetSpecialFolderPath + from win32com.shell.shellcon import CSIDL_PROFILE + expanded_rc_path = "%s/%s" % ( + SHGetSpecialFolderPath(0, CSIDL_PROFILE), + rc_file + ) + return expanded_rc_path + +default_port = '22' # hurr durr +default_ssh_config_path = '~/.ssh/config' + +# Options/settings which exist both as environment keys and which can be set on +# the command line, are defined here. When used via `fab` they will be added to +# the optparse parser, and either way they are added to `env` below (i.e. the +# 'dest' value becomes the environment key and the value, the env value). +# +# Keep in mind that optparse changes hyphens to underscores when automatically +# deriving the `dest` name, e.g. `--reject-unknown-hosts` becomes +# `reject_unknown_hosts`. +# +# Furthermore, *always* specify some sort of default to avoid ending up with +# optparse.NO_DEFAULT (currently a two-tuple)! In general, None is a better +# default than ''. +# +# User-facing documentation for these are kept in sites/docs/env.rst. +env_options = [ + + make_option('-a', '--no_agent', + action='store_true', + default=False, + help="don't use the running SSH agent" + ), + + make_option('-A', '--forward-agent', + action='store_true', + default=False, + help="forward local agent to remote end" + ), + + make_option('--abort-on-prompts', + action='store_true', + default=False, + help="abort instead of prompting (for password, host, etc)" + ), + + make_option('-c', '--config', + dest='rcfile', + default=_rc_path(), + metavar='PATH', + help="specify location of config file to use" + ), + + make_option('--colorize-errors', + action='store_true', + default=False, + help="Color error output", + ), + + make_option('-D', '--disable-known-hosts', + action='store_true', + default=False, + help="do not load user known_hosts file" + ), + + make_option('-e', '--eagerly-disconnect', + action='store_true', + default=False, + help="disconnect from hosts as soon as possible" + ), + + make_option('-f', '--fabfile', + default='fabfile', + metavar='PATH', + help="python module file to import, e.g. '../other.py'" + ), + + make_option('-g', '--gateway', + default=None, + metavar='HOST', + help="gateway host to connect through" + ), + + make_option('--hide', + metavar='LEVELS', + help="comma-separated list of output levels to hide" + ), + + make_option('-H', '--hosts', + default=[], + help="comma-separated list of hosts to operate on" + ), + + make_option('-i', + action='append', + dest='key_filename', + metavar='PATH', + default=None, + help="path to SSH private key file. May be repeated." + ), + + make_option('-k', '--no-keys', + action='store_true', + default=False, + help="don't load private key files from ~/.ssh/" + ), + + make_option('--keepalive', + dest='keepalive', + type=int, + default=0, + metavar="N", + help="enables a keepalive every N seconds" + ), + + make_option('--linewise', + action='store_true', + default=False, + help="print line-by-line instead of byte-by-byte" + ), + + make_option('-n', '--connection-attempts', + type='int', + metavar='M', + dest='connection_attempts', + default=1, + help="make M attempts to connect before giving up" + ), + + make_option('--no-pty', + dest='always_use_pty', + action='store_false', + default=True, + help="do not use pseudo-terminal in run/sudo" + ), + + make_option('-p', '--password', + default=None, + help="password for use with authentication and/or sudo" + ), + + make_option('-P', '--parallel', + dest='parallel', + action='store_true', + default=False, + help="default to parallel execution method" + ), + + make_option('--port', + default=default_port, + help="SSH connection port" + ), + + make_option('-r', '--reject-unknown-hosts', + action='store_true', + default=False, + help="reject unknown hosts" + ), + + make_option('--system-known-hosts', + default=None, + help="load system known_hosts file before reading user known_hosts" + ), + + make_option('-R', '--roles', + default=[], + help="comma-separated list of roles to operate on" + ), + + make_option('-s', '--shell', + default='/bin/bash -l -c', + help="specify a new shell, defaults to '/bin/bash -l -c'" + ), + + make_option('--show', + metavar='LEVELS', + help="comma-separated list of output levels to show" + ), + + make_option('--skip-bad-hosts', + action="store_true", + default=False, + help="skip over hosts that can't be reached" + ), + + make_option('--ssh-config-path', + default=default_ssh_config_path, + metavar='PATH', + help="Path to SSH config file" + ), + + make_option('-t', '--timeout', + type='int', + default=10, + metavar="N", + help="set connection timeout to N seconds" + ), + + make_option('-T', '--command-timeout', + dest='command_timeout', + type='int', + default=None, + metavar="N", + help="set remote command timeout to N seconds" + ), + + make_option('-u', '--user', + default=_get_system_username(), + help="username to use when connecting to remote hosts" + ), + + make_option('-w', '--warn-only', + action='store_true', + default=False, + help="warn, instead of abort, when commands fail" + ), + + make_option('-x', '--exclude-hosts', + default=[], + metavar='HOSTS', + help="comma-separated list of hosts to exclude" + ), + + make_option('-z', '--pool-size', + dest='pool_size', + type='int', + metavar='INT', + default=0, + help="number of concurrent processes to use in parallel mode", + ), + +] + + +# +# Environment dictionary - actual dictionary object +# + + +# Global environment dict. Currently a catchall for everything: config settings +# such as global deep/broad mode, host lists, username etc. +# Most default values are specified in `env_options` above, in the interests of +# preserving DRY: anything in here is generally not settable via the command +# line. +env = _AttributeDict({ + 'abort_exception': None, + 'again_prompt': 'Sorry, try again.', + 'all_hosts': [], + 'combine_stderr': True, + 'colorize_errors': False, + 'command': None, + 'command_prefixes': [], + 'cwd': '', # Must be empty string, not None, for concatenation purposes + 'dedupe_hosts': True, + 'default_port': default_port, + 'eagerly_disconnect': False, + 'echo_stdin': True, + 'effective_roles': [], + 'exclude_hosts': [], + 'gateway': None, + 'host': None, + 'host_string': None, + 'lcwd': '', # Must be empty string, not None, for concatenation purposes + 'local_user': _get_system_username(), + 'output_prefix': True, + 'passwords': {}, + 'path': '', + 'path_behavior': 'append', + 'port': default_port, + 'real_fabfile': None, + 'remote_interrupt': None, + 'roles': [], + 'roledefs': {}, + 'shell_env': {}, + 'skip_bad_hosts': False, + 'ssh_config_path': default_ssh_config_path, + 'ok_ret_codes': [0], # a list of return codes that indicate success + # -S so sudo accepts passwd via stdin, -p with our known-value prompt for + # later detection (thus %s -- gets filled with env.sudo_prompt at runtime) + 'sudo_prefix': "sudo -S -p '%(sudo_prompt)s' ", + 'sudo_prompt': 'sudo password:', + 'sudo_user': None, + 'tasks': [], + 'prompts': {}, + 'use_exceptions_for': {'network': False}, + 'use_shell': True, + 'use_ssh_config': False, + 'user': None, + 'version': get_version('short') +}) + +# Fill in exceptions settings +exceptions = ['network'] +exception_dict = {} +for e in exceptions: + exception_dict[e] = False +env.use_exceptions_for = _AliasDict(exception_dict, + aliases={'everything': exceptions}) + + +# Add in option defaults +for option in env_options: + env[option.dest] = option.default + +# +# Command dictionary +# + +# Keys are the command/function names, values are the callables themselves. +# This is filled in when main() runs. +commands = {} + + +# +# Host connection dict/cache +# + +connections = HostConnectionCache() + + +def _open_session(): + return connections[env.host_string].get_transport().open_session() + + +def default_channel(): + """ + Return a channel object based on ``env.host_string``. + """ + try: + chan = _open_session() + except ssh.SSHException, err: + if str(err) == 'SSH session not active': + connections[env.host_string].close() + del connections[env.host_string] + chan = _open_session() + else: + raise + chan.settimeout(0.1) + chan.input_enabled = True + return chan + + +# +# Output controls +# + +# Keys are "levels" or "groups" of output, values are always boolean, +# determining whether output falling into the given group is printed or not +# printed. +# +# By default, everything except 'debug' is printed, as this is what the average +# user, and new users, are most likely to expect. +# +# See docs/usage.rst for details on what these levels mean. +output = _AliasDict({ + 'status': True, + 'aborts': True, + 'warnings': True, + 'running': True, + 'stdout': True, + 'stderr': True, + 'debug': False, + 'user': True +}, aliases={ + 'everything': ['warnings', 'running', 'user', 'output'], + 'output': ['stdout', 'stderr'], + 'commands': ['stdout', 'running'] +}) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/task_utils.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/task_utils.py new file mode 100644 index 0000000..5d5f96b --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/task_utils.py @@ -0,0 +1,94 @@ +from fabric.utils import abort, indent +from fabric import state + + +# For attribute tomfoolery +class _Dict(dict): + pass + + +def _crawl(name, mapping): + """ + ``name`` of ``'a.b.c'`` => ``mapping['a']['b']['c']`` + """ + key, _, rest = name.partition('.') + value = mapping[key] + if not rest: + return value + return _crawl(rest, value) + + +def crawl(name, mapping): + try: + result = _crawl(name, mapping) + # Handle default tasks + if isinstance(result, _Dict): + if getattr(result, 'default', False): + result = result.default + # Ensure task modules w/ no default are treated as bad targets + else: + result = None + return result + except (KeyError, TypeError): + return None + + +def merge(hosts, roles, exclude, roledefs): + """ + Merge given host and role lists into one list of deduped hosts. + """ + # Abort if any roles don't exist + bad_roles = [x for x in roles if x not in roledefs] + if bad_roles: + abort("The following specified roles do not exist:\n%s" % ( + indent(bad_roles) + )) + + # Coerce strings to one-item lists + if isinstance(hosts, basestring): + hosts = [hosts] + + # Look up roles, turn into flat list of hosts + role_hosts = [] + for role in roles: + value = roledefs[role] + # Handle "lazy" roles (callables) + if callable(value): + value = value() + role_hosts += value + + # Strip whitespace from host strings. + cleaned_hosts = [x.strip() for x in list(hosts) + list(role_hosts)] + # Return deduped combo of hosts and role_hosts, preserving order within + # them (vs using set(), which may lose ordering) and skipping hosts to be + # excluded. + # But only if the user hasn't indicated they want this behavior disabled. + all_hosts = cleaned_hosts + if state.env.dedupe_hosts: + deduped_hosts = [] + for host in cleaned_hosts: + if host not in deduped_hosts and host not in exclude: + deduped_hosts.append(host) + all_hosts = deduped_hosts + return all_hosts + + +def parse_kwargs(kwargs): + new_kwargs = {} + hosts = [] + roles = [] + exclude_hosts = [] + for key, value in kwargs.iteritems(): + if key == 'host': + hosts = [value] + elif key == 'hosts': + hosts = value + elif key == 'role': + roles = [value] + elif key == 'roles': + roles = value + elif key == 'exclude_hosts': + exclude_hosts = value + else: + new_kwargs[key] = value + return new_kwargs, hosts, roles, exclude_hosts diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/tasks.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/tasks.py new file mode 100644 index 0000000..879d97d --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/tasks.py @@ -0,0 +1,412 @@ +from __future__ import with_statement + +from functools import wraps +import inspect +import sys +import textwrap + +from fabric import state +from fabric.utils import abort, warn, error +from fabric.network import to_dict, normalize_to_string, disconnect_all +from fabric.context_managers import settings +from fabric.job_queue import JobQueue +from fabric.task_utils import crawl, merge, parse_kwargs +from fabric.exceptions import NetworkError + +if sys.version_info[:2] == (2, 5): + # Python 2.5 inspect.getargspec returns a tuple + # instead of ArgSpec namedtuple. + class ArgSpec(object): + def __init__(self, args, varargs, keywords, defaults): + self.args = args + self.varargs = varargs + self.keywords = keywords + self.defaults = defaults + self._tuple = (args, varargs, keywords, defaults) + + def __getitem__(self, idx): + return self._tuple[idx] + + def patched_get_argspec(func): + return ArgSpec(*inspect._getargspec(func)) + + inspect._getargspec = inspect.getargspec + inspect.getargspec = patched_get_argspec + + +def get_task_details(task): + details = [ + textwrap.dedent(task.__doc__) + if task.__doc__ + else 'No docstring provided'] + argspec = inspect.getargspec(task) + + default_args = [] if not argspec.defaults else argspec.defaults + num_default_args = len(default_args) + args_without_defaults = argspec.args[:len(argspec.args) - num_default_args] + args_with_defaults = argspec.args[-1 * num_default_args:] + + details.append('Arguments: %s' % ( + ', '.join( + args_without_defaults + [ + '%s=%r' % (arg, default) + for arg, default in zip(args_with_defaults, default_args) + ]) + )) + + return '\n'.join(details) + + +def _get_list(env): + def inner(key): + return env.get(key, []) + return inner + + +class Task(object): + """ + Abstract base class for objects wishing to be picked up as Fabric tasks. + + Instances of subclasses will be treated as valid tasks when present in + fabfiles loaded by the :doc:`fab ` tool. + + For details on how to implement and use `~fabric.tasks.Task` subclasses, + please see the usage documentation on :ref:`new-style tasks + `. + + .. versionadded:: 1.1 + """ + name = 'undefined' + use_task_objects = True + aliases = None + is_default = False + + # TODO: make it so that this wraps other decorators as expected + def __init__(self, alias=None, aliases=None, default=False, name=None, + *args, **kwargs): + if alias is not None: + self.aliases = [alias, ] + if aliases is not None: + self.aliases = aliases + if name is not None: + self.name = name + self.is_default = default + + def __details__(self): + return get_task_details(self.run) + + def run(self): + raise NotImplementedError + + def get_hosts_and_effective_roles(self, arg_hosts, arg_roles, arg_exclude_hosts, env=None): + """ + Return a tuple containing the host list the given task should be using + and the roles being used. + + See :ref:`host-lists` for detailed documentation on how host lists are + set. + + .. versionchanged:: 1.9 + """ + env = env or {'hosts': [], 'roles': [], 'exclude_hosts': []} + roledefs = env.get('roledefs', {}) + # Command line per-task takes precedence over anything else. + if arg_hosts or arg_roles: + return merge(arg_hosts, arg_roles, arg_exclude_hosts, roledefs), arg_roles + # Decorator-specific hosts/roles go next + func_hosts = getattr(self, 'hosts', []) + func_roles = getattr(self, 'roles', []) + if func_hosts or func_roles: + return merge(func_hosts, func_roles, arg_exclude_hosts, roledefs), func_roles + # Finally, the env is checked (which might contain globally set lists + # from the CLI or from module-level code). This will be the empty list + # if these have not been set -- which is fine, this method should + # return an empty list if no hosts have been set anywhere. + env_vars = map(_get_list(env), "hosts roles exclude_hosts".split()) + env_vars.append(roledefs) + return merge(*env_vars), env.get('roles', []) + + def get_pool_size(self, hosts, default): + # Default parallel pool size (calculate per-task in case variables + # change) + default_pool_size = default or len(hosts) + # Allow per-task override + # Also cast to int in case somebody gave a string + from_task = getattr(self, 'pool_size', None) + pool_size = int(from_task or default_pool_size) + # But ensure it's never larger than the number of hosts + pool_size = min((pool_size, len(hosts))) + # Inform user of final pool size for this task + if state.output.debug: + print("Parallel tasks now using pool size of %d" % pool_size) + return pool_size + + +class WrappedCallableTask(Task): + """ + Wraps a given callable transparently, while marking it as a valid Task. + + Generally used via `~fabric.decorators.task` and not directly. + + .. versionadded:: 1.1 + + .. seealso:: `~fabric.docs.unwrap_tasks`, `~fabric.decorators.task` + """ + def __init__(self, callable, *args, **kwargs): + super(WrappedCallableTask, self).__init__(*args, **kwargs) + self.wrapped = callable + # Don't use getattr() here -- we want to avoid touching self.name + # entirely so the superclass' value remains default. + if hasattr(callable, '__name__'): + if self.name == 'undefined': + self.__name__ = self.name = callable.__name__ + else: + self.__name__ = self.name + if hasattr(callable, '__doc__'): + self.__doc__ = callable.__doc__ + if hasattr(callable, '__module__'): + self.__module__ = callable.__module__ + + def __call__(self, *args, **kwargs): + return self.run(*args, **kwargs) + + def run(self, *args, **kwargs): + return self.wrapped(*args, **kwargs) + + def __getattr__(self, k): + return getattr(self.wrapped, k) + + def __details__(self): + return get_task_details(self.wrapped) + + +def requires_parallel(task): + """ + Returns True if given ``task`` should be run in parallel mode. + + Specifically: + + * It's been explicitly marked with ``@parallel``, or: + * It's *not* been explicitly marked with ``@serial`` *and* the global + parallel option (``env.parallel``) is set to ``True``. + """ + return ( + (state.env.parallel and not getattr(task, 'serial', False)) + or getattr(task, 'parallel', False) + ) + + +def _parallel_tasks(commands_to_run): + return any(map( + lambda x: requires_parallel(crawl(x[0], state.commands)), + commands_to_run + )) + + +def _execute(task, host, my_env, args, kwargs, jobs, queue, multiprocessing): + """ + Primary single-host work body of execute() + """ + # Log to stdout + if state.output.running and not hasattr(task, 'return_value'): + print("[%s] Executing task '%s'" % (host, my_env['command'])) + # Create per-run env with connection settings + local_env = to_dict(host) + local_env.update(my_env) + # Set a few more env flags for parallelism + if queue is not None: + local_env.update({'parallel': True, 'linewise': True}) + # Handle parallel execution + if queue is not None: # Since queue is only set for parallel + name = local_env['host_string'] + # Wrap in another callable that: + # * expands the env it's given to ensure parallel, linewise, etc are + # all set correctly and explicitly. Such changes are naturally + # insulted from the parent process. + # * nukes the connection cache to prevent shared-access problems + # * knows how to send the tasks' return value back over a Queue + # * captures exceptions raised by the task + def inner(args, kwargs, queue, name, env): + state.env.update(env) + def submit(result): + queue.put({'name': name, 'result': result}) + try: + key = normalize_to_string(state.env.host_string) + state.connections.pop(key, "") + submit(task.run(*args, **kwargs)) + except BaseException, e: # We really do want to capture everything + # SystemExit implies use of abort(), which prints its own + # traceback, host info etc -- so we don't want to double up + # on that. For everything else, though, we need to make + # clear what host encountered the exception that will + # print. + if e.__class__ is not SystemExit: + sys.stderr.write("!!! Parallel execution exception under host %r:\n" % name) + submit(e) + # Here, anything -- unexpected exceptions, or abort() + # driven SystemExits -- will bubble up and terminate the + # child process. + raise + + # Stuff into Process wrapper + kwarg_dict = { + 'args': args, + 'kwargs': kwargs, + 'queue': queue, + 'name': name, + 'env': local_env, + } + p = multiprocessing.Process(target=inner, kwargs=kwarg_dict) + # Name/id is host string + p.name = name + # Add to queue + jobs.append(p) + # Handle serial execution + else: + with settings(**local_env): + return task.run(*args, **kwargs) + +def _is_task(task): + return isinstance(task, Task) + +def execute(task, *args, **kwargs): + """ + Execute ``task`` (callable or name), honoring host/role decorators, etc. + + ``task`` may be an actual callable object, or it may be a registered task + name, which is used to look up a callable just as if the name had been + given on the command line (including :ref:`namespaced tasks `, + e.g. ``"deploy.migrate"``. + + The task will then be executed once per host in its host list, which is + (again) assembled in the same manner as CLI-specified tasks: drawing from + :option:`-H`, :ref:`env.hosts `, the `~fabric.decorators.hosts` or + `~fabric.decorators.roles` decorators, and so forth. + + ``host``, ``hosts``, ``role``, ``roles`` and ``exclude_hosts`` kwargs will + be stripped out of the final call, and used to set the task's host list, as + if they had been specified on the command line like e.g. ``fab + taskname:host=hostname``. + + Any other arguments or keyword arguments will be passed verbatim into + ``task`` (the function itself -- not the ``@task`` decorator wrapping your + function!) when it is called, so ``execute(mytask, 'arg1', + kwarg1='value')`` will (once per host) invoke ``mytask('arg1', + kwarg1='value')``. + + :returns: + a dictionary mapping host strings to the given task's return value for + that host's execution run. For example, ``execute(foo, hosts=['a', + 'b'])`` might return ``{'a': None, 'b': 'bar'}`` if ``foo`` returned + nothing on host `a` but returned ``'bar'`` on host `b`. + + In situations where a task execution fails for a given host but overall + progress does not abort (such as when :ref:`env.skip_bad_hosts + ` is True) the return value for that host will be the + error object or message. + + .. seealso:: + :ref:`The execute usage docs `, for an expanded explanation + and some examples. + + .. versionadded:: 1.3 + .. versionchanged:: 1.4 + Added the return value mapping; previously this function had no defined + return value. + """ + my_env = {'clean_revert': True} + results = {} + # Obtain task + is_callable = callable(task) + if not (is_callable or _is_task(task)): + # Assume string, set env.command to it + my_env['command'] = task + task = crawl(task, state.commands) + if task is None: + abort("%r is not callable or a valid task name" % (task,)) + # Set env.command if we were given a real function or callable task obj + else: + dunder_name = getattr(task, '__name__', None) + my_env['command'] = getattr(task, 'name', dunder_name) + # Normalize to Task instance if we ended up with a regular callable + if not _is_task(task): + task = WrappedCallableTask(task) + # Filter out hosts/roles kwargs + new_kwargs, hosts, roles, exclude_hosts = parse_kwargs(kwargs) + # Set up host list + my_env['all_hosts'], my_env['effective_roles'] = task.get_hosts_and_effective_roles(hosts, roles, + exclude_hosts, state.env) + + parallel = requires_parallel(task) + if parallel: + # Import multiprocessing if needed, erroring out usefully + # if it can't. + try: + import multiprocessing + except ImportError: + import traceback + tb = traceback.format_exc() + abort(tb + """ + At least one task needs to be run in parallel, but the + multiprocessing module cannot be imported (see above + traceback.) Please make sure the module is installed + or that the above ImportError is fixed.""") + else: + multiprocessing = None + + # Get pool size for this task + pool_size = task.get_pool_size(my_env['all_hosts'], state.env.pool_size) + # Set up job queue in case parallel is needed + queue = multiprocessing.Queue() if parallel else None + jobs = JobQueue(pool_size, queue) + if state.output.debug: + jobs._debug = True + + # Call on host list + if my_env['all_hosts']: + # Attempt to cycle on hosts, skipping if needed + for host in my_env['all_hosts']: + try: + results[host] = _execute( + task, host, my_env, args, new_kwargs, jobs, queue, + multiprocessing + ) + except NetworkError, e: + results[host] = e + # Backwards compat test re: whether to use an exception or + # abort + if not state.env.use_exceptions_for['network']: + func = warn if state.env.skip_bad_hosts else abort + error(e.message, func=func, exception=e.wrapped) + else: + raise + + # If requested, clear out connections here and not just at the end. + if state.env.eagerly_disconnect: + disconnect_all() + + # If running in parallel, block until job queue is emptied + if jobs: + err = "One or more hosts failed while executing task '%s'" % ( + my_env['command'] + ) + jobs.close() + # Abort if any children did not exit cleanly (fail-fast). + # This prevents Fabric from continuing on to any other tasks. + # Otherwise, pull in results from the child run. + ran_jobs = jobs.run() + for name, d in ran_jobs.iteritems(): + if d['exit_code'] != 0: + if isinstance(d['results'], BaseException): + error(err, exception=d['results']) + else: + error(err) + results[name] = d['results'] + + # Or just run once for local-only + else: + with settings(**my_env): + results[''] = task.run(*args, **new_kwargs) + # Return what we can from the inner task executions + + return results diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/thread_handling.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/thread_handling.py new file mode 100644 index 0000000..6294723 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/thread_handling.py @@ -0,0 +1,25 @@ +import threading +import sys + + +class ThreadHandler(object): + def __init__(self, name, callable, *args, **kwargs): + # Set up exception handling + self.exception = None + + def wrapper(*args, **kwargs): + try: + callable(*args, **kwargs) + except BaseException: + self.exception = sys.exc_info() + # Kick off thread + thread = threading.Thread(None, wrapper, name, args, kwargs) + thread.setDaemon(True) + thread.start() + # Make thread available to instantiator + self.thread = thread + + def raise_if_needed(self): + if self.exception: + e = self.exception + raise e[0], e[1], e[2] diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/utils.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/utils.py new file mode 100644 index 0000000..6d58dc9 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/utils.py @@ -0,0 +1,380 @@ +""" +Internal subroutines for e.g. aborting execution with an error message, +or performing indenting on multiline output. +""" +import os +import sys +import textwrap +from traceback import format_exc + +def abort(msg): + """ + Abort execution, print ``msg`` to stderr and exit with error status (1.) + + This function currently makes use of `sys.exit`_, which raises + `SystemExit`_. Therefore, it's possible to detect and recover from inner + calls to `abort` by using ``except SystemExit`` or similar. + + .. _sys.exit: http://docs.python.org/library/sys.html#sys.exit + .. _SystemExit: http://docs.python.org/library/exceptions.html#exceptions.SystemExit + """ + from fabric.state import output, env + if not env.colorize_errors: + red = lambda x: x + else: + from colors import red + + if output.aborts: + sys.stderr.write(red("\nFatal error: %s\n" % str(msg))) + sys.stderr.write(red("\nAborting.\n")) + + if env.abort_exception: + raise env.abort_exception(msg) + else: + sys.exit(1) + + +def warn(msg): + """ + Print warning message, but do not abort execution. + + This function honors Fabric's :doc:`output controls + <../../usage/output_controls>` and will print the given ``msg`` to stderr, + provided that the ``warnings`` output level (which is active by default) is + turned on. + """ + from fabric.state import output, env + + if not env.colorize_errors: + magenta = lambda x: x + else: + from colors import magenta + + if output.warnings: + sys.stderr.write(magenta("\nWarning: %s\n\n" % msg)) + + +def indent(text, spaces=4, strip=False): + """ + Return ``text`` indented by the given number of spaces. + + If text is not a string, it is assumed to be a list of lines and will be + joined by ``\\n`` prior to indenting. + + When ``strip`` is ``True``, a minimum amount of whitespace is removed from + the left-hand side of the given string (so that relative indents are + preserved, but otherwise things are left-stripped). This allows you to + effectively "normalize" any previous indentation for some inputs. + """ + # Normalize list of strings into a string for dedenting. "list" here means + # "not a string" meaning "doesn't have splitlines". Meh. + if not hasattr(text, 'splitlines'): + text = '\n'.join(text) + # Dedent if requested + if strip: + text = textwrap.dedent(text) + prefix = ' ' * spaces + output = '\n'.join(prefix + line for line in text.splitlines()) + # Strip out empty lines before/aft + output = output.strip() + # Reintroduce first indent (which just got stripped out) + output = prefix + output + return output + + +def puts(text, show_prefix=None, end="\n", flush=False): + """ + An alias for ``print`` whose output is managed by Fabric's output controls. + + In other words, this function simply prints to ``sys.stdout``, but will + hide its output if the ``user`` :doc:`output level + ` is set to ``False``. + + If ``show_prefix=False``, `puts` will omit the leading ``[hostname]`` + which it tacks on by default. (It will also omit this prefix if + ``env.host_string`` is empty.) + + Newlines may be disabled by setting ``end`` to the empty string (``''``). + (This intentionally mirrors Python 3's ``print`` syntax.) + + You may force output flushing (e.g. to bypass output buffering) by setting + ``flush=True``. + + .. versionadded:: 0.9.2 + .. seealso:: `~fabric.utils.fastprint` + """ + from fabric.state import output, env + if show_prefix is None: + show_prefix = env.output_prefix + if output.user: + prefix = "" + if env.host_string and show_prefix: + prefix = "[%s] " % env.host_string + sys.stdout.write(prefix + str(text) + end) + if flush: + sys.stdout.flush() + + +def fastprint(text, show_prefix=False, end="", flush=True): + """ + Print ``text`` immediately, without any prefix or line ending. + + This function is simply an alias of `~fabric.utils.puts` with different + default argument values, such that the ``text`` is printed without any + embellishment and immediately flushed. + + It is useful for any situation where you wish to print text which might + otherwise get buffered by Python's output buffering (such as within a + processor intensive ``for`` loop). Since such use cases typically also + require a lack of line endings (such as printing a series of dots to + signify progress) it also omits the traditional newline by default. + + .. note:: + + Since `~fabric.utils.fastprint` calls `~fabric.utils.puts`, it is + likewise subject to the ``user`` :doc:`output level + `. + + .. versionadded:: 0.9.2 + .. seealso:: `~fabric.utils.puts` + """ + return puts(text=text, show_prefix=show_prefix, end=end, flush=flush) + + +def handle_prompt_abort(prompt_for): + import fabric.state + reason = "Needed to prompt for %s (host: %s), but %%s" % ( + prompt_for, fabric.state.env.host_string + ) + # Explicit "don't prompt me bro" + if fabric.state.env.abort_on_prompts: + abort(reason % "abort-on-prompts was set to True") + # Implicit "parallel == stdin/prompts have ambiguous target" + if fabric.state.env.parallel: + abort(reason % "input would be ambiguous in parallel mode") + + +class _AttributeDict(dict): + """ + Dictionary subclass enabling attribute lookup/assignment of keys/values. + + For example:: + + >>> m = _AttributeDict({'foo': 'bar'}) + >>> m.foo + 'bar' + >>> m.foo = 'not bar' + >>> m['foo'] + 'not bar' + + ``_AttributeDict`` objects also provide ``.first()`` which acts like + ``.get()`` but accepts multiple keys as arguments, and returns the value of + the first hit, e.g.:: + + >>> m = _AttributeDict({'foo': 'bar', 'biz': 'baz'}) + >>> m.first('wrong', 'incorrect', 'foo', 'biz') + 'bar' + + """ + def __getattr__(self, key): + try: + return self[key] + except KeyError: + # to conform with __getattr__ spec + raise AttributeError(key) + + def __setattr__(self, key, value): + self[key] = value + + def first(self, *names): + for name in names: + value = self.get(name) + if value: + return value + + +class _AliasDict(_AttributeDict): + """ + `_AttributeDict` subclass that allows for "aliasing" of keys to other keys. + + Upon creation, takes an ``aliases`` mapping, which should map alias names + to lists of key names. Aliases do not store their own value, but instead + set (override) all mapped keys' values. For example, in the following + `_AliasDict`, calling ``mydict['foo'] = True`` will set the values of + ``mydict['bar']``, ``mydict['biz']`` and ``mydict['baz']`` all to True:: + + mydict = _AliasDict( + {'biz': True, 'baz': False}, + aliases={'foo': ['bar', 'biz', 'baz']} + ) + + Because it is possible for the aliased values to be in a heterogenous + state, reading aliases is not supported -- only writing to them is allowed. + This also means they will not show up in e.g. ``dict.keys()``. + + ..note:: + + Aliases are recursive, so you may refer to an alias within the key list + of another alias. Naturally, this means that you can end up with + infinite loops if you're not careful. + + `_AliasDict` provides a special function, `expand_aliases`, which will take + a list of keys as an argument and will return that list of keys with any + aliases expanded. This function will **not** dedupe, so any aliases which + overlap will result in duplicate keys in the resulting list. + """ + def __init__(self, arg=None, aliases=None): + init = super(_AliasDict, self).__init__ + if arg is not None: + init(arg) + else: + init() + # Can't use super() here because of _AttributeDict's setattr override + dict.__setattr__(self, 'aliases', aliases) + + def __setitem__(self, key, value): + # Attr test required to not blow up when deepcopy'd + if hasattr(self, 'aliases') and key in self.aliases: + for aliased in self.aliases[key]: + self[aliased] = value + else: + return super(_AliasDict, self).__setitem__(key, value) + + def expand_aliases(self, keys): + ret = [] + for key in keys: + if key in self.aliases: + ret.extend(self.expand_aliases(self.aliases[key])) + else: + ret.append(key) + return ret + + +def _pty_size(): + """ + Obtain (rows, cols) tuple for sizing a pty on the remote end. + + Defaults to 80x24 (which is also the 'ssh' lib's default) but will detect + local (stdout-based) terminal window size on non-Windows platforms. + """ + from fabric.state import win32 + if not win32: + import fcntl + import termios + import struct + + default_rows, default_cols = 24, 80 + rows, cols = default_rows, default_cols + if not win32 and sys.stdout.isatty(): + # We want two short unsigned integers (rows, cols) + fmt = 'HH' + # Create an empty (zeroed) buffer for ioctl to map onto. Yay for C! + buffer = struct.pack(fmt, 0, 0) + # Call TIOCGWINSZ to get window size of stdout, returns our filled + # buffer + try: + result = fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, + buffer) + # Unpack buffer back into Python data types + rows, cols = struct.unpack(fmt, result) + # Fall back to defaults if TIOCGWINSZ returns unreasonable values + if rows == 0: + rows = default_rows + if cols == 0: + cols = default_cols + # Deal with e.g. sys.stdout being monkeypatched, such as in testing. + # Or termios not having a TIOCGWINSZ. + except AttributeError: + pass + return rows, cols + + +def error(message, func=None, exception=None, stdout=None, stderr=None): + """ + Call ``func`` with given error ``message``. + + If ``func`` is None (the default), the value of ``env.warn_only`` + determines whether to call ``abort`` or ``warn``. + + If ``exception`` is given, it is inspected to get a string message, which + is printed alongside the user-generated ``message``. + + If ``stdout`` and/or ``stderr`` are given, they are assumed to be strings + to be printed. + """ + import fabric.state + if func is None: + func = fabric.state.env.warn_only and warn or abort + # If debug printing is on, append a traceback to the message + if fabric.state.output.debug: + message += "\n\n" + format_exc() + # Otherwise, if we were given an exception, append its contents. + elif exception is not None: + # Figure out how to get a string out of the exception; EnvironmentError + # subclasses, for example, "are" integers and .strerror is the string. + # Others "are" strings themselves. May have to expand this further for + # other error types. + if hasattr(exception, 'strerror') and exception.strerror is not None: + underlying = exception.strerror + else: + underlying = exception + message += "\n\nUnderlying exception:\n" + indent(str(underlying)) + if func is abort: + if stdout and not fabric.state.output.stdout: + message += _format_error_output("Standard output", stdout) + if stderr and not fabric.state.output.stderr: + message += _format_error_output("Standard error", stderr) + return func(message) + + +def _format_error_output(header, body): + term_width = _pty_size()[1] + header_side_length = (term_width - (len(header) + 2)) / 2 + mark = "=" + side = mark * header_side_length + return "\n\n%s %s %s\n\n%s\n\n%s" % ( + side, header, side, body, mark * term_width + ) + + +# TODO: replace with collections.deque(maxlen=xxx) in Python 2.6 +class RingBuffer(list): + def __init__(self, value, maxlen): + # Heh. + self._super = super(RingBuffer, self) + self._maxlen = maxlen + return self._super.__init__(value) + + def _free(self): + return self._maxlen - len(self) + + def append(self, value): + if self._free() == 0: + del self[0] + return self._super.append(value) + + def extend(self, values): + overage = len(values) - self._free() + if overage > 0: + del self[0:overage] + return self._super.extend(values) + + # Paranoia from here on out. + def insert(self, index, value): + raise ValueError("Can't insert into the middle of a ring buffer!") + + def __setslice__(self, i, j, sequence): + raise ValueError("Can't set a slice of a ring buffer!") + + def __setitem__(self, key, value): + if isinstance(key, slice): + raise ValueError("Can't set a slice of a ring buffer!") + else: + return self._super.__setitem__(key, value) + + +def apply_lcwd(path, env): + # Apply CWD if a relative path + if not os.path.isabs(path) and env.lcwd: + path = os.path.join(env.lcwd, path) + return path diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/version.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/version.py new file mode 100644 index 0000000..b688df9 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/fabric/version.py @@ -0,0 +1,108 @@ +""" +Current Fabric version constant plus version pretty-print method. + +This functionality is contained in its own module to prevent circular import +problems with ``__init__.py`` (which is loaded by setup.py during installation, +which in turn needs access to this version information.) +""" +from subprocess import Popen, PIPE +from os.path import abspath, dirname + + +VERSION = (1, 9, 0, 'final', 0) + + +def git_sha(): + loc = abspath(dirname(__file__)) + try: + p = Popen( + "cd \"%s\" && git log -1 --format=format:%%h" % loc, + shell=True, + stdout=PIPE, + stderr=PIPE + ) + return p.communicate()[0] + # OSError occurs on Unix-derived platforms lacking Popen's configured shell + # default, /bin/sh. E.g. Android. + except OSError: + return None + + +def get_version(form='short'): + """ + Return a version string for this package, based on `VERSION`. + + Takes a single argument, ``form``, which should be one of the following + strings: + + * ``branch``: just the major + minor, e.g. "0.9", "1.0". + * ``short`` (default): compact, e.g. "0.9rc1", "0.9.0". For package + filenames or SCM tag identifiers. + * ``normal``: human readable, e.g. "0.9", "0.9.1", "0.9 beta 1". For e.g. + documentation site headers. + * ``verbose``: like ``normal`` but fully explicit, e.g. "0.9 final". For + tag commit messages, or anywhere that it's important to remove ambiguity + between a branch and the first final release within that branch. + * ``all``: Returns all of the above, as a dict. + """ + # Setup + versions = {} + branch = "%s.%s" % (VERSION[0], VERSION[1]) + tertiary = VERSION[2] + type_ = VERSION[3] + final = (type_ == "final") + type_num = VERSION[4] + firsts = "".join([x[0] for x in type_.split()]) + sha = git_sha() + sha1 = (" (%s)" % sha) if sha else "" + + # Branch + versions['branch'] = branch + + # Short + v = branch + if (tertiary or final): + v += "." + str(tertiary) + if not final: + v += firsts + if type_num: + v += str(type_num) + else: + v += sha1 + versions['short'] = v + + # Normal + v = branch + if tertiary: + v += "." + str(tertiary) + if not final: + if type_num: + v += " " + type_ + " " + str(type_num) + else: + v += " pre-" + type_ + sha1 + versions['normal'] = v + + # Verbose + v = branch + if tertiary: + v += "." + str(tertiary) + if not final: + if type_num: + v += " " + type_ + " " + str(type_num) + else: + v += " pre-" + type_ + sha1 + else: + v += " final" + versions['verbose'] = v + + try: + return versions[form] + except KeyError: + if form == 'all': + return versions + raise TypeError('"%s" is not a valid form specifier.' % form) + +__version__ = get_version('short') + +if __name__ == "__main__": + print(get_version('all')) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko-1.14.0.dist-info/DESCRIPTION.rst b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko-1.14.0.dist-info/DESCRIPTION.rst new file mode 100644 index 0000000..f3307fe --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko-1.14.0.dist-info/DESCRIPTION.rst @@ -0,0 +1,13 @@ +This is a library for making SSH2 connections (client or server). +Emphasis is on using SSH2 as an alternative to SSL for making secure +connections between python scripts. All major ciphers and hash methods +are supported. SFTP client and server mode are both supported too. + +Required packages: + pyCrypto + +To install the `in-development version +`_, use +`pip install paramiko==dev`. + + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko-1.14.0.dist-info/METADATA b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko-1.14.0.dist-info/METADATA new file mode 100644 index 0000000..d02037d --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko-1.14.0.dist-info/METADATA @@ -0,0 +1,38 @@ +Metadata-Version: 2.0 +Name: paramiko +Version: 1.14.0 +Summary: SSH2 protocol library +Home-page: https://github.com/paramiko/paramiko/ +Author: Jeff Forcier +Author-email: jeff@bitprophet.org +License: LGPL +Platform: Posix; MacOS X; Windows +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL) +Classifier: Operating System :: OS Independent +Classifier: Topic :: Internet +Classifier: Topic :: Security :: Cryptography +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.2 +Classifier: Programming Language :: Python :: 3.3 +Requires-Dist: pycrypto (>=2.1,!=2.4) +Requires-Dist: ecdsa + +This is a library for making SSH2 connections (client or server). +Emphasis is on using SSH2 as an alternative to SSL for making secure +connections between python scripts. All major ciphers and hash methods +are supported. SFTP client and server mode are both supported too. + +Required packages: + pyCrypto + +To install the `in-development version +`_, use +`pip install paramiko==dev`. + + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko-1.14.0.dist-info/RECORD b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko-1.14.0.dist-info/RECORD new file mode 100644 index 0000000..daed8b4 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko-1.14.0.dist-info/RECORD @@ -0,0 +1,82 @@ +paramiko/__init__.py,sha256=Ilt7-7qYsADTT8tGRa5nE74XcDdL1Ys9Z_B3xD06VT4,3897 +paramiko/_winapi.py,sha256=etp5PAfu_5_0J_-XeXHCbsPVCytosUhVjRaBpqiZKHg,8289 +paramiko/agent.py,sha256=A58IeuPi2rqSaERN1QwTUWIeUm9xUQuqBdyYjVTSwHQ,11492 +paramiko/auth_handler.py,sha256=SQgrhcnsc6NMKXLJX8SdFGudyZ18KMDCcBav5rtJOL0,16836 +paramiko/ber.py,sha256=yfUpV3wvNeaR7M211yAqHPKiyDX4OgS4DRNVKp_GXgc,4332 +paramiko/buffered_pipe.py,sha256=xpYc6VqMppFgx-AP_fcLCxLGQ8zemNoMiNLZco7pr4g,6984 +paramiko/channel.py,sha256=ufIC8D9toZRcB1nJbWhSaqXssi6KGjC-SfCzPFMu-dE,46007 +paramiko/client.py,sha256=3ZfdCpnVDybZ7YZdD5y4MI4-7R9Wy76qlvF4gjwigLw,21089 +paramiko/common.py,sha256=zcLnc35-QZ3k4wT5gLJZAsq5B3SFVf39tDTpG-N-XwI,5993 +paramiko/compress.py,sha256=6A11Gxw4gbEwXpRZcJ2EmnWWoRGyd6On8RUk98ID30c,1245 +paramiko/config.py,sha256=wu_MTJSohhezb_3gIMeBaZULixlweCZoERs3H4krIbc,9859 +paramiko/dsskey.py,sha256=wHyobmiv49vU37xHcfclQ4SpmlOB1DMV1HWoJfpsl78,6777 +paramiko/ecdsakey.py,sha256=N-gKm2sqQ5J7AE3odONSvbpcnXftaDdy3Pfb18ncr-M,6312 +paramiko/file.py,sha256=WeoUHgKJ3zSiZsg1jJJ1zOIEA2J1mXM977jOqs5DgB0,17643 +paramiko/hostkeys.py,sha256=JVnud5IiGqu7LsGJOZG30vxlx_K4PshpXBl1QM7AXcE,12191 +paramiko/kex_gex.py,sha256=2f-XjMptsLn1K9C2EjUuwoTJSpv0--urG_sUKj3q8oI,9739 +paramiko/kex_group1.py,sha256=c2sdCDic3TLjoAcJVr9R6dKfekSZwATBPQAbbYP0q0M,5476 +paramiko/message.py,sha256=yYDvWoDXT_0FdVkjdTt2s3lRBm5Qlz8xE6TObuJLOkw,9494 +paramiko/packet.py,sha256=PzYS3YU8WRnHv2TgiJvVDsO2XD91znrpYAa31T6v4uU,17259 +paramiko/pipe.py,sha256=w0u2J3_Abf8ZDhFaTvkSl-hh1SLhFnW3-I6RG_T7yMo,3972 +paramiko/pkey.py,sha256=igi6LI9yfFRc3frOf1-oP0CT6Tmzmnu5DNG7ydt1gIA,14066 +paramiko/primes.py,sha256=HWmo5_ZOT7jVdv7bCQ2RHcF4_sZbCsMSMjAOCPjauDU,4919 +paramiko/proxy.py,sha256=dhyzqyBeCLemADrlpok2Rmszq9RiO6yvpuWSZ5RAptM,4048 +paramiko/py3compat.py,sha256=DEHm2ry0VOZXzO_SiV8DVPbeBgZcIETjVsiWcGk3PNg,3763 +paramiko/resource.py,sha256=snOgXJ1geiD0OTEAojCq4MJBx13u1NFmDetbBNBTzvU,2547 +paramiko/rsakey.py,sha256=PaFf_9SnWPi95NwZ-Yv9XWfp1AOG1Jv4e01wqAqTkdM,6636 +paramiko/server.py,sha256=TZ6i0SpWuD7jlWVRV5H5kyE3vZfhLVWo6qgClizFO1o,26563 +paramiko/sftp.py,sha256=TvIQhDSf2RJ2SwKpMBfym74xSG60rfY8tNohGemsE8o,6206 +paramiko/sftp_attr.py,sha256=EyqjnnjMAEHc06Iwd_dcNuNzrqLFyskgh13g3TMsp6c,7875 +paramiko/sftp_client.py,sha256=P-UlJWQo8TSAA5sVHF8M38iiKT5IJBNNl_tnheOWMAk,28704 +paramiko/sftp_file.py,sha256=uFgOGxPcH-VrQAxqeqr0fIDwR5_sX5ExpP2PYZ-JOPE,19209 +paramiko/sftp_handle.py,sha256=xG7jVIQvvOexbi2EFlreAwV1Szi5IL9_ey-YABazDi8,7391 +paramiko/sftp_server.py,sha256=m6bsA8TtW0axbv9uxh8wqAS2-3FRf9OwsoXSdNtR9KM,18493 +paramiko/sftp_si.py,sha256=9vugsNer7vJmj_gySCm0JhYVClPJRTgEZTz5OYzoWNk,11932 +paramiko/ssh_exception.py,sha256=GDk0WJRXPHhNrt-tr5ZPoSNdNyYMgijjeqK63YeijN8,4197 +paramiko/transport.py,sha256=IACNEHOXOKTEBnLPXEqTQrBsEKhLG-wP_wqVA0dAtNE,89390 +paramiko/util.py,sha256=A1aytnf4gS8UXC-UgWT920Gue9s8LOWbuwXCOiRuOsI,9631 +paramiko/win_pageant.py,sha256=Z8HWHkTbm7_5UYNT_ZBr63ZhzIFunl8G-TtYUGlw1jM,4171 +paramiko-1.14.0.dist-info/DESCRIPTION.rst,sha256=GOwgDeLcu7w_t0eQHrMXjXoe7wGHQ06O24JjHMjd284,457 +paramiko-1.14.0.dist-info/METADATA,sha256=s1-whRCSgy6YVqExIrRy9UpJQuGXuYQyP3Zd4Qw35k4,1406 +paramiko-1.14.0.dist-info/metadata.json,sha256=H8OCuKKNlbgKMH2katt-y8174Ia-UZNRU4H15ajzU0k,1046 +paramiko-1.14.0.dist-info/RECORD,, +paramiko-1.14.0.dist-info/top_level.txt,sha256=R9n-eCc_1kx1DnijF7Glmm-H67k9jUz5rm2YoPL8n54,9 +paramiko-1.14.0.dist-info/WHEEL,sha256=6lxp_S3wZGmTBtGMVmNNLyvKFcp7HqQw2Wn4YYk-Suo,110 +paramiko/server.pyc,, +paramiko/ber.pyc,, +paramiko/__init__.pyc,, +paramiko/kex_group1.pyc,, +paramiko/sftp_client.pyc,, +paramiko/buffered_pipe.pyc,, +paramiko/file.pyc,, +paramiko/resource.pyc,, +paramiko/dsskey.pyc,, +paramiko/sftp.pyc,, +paramiko/py3compat.pyc,, +paramiko/sftp_si.pyc,, +paramiko/sftp_handle.pyc,, +paramiko/pkey.pyc,, +paramiko/ssh_exception.pyc,, +paramiko/_winapi.pyc,, +paramiko/packet.pyc,, +paramiko/sftp_file.pyc,, +paramiko/message.pyc,, +paramiko/primes.pyc,, +paramiko/config.pyc,, +paramiko/compress.pyc,, +paramiko/rsakey.pyc,, +paramiko/common.pyc,, +paramiko/hostkeys.pyc,, +paramiko/pipe.pyc,, +paramiko/kex_gex.pyc,, +paramiko/sftp_attr.pyc,, +paramiko/transport.pyc,, +paramiko/proxy.pyc,, +paramiko/client.pyc,, +paramiko/agent.pyc,, +paramiko/win_pageant.pyc,, +paramiko/sftp_server.pyc,, +paramiko/channel.pyc,, +paramiko/auth_handler.pyc,, +paramiko/ecdsakey.pyc,, +paramiko/util.pyc,, diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko-1.14.0.dist-info/WHEEL b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko-1.14.0.dist-info/WHEEL new file mode 100644 index 0000000..f19235c --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko-1.14.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.23.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko-1.14.0.dist-info/metadata.json b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko-1.14.0.dist-info/metadata.json new file mode 100644 index 0000000..07d2722 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko-1.14.0.dist-info/metadata.json @@ -0,0 +1 @@ +{"license": "LGPL", "document_names": {"description": "DESCRIPTION.rst"}, "name": "paramiko", "metadata_version": "2.0", "contacts": [{"role": "author", "email": "jeff@bitprophet.org", "name": "Jeff Forcier"}], "generator": "bdist_wheel (0.23.0)", "summary": "SSH2 protocol library", "project_urls": {"Home": "https://github.com/paramiko/paramiko/"}, "platform": "Posix; MacOS X; Windows", "run_requires": [{"requires": ["pycrypto (>=2.1,!=2.4)", "ecdsa"]}], "version": "1.14.0", "classifiers": ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)", "Operating System :: OS Independent", "Topic :: Internet", "Topic :: Security :: Cryptography", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3"], "extras": []} \ No newline at end of file diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko-1.14.0.dist-info/top_level.txt b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko-1.14.0.dist-info/top_level.txt new file mode 100644 index 0000000..8608c1b --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko-1.14.0.dist-info/top_level.txt @@ -0,0 +1 @@ +paramiko diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/__init__.py new file mode 100644 index 0000000..4c62ad4 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/__init__.py @@ -0,0 +1,103 @@ +# Copyright (C) 2003-2011 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +import sys + +if sys.version_info < (2, 6): + raise RuntimeError('You need Python 2.6+ for this module.') + + +__author__ = "Jeff Forcier " +__version__ = "1.14.0" +__version_info__ = tuple([ int(d) for d in __version__.split(".") ]) +__license__ = "GNU Lesser General Public License (LGPL)" + + +from paramiko.transport import SecurityOptions, Transport +from paramiko.client import SSHClient, MissingHostKeyPolicy, AutoAddPolicy, RejectPolicy, WarningPolicy +from paramiko.auth_handler import AuthHandler +from paramiko.channel import Channel, ChannelFile +from paramiko.ssh_exception import SSHException, PasswordRequiredException, \ + BadAuthenticationType, ChannelException, BadHostKeyException, \ + AuthenticationException, ProxyCommandFailure +from paramiko.server import ServerInterface, SubsystemHandler, InteractiveQuery +from paramiko.rsakey import RSAKey +from paramiko.dsskey import DSSKey +from paramiko.ecdsakey import ECDSAKey +from paramiko.sftp import SFTPError, BaseSFTP +from paramiko.sftp_client import SFTP, SFTPClient +from paramiko.sftp_server import SFTPServer +from paramiko.sftp_attr import SFTPAttributes +from paramiko.sftp_handle import SFTPHandle +from paramiko.sftp_si import SFTPServerInterface +from paramiko.sftp_file import SFTPFile +from paramiko.message import Message +from paramiko.packet import Packetizer +from paramiko.file import BufferedFile +from paramiko.agent import Agent, AgentKey +from paramiko.pkey import PKey +from paramiko.hostkeys import HostKeys +from paramiko.config import SSHConfig +from paramiko.proxy import ProxyCommand + +from paramiko.common import AUTH_SUCCESSFUL, AUTH_PARTIALLY_SUCCESSFUL, AUTH_FAILED, \ + OPEN_SUCCEEDED, OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED, OPEN_FAILED_CONNECT_FAILED, \ + OPEN_FAILED_UNKNOWN_CHANNEL_TYPE, OPEN_FAILED_RESOURCE_SHORTAGE + +from paramiko.sftp import SFTP_OK, SFTP_EOF, SFTP_NO_SUCH_FILE, SFTP_PERMISSION_DENIED, SFTP_FAILURE, \ + SFTP_BAD_MESSAGE, SFTP_NO_CONNECTION, SFTP_CONNECTION_LOST, SFTP_OP_UNSUPPORTED + +from paramiko.common import io_sleep + +__all__ = [ 'Transport', + 'SSHClient', + 'MissingHostKeyPolicy', + 'AutoAddPolicy', + 'RejectPolicy', + 'WarningPolicy', + 'SecurityOptions', + 'SubsystemHandler', + 'Channel', + 'PKey', + 'RSAKey', + 'DSSKey', + 'Message', + 'SSHException', + 'AuthenticationException', + 'PasswordRequiredException', + 'BadAuthenticationType', + 'ChannelException', + 'BadHostKeyException', + 'ProxyCommand', + 'ProxyCommandFailure', + 'SFTP', + 'SFTPFile', + 'SFTPHandle', + 'SFTPClient', + 'SFTPServer', + 'SFTPError', + 'SFTPAttributes', + 'SFTPServerInterface', + 'ServerInterface', + 'BufferedFile', + 'Agent', + 'AgentKey', + 'HostKeys', + 'SSHConfig', + 'util', + 'io_sleep' ] diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/_winapi.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/_winapi.py new file mode 100644 index 0000000..0d55d29 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/_winapi.py @@ -0,0 +1,278 @@ +""" +Windows API functions implemented as ctypes functions and classes as found +in jaraco.windows (2.10). + +If you encounter issues with this module, please consider reporting the issues +in jaraco.windows and asking the author to port the fixes back here. +""" + +import ctypes +import ctypes.wintypes +from paramiko.py3compat import u +try: + import builtins +except ImportError: + import __builtin__ as builtins + +try: + USHORT = ctypes.wintypes.USHORT +except AttributeError: + USHORT = ctypes.c_ushort + +###################### +# jaraco.windows.error + +def format_system_message(errno): + """ + Call FormatMessage with a system error number to retrieve + the descriptive error message. + """ + # first some flags used by FormatMessageW + ALLOCATE_BUFFER = 0x100 + ARGUMENT_ARRAY = 0x2000 + FROM_HMODULE = 0x800 + FROM_STRING = 0x400 + FROM_SYSTEM = 0x1000 + IGNORE_INSERTS = 0x200 + + # Let FormatMessageW allocate the buffer (we'll free it below) + # Also, let it know we want a system error message. + flags = ALLOCATE_BUFFER | FROM_SYSTEM + source = None + message_id = errno + language_id = 0 + result_buffer = ctypes.wintypes.LPWSTR() + buffer_size = 0 + arguments = None + format_bytes = ctypes.windll.kernel32.FormatMessageW( + flags, + source, + message_id, + language_id, + ctypes.byref(result_buffer), + buffer_size, + arguments, + ) + # note the following will cause an infinite loop if GetLastError + # repeatedly returns an error that cannot be formatted, although + # this should not happen. + handle_nonzero_success(format_bytes) + message = result_buffer.value + ctypes.windll.kernel32.LocalFree(result_buffer) + return message + + +class WindowsError(builtins.WindowsError): + "more info about errors at http://msdn.microsoft.com/en-us/library/ms681381(VS.85).aspx" + + def __init__(self, value=None): + if value is None: + value = ctypes.windll.kernel32.GetLastError() + strerror = format_system_message(value) + super(WindowsError, self).__init__(value, strerror) + + @property + def message(self): + return self.strerror + + @property + def code(self): + return self.winerror + + def __str__(self): + return self.message + + def __repr__(self): + return '{self.__class__.__name__}({self.winerror})'.format(**vars()) + +def handle_nonzero_success(result): + if result == 0: + raise WindowsError() + + +CreateFileMapping = ctypes.windll.kernel32.CreateFileMappingW +CreateFileMapping.argtypes = [ + ctypes.wintypes.HANDLE, + ctypes.c_void_p, + ctypes.wintypes.DWORD, + ctypes.wintypes.DWORD, + ctypes.wintypes.DWORD, + ctypes.wintypes.LPWSTR, +] +CreateFileMapping.restype = ctypes.wintypes.HANDLE + +MapViewOfFile = ctypes.windll.kernel32.MapViewOfFile +MapViewOfFile.restype = ctypes.wintypes.HANDLE + +class MemoryMap(object): + """ + A memory map object which can have security attributes overrideden. + """ + def __init__(self, name, length, security_attributes=None): + self.name = name + self.length = length + self.security_attributes = security_attributes + self.pos = 0 + + def __enter__(self): + p_SA = ( + ctypes.byref(self.security_attributes) + if self.security_attributes else None + ) + INVALID_HANDLE_VALUE = -1 + PAGE_READWRITE = 0x4 + FILE_MAP_WRITE = 0x2 + filemap = ctypes.windll.kernel32.CreateFileMappingW( + INVALID_HANDLE_VALUE, p_SA, PAGE_READWRITE, 0, self.length, + u(self.name)) + handle_nonzero_success(filemap) + if filemap == INVALID_HANDLE_VALUE: + raise Exception("Failed to create file mapping") + self.filemap = filemap + self.view = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0) + return self + + def seek(self, pos): + self.pos = pos + + def write(self, msg): + n = len(msg) + if self.pos + n >= self.length: # A little safety. + raise ValueError("Refusing to write %d bytes" % n) + ctypes.windll.kernel32.RtlMoveMemory(self.view + self.pos, msg, n) + self.pos += n + + def read(self, n): + """ + Read n bytes from mapped view. + """ + out = ctypes.create_string_buffer(n) + ctypes.windll.kernel32.RtlMoveMemory(out, self.view + self.pos, n) + self.pos += n + return out.raw + + def __exit__(self, exc_type, exc_val, tb): + ctypes.windll.kernel32.UnmapViewOfFile(self.view) + ctypes.windll.kernel32.CloseHandle(self.filemap) + +######################### +# jaraco.windows.security + +class TokenInformationClass: + TokenUser = 1 + +class TOKEN_USER(ctypes.Structure): + num = 1 + _fields_ = [ + ('SID', ctypes.c_void_p), + ('ATTRIBUTES', ctypes.wintypes.DWORD), + ] + + +class SECURITY_DESCRIPTOR(ctypes.Structure): + """ + typedef struct _SECURITY_DESCRIPTOR + { + UCHAR Revision; + UCHAR Sbz1; + SECURITY_DESCRIPTOR_CONTROL Control; + PSID Owner; + PSID Group; + PACL Sacl; + PACL Dacl; + } SECURITY_DESCRIPTOR; + """ + SECURITY_DESCRIPTOR_CONTROL = USHORT + REVISION = 1 + + _fields_ = [ + ('Revision', ctypes.c_ubyte), + ('Sbz1', ctypes.c_ubyte), + ('Control', SECURITY_DESCRIPTOR_CONTROL), + ('Owner', ctypes.c_void_p), + ('Group', ctypes.c_void_p), + ('Sacl', ctypes.c_void_p), + ('Dacl', ctypes.c_void_p), + ] + +class SECURITY_ATTRIBUTES(ctypes.Structure): + """ + typedef struct _SECURITY_ATTRIBUTES { + DWORD nLength; + LPVOID lpSecurityDescriptor; + BOOL bInheritHandle; + } SECURITY_ATTRIBUTES; + """ + _fields_ = [ + ('nLength', ctypes.wintypes.DWORD), + ('lpSecurityDescriptor', ctypes.c_void_p), + ('bInheritHandle', ctypes.wintypes.BOOL), + ] + + def __init__(self, *args, **kwargs): + super(SECURITY_ATTRIBUTES, self).__init__(*args, **kwargs) + self.nLength = ctypes.sizeof(SECURITY_ATTRIBUTES) + + def _get_descriptor(self): + return self._descriptor + def _set_descriptor(self, descriptor): + self._descriptor = descriptor + self.lpSecurityDescriptor = ctypes.addressof(descriptor) + descriptor = property(_get_descriptor, _set_descriptor) + +def GetTokenInformation(token, information_class): + """ + Given a token, get the token information for it. + """ + data_size = ctypes.wintypes.DWORD() + ctypes.windll.advapi32.GetTokenInformation(token, information_class.num, + 0, 0, ctypes.byref(data_size)) + data = ctypes.create_string_buffer(data_size.value) + handle_nonzero_success(ctypes.windll.advapi32.GetTokenInformation(token, + information_class.num, + ctypes.byref(data), ctypes.sizeof(data), + ctypes.byref(data_size))) + return ctypes.cast(data, ctypes.POINTER(TOKEN_USER)).contents + +class TokenAccess: + TOKEN_QUERY = 0x8 + +def OpenProcessToken(proc_handle, access): + result = ctypes.wintypes.HANDLE() + proc_handle = ctypes.wintypes.HANDLE(proc_handle) + handle_nonzero_success(ctypes.windll.advapi32.OpenProcessToken( + proc_handle, access, ctypes.byref(result))) + return result + +def get_current_user(): + """ + Return a TOKEN_USER for the owner of this process. + """ + process = OpenProcessToken( + ctypes.windll.kernel32.GetCurrentProcess(), + TokenAccess.TOKEN_QUERY, + ) + return GetTokenInformation(process, TOKEN_USER) + +def get_security_attributes_for_user(user=None): + """ + Return a SECURITY_ATTRIBUTES structure with the SID set to the + specified user (uses current user if none is specified). + """ + if user is None: + user = get_current_user() + + assert isinstance(user, TOKEN_USER), "user must be TOKEN_USER instance" + + SD = SECURITY_DESCRIPTOR() + SA = SECURITY_ATTRIBUTES() + # by attaching the actual security descriptor, it will be garbage- + # collected with the security attributes + SA.descriptor = SD + SA.bInheritHandle = 1 + + ctypes.windll.advapi32.InitializeSecurityDescriptor(ctypes.byref(SD), + SECURITY_DESCRIPTOR.REVISION) + ctypes.windll.advapi32.SetSecurityDescriptorOwner(ctypes.byref(SD), + user.SID, 0) + return SA diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/agent.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/agent.py new file mode 100644 index 0000000..5a08d45 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/agent.py @@ -0,0 +1,376 @@ +# Copyright (C) 2003-2007 John Rochester +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +SSH Agent interface +""" + +import os +import socket +import struct +import sys +import threading +import time +import tempfile +import stat +from select import select +from paramiko.common import asbytes, io_sleep +from paramiko.py3compat import byte_chr + +from paramiko.ssh_exception import SSHException +from paramiko.message import Message +from paramiko.pkey import PKey +from paramiko.util import retry_on_signal + +cSSH2_AGENTC_REQUEST_IDENTITIES = byte_chr(11) +SSH2_AGENT_IDENTITIES_ANSWER = 12 +cSSH2_AGENTC_SIGN_REQUEST = byte_chr(13) +SSH2_AGENT_SIGN_RESPONSE = 14 + + +class AgentSSH(object): + def __init__(self): + self._conn = None + self._keys = () + + def get_keys(self): + """ + Return the list of keys available through the SSH agent, if any. If + no SSH agent was running (or it couldn't be contacted), an empty list + will be returned. + + :return: + a tuple of `.AgentKey` objects representing keys available on the + SSH agent + """ + return self._keys + + def _connect(self, conn): + self._conn = conn + ptype, result = self._send_message(cSSH2_AGENTC_REQUEST_IDENTITIES) + if ptype != SSH2_AGENT_IDENTITIES_ANSWER: + raise SSHException('could not get keys from ssh-agent') + keys = [] + for i in range(result.get_int()): + keys.append(AgentKey(self, result.get_binary())) + result.get_string() + self._keys = tuple(keys) + + def _close(self): + #self._conn.close() + self._conn = None + self._keys = () + + def _send_message(self, msg): + msg = asbytes(msg) + self._conn.send(struct.pack('>I', len(msg)) + msg) + l = self._read_all(4) + msg = Message(self._read_all(struct.unpack('>I', l)[0])) + return ord(msg.get_byte()), msg + + def _read_all(self, wanted): + result = self._conn.recv(wanted) + while len(result) < wanted: + if len(result) == 0: + raise SSHException('lost ssh-agent') + extra = self._conn.recv(wanted - len(result)) + if len(extra) == 0: + raise SSHException('lost ssh-agent') + result += extra + return result + + +class AgentProxyThread(threading.Thread): + """ + Class in charge of communication between two channels. + """ + def __init__(self, agent): + threading.Thread.__init__(self, target=self.run) + self._agent = agent + self._exit = False + + def run(self): + try: + (r, addr) = self.get_connection() + self.__inr = r + self.__addr = addr + self._agent.connect() + self._communicate() + except: + #XXX Not sure what to do here ... raise or pass ? + raise + + def _communicate(self): + import fcntl + oldflags = fcntl.fcntl(self.__inr, fcntl.F_GETFL) + fcntl.fcntl(self.__inr, fcntl.F_SETFL, oldflags | os.O_NONBLOCK) + while not self._exit: + events = select([self._agent._conn, self.__inr], [], [], 0.5) + for fd in events[0]: + if self._agent._conn == fd: + data = self._agent._conn.recv(512) + if len(data) != 0: + self.__inr.send(data) + else: + self._close() + break + elif self.__inr == fd: + data = self.__inr.recv(512) + if len(data) != 0: + self._agent._conn.send(data) + else: + self._close() + break + time.sleep(io_sleep) + + def _close(self): + self._exit = True + self.__inr.close() + self._agent._conn.close() + + +class AgentLocalProxy(AgentProxyThread): + """ + Class to be used when wanting to ask a local SSH Agent being + asked from a remote fake agent (so use a unix socket for ex.) + """ + def __init__(self, agent): + AgentProxyThread.__init__(self, agent) + + def get_connection(self): + """ + Return a pair of socket object and string address. + + May block! + """ + conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + try: + conn.bind(self._agent._get_filename()) + conn.listen(1) + (r, addr) = conn.accept() + return r, addr + except: + raise + + +class AgentRemoteProxy(AgentProxyThread): + """ + Class to be used when wanting to ask a remote SSH Agent + """ + def __init__(self, agent, chan): + AgentProxyThread.__init__(self, agent) + self.__chan = chan + + def get_connection(self): + return self.__chan, None + + +class AgentClientProxy(object): + """ + Class proxying request as a client: + + #. client ask for a request_forward_agent() + #. server creates a proxy and a fake SSH Agent + #. server ask for establishing a connection when needed, + calling the forward_agent_handler at client side. + #. the forward_agent_handler launch a thread for connecting + the remote fake agent and the local agent + #. Communication occurs ... + """ + def __init__(self, chanRemote): + self._conn = None + self.__chanR = chanRemote + self.thread = AgentRemoteProxy(self, chanRemote) + self.thread.start() + + def __del__(self): + self.close() + + def connect(self): + """ + Method automatically called by ``AgentProxyThread.run``. + """ + if ('SSH_AUTH_SOCK' in os.environ) and (sys.platform != 'win32'): + conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + try: + retry_on_signal(lambda: conn.connect(os.environ['SSH_AUTH_SOCK'])) + except: + # probably a dangling env var: the ssh agent is gone + return + elif sys.platform == 'win32': + import paramiko.win_pageant as win_pageant + if win_pageant.can_talk_to_agent(): + conn = win_pageant.PageantConnection() + else: + return + else: + # no agent support + return + self._conn = conn + + def close(self): + """ + Close the current connection and terminate the agent + Should be called manually + """ + if hasattr(self, "thread"): + self.thread._exit = True + self.thread.join(1000) + if self._conn is not None: + self._conn.close() + + +class AgentServerProxy(AgentSSH): + """ + :param .Transport t: Transport used for SSH Agent communication forwarding + + :raises SSHException: mostly if we lost the agent + """ + def __init__(self, t): + AgentSSH.__init__(self) + self.__t = t + self._dir = tempfile.mkdtemp('sshproxy') + os.chmod(self._dir, stat.S_IRWXU) + self._file = self._dir + '/sshproxy.ssh' + self.thread = AgentLocalProxy(self) + self.thread.start() + + def __del__(self): + self.close() + + def connect(self): + conn_sock = self.__t.open_forward_agent_channel() + if conn_sock is None: + raise SSHException('lost ssh-agent') + conn_sock.set_name('auth-agent') + self._connect(conn_sock) + + def close(self): + """ + Terminate the agent, clean the files, close connections + Should be called manually + """ + os.remove(self._file) + os.rmdir(self._dir) + self.thread._exit = True + self.thread.join(1000) + self._close() + + def get_env(self): + """ + Helper for the environnement under unix + + :return: + a dict containing the ``SSH_AUTH_SOCK`` environnement variables + """ + return {'SSH_AUTH_SOCK': self._get_filename()} + + def _get_filename(self): + return self._file + + +class AgentRequestHandler(object): + def __init__(self, chanClient): + self._conn = None + self.__chanC = chanClient + chanClient.request_forward_agent(self._forward_agent_handler) + self.__clientProxys = [] + + def _forward_agent_handler(self, chanRemote): + self.__clientProxys.append(AgentClientProxy(chanRemote)) + + def __del__(self): + self.close() + + def close(self): + for p in self.__clientProxys: + p.close() + + +class Agent(AgentSSH): + """ + Client interface for using private keys from an SSH agent running on the + local machine. If an SSH agent is running, this class can be used to + connect to it and retreive `.PKey` objects which can be used when + attempting to authenticate to remote SSH servers. + + Upon initialization, a session with the local machine's SSH agent is + opened, if one is running. If no agent is running, initialization will + succeed, but `get_keys` will return an empty tuple. + + :raises SSHException: + if an SSH agent is found, but speaks an incompatible protocol + """ + def __init__(self): + AgentSSH.__init__(self) + + if ('SSH_AUTH_SOCK' in os.environ) and (sys.platform != 'win32'): + conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + try: + conn.connect(os.environ['SSH_AUTH_SOCK']) + except: + # probably a dangling env var: the ssh agent is gone + return + elif sys.platform == 'win32': + from . import win_pageant + if win_pageant.can_talk_to_agent(): + conn = win_pageant.PageantConnection() + else: + return + else: + # no agent support + return + self._connect(conn) + + def close(self): + """ + Close the SSH agent connection. + """ + self._close() + + +class AgentKey(PKey): + """ + Private key held in a local SSH agent. This type of key can be used for + authenticating to a remote server (signing). Most other key operations + work as expected. + """ + def __init__(self, agent, blob): + self.agent = agent + self.blob = blob + self.name = Message(blob).get_text() + + def asbytes(self): + return self.blob + + def __str__(self): + return self.asbytes() + + def get_name(self): + return self.name + + def sign_ssh_data(self, data): + msg = Message() + msg.add_byte(cSSH2_AGENTC_SIGN_REQUEST) + msg.add_string(self.blob) + msg.add_string(data) + msg.add_int(0) + ptype, result = self.agent._send_message(msg) + if ptype != SSH2_AGENT_SIGN_RESPONSE: + raise SSHException('key cannot be used for signing') + return result.get_binary() diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/auth_handler.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/auth_handler.py new file mode 100644 index 0000000..57babef --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/auth_handler.py @@ -0,0 +1,425 @@ +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +`.AuthHandler` +""" + +import weakref +from paramiko.common import cMSG_SERVICE_REQUEST, cMSG_DISCONNECT, \ + DISCONNECT_SERVICE_NOT_AVAILABLE, DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE, \ + cMSG_USERAUTH_REQUEST, cMSG_SERVICE_ACCEPT, DEBUG, AUTH_SUCCESSFUL, INFO, \ + cMSG_USERAUTH_SUCCESS, cMSG_USERAUTH_FAILURE, AUTH_PARTIALLY_SUCCESSFUL, \ + cMSG_USERAUTH_INFO_REQUEST, WARNING, AUTH_FAILED, cMSG_USERAUTH_PK_OK, \ + cMSG_USERAUTH_INFO_RESPONSE, MSG_SERVICE_REQUEST, MSG_SERVICE_ACCEPT, \ + MSG_USERAUTH_REQUEST, MSG_USERAUTH_SUCCESS, MSG_USERAUTH_FAILURE, \ + MSG_USERAUTH_BANNER, MSG_USERAUTH_INFO_REQUEST, MSG_USERAUTH_INFO_RESPONSE + +from paramiko.message import Message +from paramiko.py3compat import bytestring +from paramiko.ssh_exception import SSHException, AuthenticationException, \ + BadAuthenticationType, PartialAuthentication +from paramiko.server import InteractiveQuery + + +class AuthHandler (object): + """ + Internal class to handle the mechanics of authentication. + """ + + def __init__(self, transport): + self.transport = weakref.proxy(transport) + self.username = None + self.authenticated = False + self.auth_event = None + self.auth_method = '' + self.banner = None + self.password = None + self.private_key = None + self.interactive_handler = None + self.submethods = None + # for server mode: + self.auth_username = None + self.auth_fail_count = 0 + + def is_authenticated(self): + return self.authenticated + + def get_username(self): + if self.transport.server_mode: + return self.auth_username + else: + return self.username + + def auth_none(self, username, event): + self.transport.lock.acquire() + try: + self.auth_event = event + self.auth_method = 'none' + self.username = username + self._request_auth() + finally: + self.transport.lock.release() + + def auth_publickey(self, username, key, event): + self.transport.lock.acquire() + try: + self.auth_event = event + self.auth_method = 'publickey' + self.username = username + self.private_key = key + self._request_auth() + finally: + self.transport.lock.release() + + def auth_password(self, username, password, event): + self.transport.lock.acquire() + try: + self.auth_event = event + self.auth_method = 'password' + self.username = username + self.password = password + self._request_auth() + finally: + self.transport.lock.release() + + def auth_interactive(self, username, handler, event, submethods=''): + """ + response_list = handler(title, instructions, prompt_list) + """ + self.transport.lock.acquire() + try: + self.auth_event = event + self.auth_method = 'keyboard-interactive' + self.username = username + self.interactive_handler = handler + self.submethods = submethods + self._request_auth() + finally: + self.transport.lock.release() + + def abort(self): + if self.auth_event is not None: + self.auth_event.set() + + ### internals... + + def _request_auth(self): + m = Message() + m.add_byte(cMSG_SERVICE_REQUEST) + m.add_string('ssh-userauth') + self.transport._send_message(m) + + def _disconnect_service_not_available(self): + m = Message() + m.add_byte(cMSG_DISCONNECT) + m.add_int(DISCONNECT_SERVICE_NOT_AVAILABLE) + m.add_string('Service not available') + m.add_string('en') + self.transport._send_message(m) + self.transport.close() + + def _disconnect_no_more_auth(self): + m = Message() + m.add_byte(cMSG_DISCONNECT) + m.add_int(DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE) + m.add_string('No more auth methods available') + m.add_string('en') + self.transport._send_message(m) + self.transport.close() + + def _get_session_blob(self, key, service, username): + m = Message() + m.add_string(self.transport.session_id) + m.add_byte(cMSG_USERAUTH_REQUEST) + m.add_string(username) + m.add_string(service) + m.add_string('publickey') + m.add_boolean(True) + m.add_string(key.get_name()) + m.add_string(key) + return m.asbytes() + + def wait_for_response(self, event): + while True: + event.wait(0.1) + if not self.transport.is_active(): + e = self.transport.get_exception() + if (e is None) or issubclass(e.__class__, EOFError): + e = AuthenticationException('Authentication failed.') + raise e + if event.isSet(): + break + if not self.is_authenticated(): + e = self.transport.get_exception() + if e is None: + e = AuthenticationException('Authentication failed.') + # this is horrible. Python Exception isn't yet descended from + # object, so type(e) won't work. :( + if issubclass(e.__class__, PartialAuthentication): + return e.allowed_types + raise e + return [] + + def _parse_service_request(self, m): + service = m.get_text() + if self.transport.server_mode and (service == 'ssh-userauth'): + # accepted + m = Message() + m.add_byte(cMSG_SERVICE_ACCEPT) + m.add_string(service) + self.transport._send_message(m) + return + # dunno this one + self._disconnect_service_not_available() + + def _parse_service_accept(self, m): + service = m.get_text() + if service == 'ssh-userauth': + self.transport._log(DEBUG, 'userauth is OK') + m = Message() + m.add_byte(cMSG_USERAUTH_REQUEST) + m.add_string(self.username) + m.add_string('ssh-connection') + m.add_string(self.auth_method) + if self.auth_method == 'password': + m.add_boolean(False) + password = bytestring(self.password) + m.add_string(password) + elif self.auth_method == 'publickey': + m.add_boolean(True) + m.add_string(self.private_key.get_name()) + m.add_string(self.private_key) + blob = self._get_session_blob(self.private_key, 'ssh-connection', self.username) + sig = self.private_key.sign_ssh_data(blob) + m.add_string(sig) + elif self.auth_method == 'keyboard-interactive': + m.add_string('') + m.add_string(self.submethods) + elif self.auth_method == 'none': + pass + else: + raise SSHException('Unknown auth method "%s"' % self.auth_method) + self.transport._send_message(m) + else: + self.transport._log(DEBUG, 'Service request "%s" accepted (?)' % service) + + def _send_auth_result(self, username, method, result): + # okay, send result + m = Message() + if result == AUTH_SUCCESSFUL: + self.transport._log(INFO, 'Auth granted (%s).' % method) + m.add_byte(cMSG_USERAUTH_SUCCESS) + self.authenticated = True + else: + self.transport._log(INFO, 'Auth rejected (%s).' % method) + m.add_byte(cMSG_USERAUTH_FAILURE) + m.add_string(self.transport.server_object.get_allowed_auths(username)) + if result == AUTH_PARTIALLY_SUCCESSFUL: + m.add_boolean(True) + else: + m.add_boolean(False) + self.auth_fail_count += 1 + self.transport._send_message(m) + if self.auth_fail_count >= 10: + self._disconnect_no_more_auth() + if result == AUTH_SUCCESSFUL: + self.transport._auth_trigger() + + def _interactive_query(self, q): + # make interactive query instead of response + m = Message() + m.add_byte(cMSG_USERAUTH_INFO_REQUEST) + m.add_string(q.name) + m.add_string(q.instructions) + m.add_string(bytes()) + m.add_int(len(q.prompts)) + for p in q.prompts: + m.add_string(p[0]) + m.add_boolean(p[1]) + self.transport._send_message(m) + + def _parse_userauth_request(self, m): + if not self.transport.server_mode: + # er, uh... what? + m = Message() + m.add_byte(cMSG_USERAUTH_FAILURE) + m.add_string('none') + m.add_boolean(False) + self.transport._send_message(m) + return + if self.authenticated: + # ignore + return + username = m.get_text() + service = m.get_text() + method = m.get_text() + self.transport._log(DEBUG, 'Auth request (type=%s) service=%s, username=%s' % (method, service, username)) + if service != 'ssh-connection': + self._disconnect_service_not_available() + return + if (self.auth_username is not None) and (self.auth_username != username): + self.transport._log(WARNING, 'Auth rejected because the client attempted to change username in mid-flight') + self._disconnect_no_more_auth() + return + self.auth_username = username + + if method == 'none': + result = self.transport.server_object.check_auth_none(username) + elif method == 'password': + changereq = m.get_boolean() + password = m.get_binary() + try: + password = password.decode('UTF-8') + except UnicodeError: + # some clients/servers expect non-utf-8 passwords! + # in this case, just return the raw byte string. + pass + if changereq: + # always treated as failure, since we don't support changing passwords, but collect + # the list of valid auth types from the callback anyway + self.transport._log(DEBUG, 'Auth request to change passwords (rejected)') + newpassword = m.get_binary() + try: + newpassword = newpassword.decode('UTF-8', 'replace') + except UnicodeError: + pass + result = AUTH_FAILED + else: + result = self.transport.server_object.check_auth_password(username, password) + elif method == 'publickey': + sig_attached = m.get_boolean() + keytype = m.get_text() + keyblob = m.get_binary() + try: + key = self.transport._key_info[keytype](Message(keyblob)) + except SSHException as e: + self.transport._log(INFO, 'Auth rejected: public key: %s' % str(e)) + key = None + except: + self.transport._log(INFO, 'Auth rejected: unsupported or mangled public key') + key = None + if key is None: + self._disconnect_no_more_auth() + return + # first check if this key is okay... if not, we can skip the verify + result = self.transport.server_object.check_auth_publickey(username, key) + if result != AUTH_FAILED: + # key is okay, verify it + if not sig_attached: + # client wants to know if this key is acceptable, before it + # signs anything... send special "ok" message + m = Message() + m.add_byte(cMSG_USERAUTH_PK_OK) + m.add_string(keytype) + m.add_string(keyblob) + self.transport._send_message(m) + return + sig = Message(m.get_binary()) + blob = self._get_session_blob(key, service, username) + if not key.verify_ssh_sig(blob, sig): + self.transport._log(INFO, 'Auth rejected: invalid signature') + result = AUTH_FAILED + elif method == 'keyboard-interactive': + lang = m.get_string() + submethods = m.get_string() + result = self.transport.server_object.check_auth_interactive(username, submethods) + if isinstance(result, InteractiveQuery): + # make interactive query instead of response + self._interactive_query(result) + return + else: + result = self.transport.server_object.check_auth_none(username) + # okay, send result + self._send_auth_result(username, method, result) + + def _parse_userauth_success(self, m): + self.transport._log(INFO, 'Authentication (%s) successful!' % self.auth_method) + self.authenticated = True + self.transport._auth_trigger() + if self.auth_event is not None: + self.auth_event.set() + + def _parse_userauth_failure(self, m): + authlist = m.get_list() + partial = m.get_boolean() + if partial: + self.transport._log(INFO, 'Authentication continues...') + self.transport._log(DEBUG, 'Methods: ' + str(authlist)) + self.transport.saved_exception = PartialAuthentication(authlist) + elif self.auth_method not in authlist: + self.transport._log(DEBUG, 'Authentication type (%s) not permitted.' % self.auth_method) + self.transport._log(DEBUG, 'Allowed methods: ' + str(authlist)) + self.transport.saved_exception = BadAuthenticationType('Bad authentication type', authlist) + else: + self.transport._log(INFO, 'Authentication (%s) failed.' % self.auth_method) + self.authenticated = False + self.username = None + if self.auth_event is not None: + self.auth_event.set() + + def _parse_userauth_banner(self, m): + banner = m.get_string() + self.banner = banner + lang = m.get_string() + self.transport._log(INFO, 'Auth banner: %s' % banner) + # who cares. + + def _parse_userauth_info_request(self, m): + if self.auth_method != 'keyboard-interactive': + raise SSHException('Illegal info request from server') + title = m.get_text() + instructions = m.get_text() + m.get_binary() # lang + prompts = m.get_int() + prompt_list = [] + for i in range(prompts): + prompt_list.append((m.get_text(), m.get_boolean())) + response_list = self.interactive_handler(title, instructions, prompt_list) + + m = Message() + m.add_byte(cMSG_USERAUTH_INFO_RESPONSE) + m.add_int(len(response_list)) + for r in response_list: + m.add_string(r) + self.transport._send_message(m) + + def _parse_userauth_info_response(self, m): + if not self.transport.server_mode: + raise SSHException('Illegal info response from server') + n = m.get_int() + responses = [] + for i in range(n): + responses.append(m.get_text()) + result = self.transport.server_object.check_auth_interactive_response(responses) + if isinstance(type(result), InteractiveQuery): + # make interactive query instead of response + self._interactive_query(result) + return + self._send_auth_result(self.auth_username, 'keyboard-interactive', result) + + _handler_table = { + MSG_SERVICE_REQUEST: _parse_service_request, + MSG_SERVICE_ACCEPT: _parse_service_accept, + MSG_USERAUTH_REQUEST: _parse_userauth_request, + MSG_USERAUTH_SUCCESS: _parse_userauth_success, + MSG_USERAUTH_FAILURE: _parse_userauth_failure, + MSG_USERAUTH_BANNER: _parse_userauth_banner, + MSG_USERAUTH_INFO_REQUEST: _parse_userauth_info_request, + MSG_USERAUTH_INFO_RESPONSE: _parse_userauth_info_response, + } diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/ber.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/ber.py new file mode 100644 index 0000000..0515230 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/ber.py @@ -0,0 +1,133 @@ +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +from paramiko.common import max_byte, zero_byte +from paramiko.py3compat import b, byte_ord, byte_chr, long + +import paramiko.util as util + + +class BERException (Exception): + pass + + +class BER(object): + """ + Robey's tiny little attempt at a BER decoder. + """ + + def __init__(self, content=bytes()): + self.content = b(content) + self.idx = 0 + + def asbytes(self): + return self.content + + def __str__(self): + return self.asbytes() + + def __repr__(self): + return 'BER(\'' + repr(self.content) + '\')' + + def decode(self): + return self.decode_next() + + def decode_next(self): + if self.idx >= len(self.content): + return None + ident = byte_ord(self.content[self.idx]) + self.idx += 1 + if (ident & 31) == 31: + # identifier > 30 + ident = 0 + while self.idx < len(self.content): + t = byte_ord(self.content[self.idx]) + self.idx += 1 + ident = (ident << 7) | (t & 0x7f) + if not (t & 0x80): + break + if self.idx >= len(self.content): + return None + # now fetch length + size = byte_ord(self.content[self.idx]) + self.idx += 1 + if size & 0x80: + # more complimicated... + # FIXME: theoretically should handle indefinite-length (0x80) + t = size & 0x7f + if self.idx + t > len(self.content): + return None + size = util.inflate_long(self.content[self.idx: self.idx + t], True) + self.idx += t + if self.idx + size > len(self.content): + # can't fit + return None + data = self.content[self.idx: self.idx + size] + self.idx += size + # now switch on id + if ident == 0x30: + # sequence + return self.decode_sequence(data) + elif ident == 2: + # int + return util.inflate_long(data) + else: + # 1: boolean (00 false, otherwise true) + raise BERException('Unknown ber encoding type %d (robey is lazy)' % ident) + + def decode_sequence(data): + out = [] + ber = BER(data) + while True: + x = ber.decode_next() + if x is None: + break + out.append(x) + return out + decode_sequence = staticmethod(decode_sequence) + + def encode_tlv(self, ident, val): + # no need to support ident > 31 here + self.content += byte_chr(ident) + if len(val) > 0x7f: + lenstr = util.deflate_long(len(val)) + self.content += byte_chr(0x80 + len(lenstr)) + lenstr + else: + self.content += byte_chr(len(val)) + self.content += val + + def encode(self, x): + if type(x) is bool: + if x: + self.encode_tlv(1, max_byte) + else: + self.encode_tlv(1, zero_byte) + elif (type(x) is int) or (type(x) is long): + self.encode_tlv(2, util.deflate_long(x)) + elif type(x) is str: + self.encode_tlv(4, x) + elif (type(x) is list) or (type(x) is tuple): + self.encode_tlv(0x30, self.encode_sequence(x)) + else: + raise BERException('Unknown type for encoding: %s' % repr(type(x))) + + def encode_sequence(data): + ber = BER() + for item in data: + ber.encode(item) + return ber.asbytes() + encode_sequence = staticmethod(encode_sequence) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/buffered_pipe.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/buffered_pipe.py new file mode 100644 index 0000000..ac35b3e --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/buffered_pipe.py @@ -0,0 +1,209 @@ +# Copyright (C) 2006-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +Attempt to generalize the "feeder" part of a `.Channel`: an object which can be +read from and closed, but is reading from a buffer fed by another thread. The +read operations are blocking and can have a timeout set. +""" + +import array +import threading +import time +from paramiko.py3compat import PY2, b + + +class PipeTimeout (IOError): + """ + Indicates that a timeout was reached on a read from a `.BufferedPipe`. + """ + pass + + +class BufferedPipe (object): + """ + A buffer that obeys normal read (with timeout) & close semantics for a + file or socket, but is fed data from another thread. This is used by + `.Channel`. + """ + + def __init__(self): + self._lock = threading.Lock() + self._cv = threading.Condition(self._lock) + self._event = None + self._buffer = array.array('B') + self._closed = False + + if PY2: + def _buffer_frombytes(self, data): + self._buffer.fromstring(data) + + def _buffer_tobytes(self, limit=None): + return self._buffer[:limit].tostring() + else: + def _buffer_frombytes(self, data): + self._buffer.frombytes(data) + + def _buffer_tobytes(self, limit=None): + return self._buffer[:limit].tobytes() + + def set_event(self, event): + """ + Set an event on this buffer. When data is ready to be read (or the + buffer has been closed), the event will be set. When no data is + ready, the event will be cleared. + + :param threading.Event event: the event to set/clear + """ + self._event = event + if len(self._buffer) > 0: + event.set() + else: + event.clear() + + def feed(self, data): + """ + Feed new data into this pipe. This method is assumed to be called + from a separate thread, so synchronization is done. + + :param data: the data to add, as a `str` + """ + self._lock.acquire() + try: + if self._event is not None: + self._event.set() + self._buffer_frombytes(b(data)) + self._cv.notifyAll() + finally: + self._lock.release() + + def read_ready(self): + """ + Returns true if data is buffered and ready to be read from this + feeder. A ``False`` result does not mean that the feeder has closed; + it means you may need to wait before more data arrives. + + :return: + ``True`` if a `read` call would immediately return at least one + byte; ``False`` otherwise. + """ + self._lock.acquire() + try: + if len(self._buffer) == 0: + return False + return True + finally: + self._lock.release() + + def read(self, nbytes, timeout=None): + """ + Read data from the pipe. The return value is a string representing + the data received. The maximum amount of data to be received at once + is specified by ``nbytes``. If a string of length zero is returned, + the pipe has been closed. + + The optional ``timeout`` argument can be a nonnegative float expressing + seconds, or ``None`` for no timeout. If a float is given, a + `.PipeTimeout` will be raised if the timeout period value has elapsed + before any data arrives. + + :param int nbytes: maximum number of bytes to read + :param float timeout: + maximum seconds to wait (or ``None``, the default, to wait forever) + :return: the read data, as a `str` + + :raises PipeTimeout: + if a timeout was specified and no data was ready before that + timeout + """ + out = bytes() + self._lock.acquire() + try: + if len(self._buffer) == 0: + if self._closed: + return out + # should we block? + if timeout == 0.0: + raise PipeTimeout() + # loop here in case we get woken up but a different thread has + # grabbed everything in the buffer. + while (len(self._buffer) == 0) and not self._closed: + then = time.time() + self._cv.wait(timeout) + if timeout is not None: + timeout -= time.time() - then + if timeout <= 0.0: + raise PipeTimeout() + + # something's in the buffer and we have the lock! + if len(self._buffer) <= nbytes: + out = self._buffer_tobytes() + del self._buffer[:] + if (self._event is not None) and not self._closed: + self._event.clear() + else: + out = self._buffer_tobytes(nbytes) + del self._buffer[:nbytes] + finally: + self._lock.release() + + return out + + def empty(self): + """ + Clear out the buffer and return all data that was in it. + + :return: + any data that was in the buffer prior to clearing it out, as a + `str` + """ + self._lock.acquire() + try: + out = self._buffer_tobytes() + del self._buffer[:] + if (self._event is not None) and not self._closed: + self._event.clear() + return out + finally: + self._lock.release() + + def close(self): + """ + Close this pipe object. Future calls to `read` after the buffer + has been emptied will return immediately with an empty string. + """ + self._lock.acquire() + try: + self._closed = True + self._cv.notifyAll() + if self._event is not None: + self._event.set() + finally: + self._lock.release() + + def __len__(self): + """ + Return the number of bytes buffered. + + :return: number (`int`) of bytes buffered + """ + self._lock.acquire() + try: + return len(self._buffer) + finally: + self._lock.release() diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/channel.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/channel.py new file mode 100644 index 0000000..583809d --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/channel.py @@ -0,0 +1,1232 @@ +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +Abstraction for an SSH2 channel. +""" + +import binascii +import os +import socket +import time +import threading + +from paramiko import util +from paramiko.common import cMSG_CHANNEL_REQUEST, cMSG_CHANNEL_WINDOW_ADJUST, \ + cMSG_CHANNEL_DATA, cMSG_CHANNEL_EXTENDED_DATA, DEBUG, ERROR, \ + cMSG_CHANNEL_SUCCESS, cMSG_CHANNEL_FAILURE, cMSG_CHANNEL_EOF, \ + cMSG_CHANNEL_CLOSE +from paramiko.message import Message +from paramiko.py3compat import bytes_types +from paramiko.ssh_exception import SSHException +from paramiko.file import BufferedFile +from paramiko.buffered_pipe import BufferedPipe, PipeTimeout +from paramiko import pipe + + +# lower bound on the max packet size we'll accept from the remote host +MIN_PACKET_SIZE = 1024 + + +class Channel (object): + """ + A secure tunnel across an SSH `.Transport`. A Channel is meant to behave + like a socket, and has an API that should be indistinguishable from the + Python socket API. + + Because SSH2 has a windowing kind of flow control, if you stop reading data + from a Channel and its buffer fills up, the server will be unable to send + you any more data until you read some of it. (This won't affect other + channels on the same transport -- all channels on a single transport are + flow-controlled independently.) Similarly, if the server isn't reading + data you send, calls to `send` may block, unless you set a timeout. This + is exactly like a normal network socket, so it shouldn't be too surprising. + """ + + def __init__(self, chanid): + """ + Create a new channel. The channel is not associated with any + particular session or `.Transport` until the Transport attaches it. + Normally you would only call this method from the constructor of a + subclass of `.Channel`. + + :param int chanid: + the ID of this channel, as passed by an existing `.Transport`. + """ + self.chanid = chanid + self.remote_chanid = 0 + self.transport = None + self.active = False + self.eof_received = 0 + self.eof_sent = 0 + self.in_buffer = BufferedPipe() + self.in_stderr_buffer = BufferedPipe() + self.timeout = None + self.closed = False + self.ultra_debug = False + self.lock = threading.Lock() + self.out_buffer_cv = threading.Condition(self.lock) + self.in_window_size = 0 + self.out_window_size = 0 + self.in_max_packet_size = 0 + self.out_max_packet_size = 0 + self.in_window_threshold = 0 + self.in_window_sofar = 0 + self.status_event = threading.Event() + self._name = str(chanid) + self.logger = util.get_logger('paramiko.transport') + self._pipe = None + self.event = threading.Event() + self.event_ready = False + self.combine_stderr = False + self.exit_status = -1 + self.origin_addr = None + + def __del__(self): + try: + self.close() + except: + pass + + def __repr__(self): + """ + Return a string representation of this object, for debugging. + """ + out = ' 0: + out += ' in-buffer=%d' % (len(self.in_buffer),) + out += ' -> ' + repr(self.transport) + out += '>' + return out + + def get_pty(self, term='vt100', width=80, height=24, width_pixels=0, + height_pixels=0): + """ + Request a pseudo-terminal from the server. This is usually used right + after creating a client channel, to ask the server to provide some + basic terminal semantics for a shell invoked with `invoke_shell`. + It isn't necessary (or desirable) to call this method if you're going + to exectue a single command with `exec_command`. + + :param str term: the terminal type to emulate (for example, ``'vt100'``) + :param int width: width (in characters) of the terminal screen + :param int height: height (in characters) of the terminal screen + :param int width_pixels: width (in pixels) of the terminal screen + :param int height_pixels: height (in pixels) of the terminal screen + + :raises SSHException: + if the request was rejected or the channel was closed + """ + if self.closed or self.eof_received or self.eof_sent or not self.active: + raise SSHException('Channel is not open') + m = Message() + m.add_byte(cMSG_CHANNEL_REQUEST) + m.add_int(self.remote_chanid) + m.add_string('pty-req') + m.add_boolean(True) + m.add_string(term) + m.add_int(width) + m.add_int(height) + m.add_int(width_pixels) + m.add_int(height_pixels) + m.add_string(bytes()) + self._event_pending() + self.transport._send_user_message(m) + self._wait_for_event() + + def invoke_shell(self): + """ + Request an interactive shell session on this channel. If the server + allows it, the channel will then be directly connected to the stdin, + stdout, and stderr of the shell. + + Normally you would call `get_pty` before this, in which case the + shell will operate through the pty, and the channel will be connected + to the stdin and stdout of the pty. + + When the shell exits, the channel will be closed and can't be reused. + You must open a new channel if you wish to open another shell. + + :raises SSHException: if the request was rejected or the channel was + closed + """ + if self.closed or self.eof_received or self.eof_sent or not self.active: + raise SSHException('Channel is not open') + m = Message() + m.add_byte(cMSG_CHANNEL_REQUEST) + m.add_int(self.remote_chanid) + m.add_string('shell') + m.add_boolean(True) + self._event_pending() + self.transport._send_user_message(m) + self._wait_for_event() + + def exec_command(self, command): + """ + Execute a command on the server. If the server allows it, the channel + will then be directly connected to the stdin, stdout, and stderr of + the command being executed. + + When the command finishes executing, the channel will be closed and + can't be reused. You must open a new channel if you wish to execute + another command. + + :param str command: a shell command to execute. + + :raises SSHException: if the request was rejected or the channel was + closed + """ + if self.closed or self.eof_received or self.eof_sent or not self.active: + raise SSHException('Channel is not open') + m = Message() + m.add_byte(cMSG_CHANNEL_REQUEST) + m.add_int(self.remote_chanid) + m.add_string('exec') + m.add_boolean(True) + m.add_string(command) + self._event_pending() + self.transport._send_user_message(m) + self._wait_for_event() + + def invoke_subsystem(self, subsystem): + """ + Request a subsystem on the server (for example, ``sftp``). If the + server allows it, the channel will then be directly connected to the + requested subsystem. + + When the subsystem finishes, the channel will be closed and can't be + reused. + + :param str subsystem: name of the subsystem being requested. + + :raises SSHException: + if the request was rejected or the channel was closed + """ + if self.closed or self.eof_received or self.eof_sent or not self.active: + raise SSHException('Channel is not open') + m = Message() + m.add_byte(cMSG_CHANNEL_REQUEST) + m.add_int(self.remote_chanid) + m.add_string('subsystem') + m.add_boolean(True) + m.add_string(subsystem) + self._event_pending() + self.transport._send_user_message(m) + self._wait_for_event() + + def resize_pty(self, width=80, height=24, width_pixels=0, height_pixels=0): + """ + Resize the pseudo-terminal. This can be used to change the width and + height of the terminal emulation created in a previous `get_pty` call. + + :param int width: new width (in characters) of the terminal screen + :param int height: new height (in characters) of the terminal screen + :param int width_pixels: new width (in pixels) of the terminal screen + :param int height_pixels: new height (in pixels) of the terminal screen + + :raises SSHException: + if the request was rejected or the channel was closed + """ + if self.closed or self.eof_received or self.eof_sent or not self.active: + raise SSHException('Channel is not open') + m = Message() + m.add_byte(cMSG_CHANNEL_REQUEST) + m.add_int(self.remote_chanid) + m.add_string('window-change') + m.add_boolean(False) + m.add_int(width) + m.add_int(height) + m.add_int(width_pixels) + m.add_int(height_pixels) + self.transport._send_user_message(m) + + def exit_status_ready(self): + """ + Return true if the remote process has exited and returned an exit + status. You may use this to poll the process status if you don't + want to block in `recv_exit_status`. Note that the server may not + return an exit status in some cases (like bad servers). + + :return: + ``True`` if `recv_exit_status` will return immediately, else ``False``. + + .. versionadded:: 1.7.3 + """ + return self.closed or self.status_event.isSet() + + def recv_exit_status(self): + """ + Return the exit status from the process on the server. This is + mostly useful for retrieving the reults of an `exec_command`. + If the command hasn't finished yet, this method will wait until + it does, or until the channel is closed. If no exit status is + provided by the server, -1 is returned. + + :return: the exit code (as an `int`) of the process on the server. + + .. versionadded:: 1.2 + """ + self.status_event.wait() + assert self.status_event.isSet() + return self.exit_status + + def send_exit_status(self, status): + """ + Send the exit status of an executed command to the client. (This + really only makes sense in server mode.) Many clients expect to + get some sort of status code back from an executed command after + it completes. + + :param int status: the exit code of the process + + .. versionadded:: 1.2 + """ + # in many cases, the channel will not still be open here. + # that's fine. + m = Message() + m.add_byte(cMSG_CHANNEL_REQUEST) + m.add_int(self.remote_chanid) + m.add_string('exit-status') + m.add_boolean(False) + m.add_int(status) + self.transport._send_user_message(m) + + def request_x11(self, screen_number=0, auth_protocol=None, auth_cookie=None, + single_connection=False, handler=None): + """ + Request an x11 session on this channel. If the server allows it, + further x11 requests can be made from the server to the client, + when an x11 application is run in a shell session. + + From RFC4254:: + + It is RECOMMENDED that the 'x11 authentication cookie' that is + sent be a fake, random cookie, and that the cookie be checked and + replaced by the real cookie when a connection request is received. + + If you omit the auth_cookie, a new secure random 128-bit value will be + generated, used, and returned. You will need to use this value to + verify incoming x11 requests and replace them with the actual local + x11 cookie (which requires some knoweldge of the x11 protocol). + + If a handler is passed in, the handler is called from another thread + whenever a new x11 connection arrives. The default handler queues up + incoming x11 connections, which may be retrieved using + `.Transport.accept`. The handler's calling signature is:: + + handler(channel: Channel, (address: str, port: int)) + + :param int screen_number: the x11 screen number (0, 10, etc) + :param str auth_protocol: + the name of the X11 authentication method used; if none is given, + ``"MIT-MAGIC-COOKIE-1"`` is used + :param str auth_cookie: + hexadecimal string containing the x11 auth cookie; if none is + given, a secure random 128-bit value is generated + :param bool single_connection: + if True, only a single x11 connection will be forwarded (by + default, any number of x11 connections can arrive over this + session) + :param function handler: + an optional handler to use for incoming X11 connections + :return: the auth_cookie used + """ + if self.closed or self.eof_received or self.eof_sent or not self.active: + raise SSHException('Channel is not open') + if auth_protocol is None: + auth_protocol = 'MIT-MAGIC-COOKIE-1' + if auth_cookie is None: + auth_cookie = binascii.hexlify(os.urandom(16)) + + m = Message() + m.add_byte(cMSG_CHANNEL_REQUEST) + m.add_int(self.remote_chanid) + m.add_string('x11-req') + m.add_boolean(True) + m.add_boolean(single_connection) + m.add_string(auth_protocol) + m.add_string(auth_cookie) + m.add_int(screen_number) + self._event_pending() + self.transport._send_user_message(m) + self._wait_for_event() + self.transport._set_x11_handler(handler) + return auth_cookie + + def request_forward_agent(self, handler): + """ + Request for a forward SSH Agent on this channel. + This is only valid for an ssh-agent from OpenSSH !!! + + :param function handler: + a required handler to use for incoming SSH Agent connections + + :return: True if we are ok, else False (at that time we always return ok) + + :raises: SSHException in case of channel problem. + """ + if self.closed or self.eof_received or self.eof_sent or not self.active: + raise SSHException('Channel is not open') + + m = Message() + m.add_byte(cMSG_CHANNEL_REQUEST) + m.add_int(self.remote_chanid) + m.add_string('auth-agent-req@openssh.com') + m.add_boolean(False) + self.transport._send_user_message(m) + self.transport._set_forward_agent_handler(handler) + return True + + def get_transport(self): + """ + Return the `.Transport` associated with this channel. + """ + return self.transport + + def set_name(self, name): + """ + Set a name for this channel. Currently it's only used to set the name + of the channel in logfile entries. The name can be fetched with the + `get_name` method. + + :param str name: new channel name + """ + self._name = name + + def get_name(self): + """ + Get the name of this channel that was previously set by `set_name`. + """ + return self._name + + def get_id(self): + """ + Return the `int` ID # for this channel. + + The channel ID is unique across a `.Transport` and usually a small + number. It's also the number passed to + `.ServerInterface.check_channel_request` when determining whether to + accept a channel request in server mode. + """ + return self.chanid + + def set_combine_stderr(self, combine): + """ + Set whether stderr should be combined into stdout on this channel. + The default is ``False``, but in some cases it may be convenient to + have both streams combined. + + If this is ``False``, and `exec_command` is called (or ``invoke_shell`` + with no pty), output to stderr will not show up through the `recv` + and `recv_ready` calls. You will have to use `recv_stderr` and + `recv_stderr_ready` to get stderr output. + + If this is ``True``, data will never show up via `recv_stderr` or + `recv_stderr_ready`. + + :param bool combine: + ``True`` if stderr output should be combined into stdout on this + channel. + :return: the previous setting (a `bool`). + + .. versionadded:: 1.1 + """ + data = bytes() + self.lock.acquire() + try: + old = self.combine_stderr + self.combine_stderr = combine + if combine and not old: + # copy old stderr buffer into primary buffer + data = self.in_stderr_buffer.empty() + finally: + self.lock.release() + if len(data) > 0: + self._feed(data) + return old + + ### socket API + + def settimeout(self, timeout): + """ + Set a timeout on blocking read/write operations. The ``timeout`` + argument can be a nonnegative float expressing seconds, or ``None``. If + a float is given, subsequent channel read/write operations will raise + a timeout exception if the timeout period value has elapsed before the + operation has completed. Setting a timeout of ``None`` disables + timeouts on socket operations. + + ``chan.settimeout(0.0)`` is equivalent to ``chan.setblocking(0)``; + ``chan.settimeout(None)`` is equivalent to ``chan.setblocking(1)``. + + :param float timeout: + seconds to wait for a pending read/write operation before raising + ``socket.timeout``, or ``None`` for no timeout. + """ + self.timeout = timeout + + def gettimeout(self): + """ + Returns the timeout in seconds (as a float) associated with socket + operations, or ``None`` if no timeout is set. This reflects the last + call to `setblocking` or `settimeout`. + """ + return self.timeout + + def setblocking(self, blocking): + """ + Set blocking or non-blocking mode of the channel: if ``blocking`` is 0, + the channel is set to non-blocking mode; otherwise it's set to blocking + mode. Initially all channels are in blocking mode. + + In non-blocking mode, if a `recv` call doesn't find any data, or if a + `send` call can't immediately dispose of the data, an error exception + is raised. In blocking mode, the calls block until they can proceed. An + EOF condition is considered "immediate data" for `recv`, so if the + channel is closed in the read direction, it will never block. + + ``chan.setblocking(0)`` is equivalent to ``chan.settimeout(0)``; + ``chan.setblocking(1)`` is equivalent to ``chan.settimeout(None)``. + + :param int blocking: + 0 to set non-blocking mode; non-0 to set blocking mode. + """ + if blocking: + self.settimeout(None) + else: + self.settimeout(0.0) + + def getpeername(self): + """ + Return the address of the remote side of this Channel, if possible. + + This simply wraps `.Transport.getpeername`, used to provide enough of a + socket-like interface to allow asyncore to work. (asyncore likes to + call ``'getpeername'``.) + """ + return self.transport.getpeername() + + def close(self): + """ + Close the channel. All future read/write operations on the channel + will fail. The remote end will receive no more data (after queued data + is flushed). Channels are automatically closed when their `.Transport` + is closed or when they are garbage collected. + """ + self.lock.acquire() + try: + # only close the pipe when the user explicitly closes the channel. + # otherwise they will get unpleasant surprises. (and do it before + # checking self.closed, since the remote host may have already + # closed the connection.) + if self._pipe is not None: + self._pipe.close() + self._pipe = None + + if not self.active or self.closed: + return + msgs = self._close_internal() + finally: + self.lock.release() + for m in msgs: + if m is not None: + self.transport._send_user_message(m) + + def recv_ready(self): + """ + Returns true if data is buffered and ready to be read from this + channel. A ``False`` result does not mean that the channel has closed; + it means you may need to wait before more data arrives. + + :return: + ``True`` if a `recv` call on this channel would immediately return + at least one byte; ``False`` otherwise. + """ + return self.in_buffer.read_ready() + + def recv(self, nbytes): + """ + Receive data from the channel. The return value is a string + representing the data received. The maximum amount of data to be + received at once is specified by ``nbytes``. If a string of length zero + is returned, the channel stream has closed. + + :param int nbytes: maximum number of bytes to read. + :return: received data, as a `str` + + :raises socket.timeout: + if no data is ready before the timeout set by `settimeout`. + """ + try: + out = self.in_buffer.read(nbytes, self.timeout) + except PipeTimeout: + raise socket.timeout() + + ack = self._check_add_window(len(out)) + # no need to hold the channel lock when sending this + if ack > 0: + m = Message() + m.add_byte(cMSG_CHANNEL_WINDOW_ADJUST) + m.add_int(self.remote_chanid) + m.add_int(ack) + self.transport._send_user_message(m) + + return out + + def recv_stderr_ready(self): + """ + Returns true if data is buffered and ready to be read from this + channel's stderr stream. Only channels using `exec_command` or + `invoke_shell` without a pty will ever have data on the stderr + stream. + + :return: + ``True`` if a `recv_stderr` call on this channel would immediately + return at least one byte; ``False`` otherwise. + + .. versionadded:: 1.1 + """ + return self.in_stderr_buffer.read_ready() + + def recv_stderr(self, nbytes): + """ + Receive data from the channel's stderr stream. Only channels using + `exec_command` or `invoke_shell` without a pty will ever have data + on the stderr stream. The return value is a string representing the + data received. The maximum amount of data to be received at once is + specified by ``nbytes``. If a string of length zero is returned, the + channel stream has closed. + + :param int nbytes: maximum number of bytes to read. + :return: received data as a `str` + + :raises socket.timeout: if no data is ready before the timeout set by + `settimeout`. + + .. versionadded:: 1.1 + """ + try: + out = self.in_stderr_buffer.read(nbytes, self.timeout) + except PipeTimeout: + raise socket.timeout() + + ack = self._check_add_window(len(out)) + # no need to hold the channel lock when sending this + if ack > 0: + m = Message() + m.add_byte(cMSG_CHANNEL_WINDOW_ADJUST) + m.add_int(self.remote_chanid) + m.add_int(ack) + self.transport._send_user_message(m) + + return out + + def send_ready(self): + """ + Returns true if data can be written to this channel without blocking. + This means the channel is either closed (so any write attempt would + return immediately) or there is at least one byte of space in the + outbound buffer. If there is at least one byte of space in the + outbound buffer, a `send` call will succeed immediately and return + the number of bytes actually written. + + :return: + ``True`` if a `send` call on this channel would immediately succeed + or fail + """ + self.lock.acquire() + try: + if self.closed or self.eof_sent: + return True + return self.out_window_size > 0 + finally: + self.lock.release() + + def send(self, s): + """ + Send data to the channel. Returns the number of bytes sent, or 0 if + the channel stream is closed. Applications are responsible for + checking that all data has been sent: if only some of the data was + transmitted, the application needs to attempt delivery of the remaining + data. + + :param str s: data to send + :return: number of bytes actually sent, as an `int` + + :raises socket.timeout: if no data could be sent before the timeout set + by `settimeout`. + """ + size = len(s) + self.lock.acquire() + try: + size = self._wait_for_send_window(size) + if size == 0: + # eof or similar + return 0 + m = Message() + m.add_byte(cMSG_CHANNEL_DATA) + m.add_int(self.remote_chanid) + m.add_string(s[:size]) + finally: + self.lock.release() + # Note: We release self.lock before calling _send_user_message. + # Otherwise, we can deadlock during re-keying. + self.transport._send_user_message(m) + return size + + def send_stderr(self, s): + """ + Send data to the channel on the "stderr" stream. This is normally + only used by servers to send output from shell commands -- clients + won't use this. Returns the number of bytes sent, or 0 if the channel + stream is closed. Applications are responsible for checking that all + data has been sent: if only some of the data was transmitted, the + application needs to attempt delivery of the remaining data. + + :param str s: data to send. + :return: number of bytes actually sent, as an `int`. + + :raises socket.timeout: + if no data could be sent before the timeout set by `settimeout`. + + .. versionadded:: 1.1 + """ + size = len(s) + self.lock.acquire() + try: + size = self._wait_for_send_window(size) + if size == 0: + # eof or similar + return 0 + m = Message() + m.add_byte(cMSG_CHANNEL_EXTENDED_DATA) + m.add_int(self.remote_chanid) + m.add_int(1) + m.add_string(s[:size]) + finally: + self.lock.release() + # Note: We release self.lock before calling _send_user_message. + # Otherwise, we can deadlock during re-keying. + self.transport._send_user_message(m) + return size + + def sendall(self, s): + """ + Send data to the channel, without allowing partial results. Unlike + `send`, this method continues to send data from the given string until + either all data has been sent or an error occurs. Nothing is returned. + + :param str s: data to send. + + :raises socket.timeout: + if sending stalled for longer than the timeout set by `settimeout`. + :raises socket.error: + if an error occured before the entire string was sent. + + .. note:: + If the channel is closed while only part of the data hase been + sent, there is no way to determine how much data (if any) was sent. + This is irritating, but identically follows Python's API. + """ + while s: + if self.closed: + # this doesn't seem useful, but it is the documented behavior of Socket + raise socket.error('Socket is closed') + sent = self.send(s) + s = s[sent:] + return None + + def sendall_stderr(self, s): + """ + Send data to the channel's "stderr" stream, without allowing partial + results. Unlike `send_stderr`, this method continues to send data + from the given string until all data has been sent or an error occurs. + Nothing is returned. + + :param str s: data to send to the client as "stderr" output. + + :raises socket.timeout: + if sending stalled for longer than the timeout set by `settimeout`. + :raises socket.error: + if an error occured before the entire string was sent. + + .. versionadded:: 1.1 + """ + while s: + if self.closed: + raise socket.error('Socket is closed') + sent = self.send_stderr(s) + s = s[sent:] + return None + + def makefile(self, *params): + """ + Return a file-like object associated with this channel. The optional + ``mode`` and ``bufsize`` arguments are interpreted the same way as by + the built-in ``file()`` function in Python. + + :return: `.ChannelFile` object which can be used for Python file I/O. + """ + return ChannelFile(*([self] + list(params))) + + def makefile_stderr(self, *params): + """ + Return a file-like object associated with this channel's stderr + stream. Only channels using `exec_command` or `invoke_shell` + without a pty will ever have data on the stderr stream. + + The optional ``mode`` and ``bufsize`` arguments are interpreted the + same way as by the built-in ``file()`` function in Python. For a + client, it only makes sense to open this file for reading. For a + server, it only makes sense to open this file for writing. + + :return: `.ChannelFile` object which can be used for Python file I/O. + + .. versionadded:: 1.1 + """ + return ChannelStderrFile(*([self] + list(params))) + + def fileno(self): + """ + Returns an OS-level file descriptor which can be used for polling, but + but not for reading or writing. This is primaily to allow Python's + ``select`` module to work. + + The first time ``fileno`` is called on a channel, a pipe is created to + simulate real OS-level file descriptor (FD) behavior. Because of this, + two OS-level FDs are created, which will use up FDs faster than normal. + (You won't notice this effect unless you have hundreds of channels + open at the same time.) + + :return: an OS-level file descriptor (`int`) + + .. warning:: + This method causes channel reads to be slightly less efficient. + """ + self.lock.acquire() + try: + if self._pipe is not None: + return self._pipe.fileno() + # create the pipe and feed in any existing data + self._pipe = pipe.make_pipe() + p1, p2 = pipe.make_or_pipe(self._pipe) + self.in_buffer.set_event(p1) + self.in_stderr_buffer.set_event(p2) + return self._pipe.fileno() + finally: + self.lock.release() + + def shutdown(self, how): + """ + Shut down one or both halves of the connection. If ``how`` is 0, + further receives are disallowed. If ``how`` is 1, further sends + are disallowed. If ``how`` is 2, further sends and receives are + disallowed. This closes the stream in one or both directions. + + :param int how: + 0 (stop receiving), 1 (stop sending), or 2 (stop receiving and + sending). + """ + if (how == 0) or (how == 2): + # feign "read" shutdown + self.eof_received = 1 + if (how == 1) or (how == 2): + self.lock.acquire() + try: + m = self._send_eof() + finally: + self.lock.release() + if m is not None: + self.transport._send_user_message(m) + + def shutdown_read(self): + """ + Shutdown the receiving side of this socket, closing the stream in + the incoming direction. After this call, future reads on this + channel will fail instantly. This is a convenience method, equivalent + to ``shutdown(0)``, for people who don't make it a habit to + memorize unix constants from the 1970s. + + .. versionadded:: 1.2 + """ + self.shutdown(0) + + def shutdown_write(self): + """ + Shutdown the sending side of this socket, closing the stream in + the outgoing direction. After this call, future writes on this + channel will fail instantly. This is a convenience method, equivalent + to ``shutdown(1)``, for people who don't make it a habit to + memorize unix constants from the 1970s. + + .. versionadded:: 1.2 + """ + self.shutdown(1) + + ### calls from Transport + + def _set_transport(self, transport): + self.transport = transport + self.logger = util.get_logger(self.transport.get_log_channel()) + + def _set_window(self, window_size, max_packet_size): + self.in_window_size = window_size + self.in_max_packet_size = max_packet_size + # threshold of bytes we receive before we bother to send a window update + self.in_window_threshold = window_size // 10 + self.in_window_sofar = 0 + self._log(DEBUG, 'Max packet in: %d bytes' % max_packet_size) + + def _set_remote_channel(self, chanid, window_size, max_packet_size): + self.remote_chanid = chanid + self.out_window_size = window_size + self.out_max_packet_size = max(max_packet_size, MIN_PACKET_SIZE) + self.active = 1 + self._log(DEBUG, 'Max packet out: %d bytes' % max_packet_size) + + def _request_success(self, m): + self._log(DEBUG, 'Sesch channel %d request ok' % self.chanid) + self.event_ready = True + self.event.set() + return + + def _request_failed(self, m): + self.lock.acquire() + try: + msgs = self._close_internal() + finally: + self.lock.release() + for m in msgs: + if m is not None: + self.transport._send_user_message(m) + + def _feed(self, m): + if isinstance(m, bytes_types): + # passed from _feed_extended + s = m + else: + s = m.get_binary() + self.in_buffer.feed(s) + + def _feed_extended(self, m): + code = m.get_int() + s = m.get_binary() + if code != 1: + self._log(ERROR, 'unknown extended_data type %d; discarding' % code) + return + if self.combine_stderr: + self._feed(s) + else: + self.in_stderr_buffer.feed(s) + + def _window_adjust(self, m): + nbytes = m.get_int() + self.lock.acquire() + try: + if self.ultra_debug: + self._log(DEBUG, 'window up %d' % nbytes) + self.out_window_size += nbytes + self.out_buffer_cv.notifyAll() + finally: + self.lock.release() + + def _handle_request(self, m): + key = m.get_text() + want_reply = m.get_boolean() + server = self.transport.server_object + ok = False + if key == 'exit-status': + self.exit_status = m.get_int() + self.status_event.set() + ok = True + elif key == 'xon-xoff': + # ignore + ok = True + elif key == 'pty-req': + term = m.get_string() + width = m.get_int() + height = m.get_int() + pixelwidth = m.get_int() + pixelheight = m.get_int() + modes = m.get_string() + if server is None: + ok = False + else: + ok = server.check_channel_pty_request(self, term, width, height, pixelwidth, + pixelheight, modes) + elif key == 'shell': + if server is None: + ok = False + else: + ok = server.check_channel_shell_request(self) + elif key == 'env': + name = m.get_string() + value = m.get_string() + if server is None: + ok = False + else: + ok = server.check_channel_env_request(self, name, value) + elif key == 'exec': + cmd = m.get_text() + if server is None: + ok = False + else: + ok = server.check_channel_exec_request(self, cmd) + elif key == 'subsystem': + name = m.get_text() + if server is None: + ok = False + else: + ok = server.check_channel_subsystem_request(self, name) + elif key == 'window-change': + width = m.get_int() + height = m.get_int() + pixelwidth = m.get_int() + pixelheight = m.get_int() + if server is None: + ok = False + else: + ok = server.check_channel_window_change_request(self, width, height, pixelwidth, + pixelheight) + elif key == 'x11-req': + single_connection = m.get_boolean() + auth_proto = m.get_text() + auth_cookie = m.get_binary() + screen_number = m.get_int() + if server is None: + ok = False + else: + ok = server.check_channel_x11_request(self, single_connection, + auth_proto, auth_cookie, screen_number) + elif key == 'auth-agent-req@openssh.com': + if server is None: + ok = False + else: + ok = server.check_channel_forward_agent_request(self) + else: + self._log(DEBUG, 'Unhandled channel request "%s"' % key) + ok = False + if want_reply: + m = Message() + if ok: + m.add_byte(cMSG_CHANNEL_SUCCESS) + else: + m.add_byte(cMSG_CHANNEL_FAILURE) + m.add_int(self.remote_chanid) + self.transport._send_user_message(m) + + def _handle_eof(self, m): + self.lock.acquire() + try: + if not self.eof_received: + self.eof_received = True + self.in_buffer.close() + self.in_stderr_buffer.close() + if self._pipe is not None: + self._pipe.set_forever() + finally: + self.lock.release() + self._log(DEBUG, 'EOF received (%s)', self._name) + + def _handle_close(self, m): + self.lock.acquire() + try: + msgs = self._close_internal() + self.transport._unlink_channel(self.chanid) + finally: + self.lock.release() + for m in msgs: + if m is not None: + self.transport._send_user_message(m) + + ### internals... + + def _log(self, level, msg, *args): + self.logger.log(level, "[chan " + self._name + "] " + msg, *args) + + def _event_pending(self): + self.event.clear() + self.event_ready = False + + def _wait_for_event(self): + self.event.wait() + assert self.event.isSet() + if self.event_ready: + return + e = self.transport.get_exception() + if e is None: + e = SSHException('Channel closed.') + raise e + + def _set_closed(self): + # you are holding the lock. + self.closed = True + self.in_buffer.close() + self.in_stderr_buffer.close() + self.out_buffer_cv.notifyAll() + # Notify any waiters that we are closed + self.event.set() + self.status_event.set() + if self._pipe is not None: + self._pipe.set_forever() + + def _send_eof(self): + # you are holding the lock. + if self.eof_sent: + return None + m = Message() + m.add_byte(cMSG_CHANNEL_EOF) + m.add_int(self.remote_chanid) + self.eof_sent = True + self._log(DEBUG, 'EOF sent (%s)', self._name) + return m + + def _close_internal(self): + # you are holding the lock. + if not self.active or self.closed: + return None, None + m1 = self._send_eof() + m2 = Message() + m2.add_byte(cMSG_CHANNEL_CLOSE) + m2.add_int(self.remote_chanid) + self._set_closed() + # can't unlink from the Transport yet -- the remote side may still + # try to send meta-data (exit-status, etc) + return m1, m2 + + def _unlink(self): + # server connection could die before we become active: still signal the close! + if self.closed: + return + self.lock.acquire() + try: + self._set_closed() + self.transport._unlink_channel(self.chanid) + finally: + self.lock.release() + + def _check_add_window(self, n): + self.lock.acquire() + try: + if self.closed or self.eof_received or not self.active: + return 0 + if self.ultra_debug: + self._log(DEBUG, 'addwindow %d' % n) + self.in_window_sofar += n + if self.in_window_sofar <= self.in_window_threshold: + return 0 + if self.ultra_debug: + self._log(DEBUG, 'addwindow send %d' % self.in_window_sofar) + out = self.in_window_sofar + self.in_window_sofar = 0 + return out + finally: + self.lock.release() + + def _wait_for_send_window(self, size): + """ + (You are already holding the lock.) + Wait for the send window to open up, and allocate up to ``size`` bytes + for transmission. If no space opens up before the timeout, a timeout + exception is raised. Returns the number of bytes available to send + (may be less than requested). + """ + # you are already holding the lock + if self.closed or self.eof_sent: + return 0 + if self.out_window_size == 0: + # should we block? + if self.timeout == 0.0: + raise socket.timeout() + # loop here in case we get woken up but a different thread has filled the buffer + timeout = self.timeout + while self.out_window_size == 0: + if self.closed or self.eof_sent: + return 0 + then = time.time() + self.out_buffer_cv.wait(timeout) + if timeout is not None: + timeout -= time.time() - then + if timeout <= 0.0: + raise socket.timeout() + # we have some window to squeeze into + if self.closed or self.eof_sent: + return 0 + if self.out_window_size < size: + size = self.out_window_size + if self.out_max_packet_size - 64 < size: + size = self.out_max_packet_size - 64 + self.out_window_size -= size + if self.ultra_debug: + self._log(DEBUG, 'window down to %d' % self.out_window_size) + return size + + +class ChannelFile (BufferedFile): + """ + A file-like wrapper around `.Channel`. A ChannelFile is created by calling + `Channel.makefile`. + + .. warning:: + To correctly emulate the file object created from a socket's `makefile + ` method, a `.Channel` and its + `.ChannelFile` should be able to be closed or garbage-collected + independently. Currently, closing the `ChannelFile` does nothing but + flush the buffer. + """ + + def __init__(self, channel, mode='r', bufsize=-1): + self.channel = channel + BufferedFile.__init__(self) + self._set_mode(mode, bufsize) + + def __repr__(self): + """ + Returns a string representation of this object, for debugging. + """ + return '' + + def _read(self, size): + return self.channel.recv(size) + + def _write(self, data): + self.channel.sendall(data) + return len(data) + + +class ChannelStderrFile (ChannelFile): + def __init__(self, channel, mode='r', bufsize=-1): + ChannelFile.__init__(self, channel, mode, bufsize) + + def _read(self, size): + return self.channel.recv_stderr(size) + + def _write(self, data): + self.channel.sendall_stderr(data) + return len(data) + + +# vim: set shiftwidth=4 expandtab : diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/client.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/client.py new file mode 100644 index 0000000..c1bf473 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/client.py @@ -0,0 +1,517 @@ +# Copyright (C) 2006-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +SSH client & key policies +""" + +from binascii import hexlify +import getpass +import os +import socket +import warnings + +from paramiko.agent import Agent +from paramiko.common import DEBUG +from paramiko.config import SSH_PORT +from paramiko.dsskey import DSSKey +from paramiko.hostkeys import HostKeys +from paramiko.py3compat import string_types +from paramiko.resource import ResourceManager +from paramiko.rsakey import RSAKey +from paramiko.ssh_exception import SSHException, BadHostKeyException +from paramiko.transport import Transport +from paramiko.util import retry_on_signal + + +class SSHClient (object): + """ + A high-level representation of a session with an SSH server. This class + wraps `.Transport`, `.Channel`, and `.SFTPClient` to take care of most + aspects of authenticating and opening channels. A typical use case is:: + + client = SSHClient() + client.load_system_host_keys() + client.connect('ssh.example.com') + stdin, stdout, stderr = client.exec_command('ls -l') + + You may pass in explicit overrides for authentication and server host key + checking. The default mechanism is to try to use local key files or an + SSH agent (if one is running). + + .. versionadded:: 1.6 + """ + + def __init__(self): + """ + Create a new SSHClient. + """ + self._system_host_keys = HostKeys() + self._host_keys = HostKeys() + self._host_keys_filename = None + self._log_channel = None + self._policy = RejectPolicy() + self._transport = None + self._agent = None + + def load_system_host_keys(self, filename=None): + """ + Load host keys from a system (read-only) file. Host keys read with + this method will not be saved back by `save_host_keys`. + + This method can be called multiple times. Each new set of host keys + will be merged with the existing set (new replacing old if there are + conflicts). + + If ``filename`` is left as ``None``, an attempt will be made to read + keys from the user's local "known hosts" file, as used by OpenSSH, + and no exception will be raised if the file can't be read. This is + probably only useful on posix. + + :param str filename: the filename to read, or ``None`` + + :raises IOError: + if a filename was provided and the file could not be read + """ + if filename is None: + # try the user's .ssh key file, and mask exceptions + filename = os.path.expanduser('~/.ssh/known_hosts') + try: + self._system_host_keys.load(filename) + except IOError: + pass + return + self._system_host_keys.load(filename) + + def load_host_keys(self, filename): + """ + Load host keys from a local host-key file. Host keys read with this + method will be checked after keys loaded via `load_system_host_keys`, + but will be saved back by `save_host_keys` (so they can be modified). + The missing host key policy `.AutoAddPolicy` adds keys to this set and + saves them, when connecting to a previously-unknown server. + + This method can be called multiple times. Each new set of host keys + will be merged with the existing set (new replacing old if there are + conflicts). When automatically saving, the last hostname is used. + + :param str filename: the filename to read + + :raises IOError: if the filename could not be read + """ + self._host_keys_filename = filename + self._host_keys.load(filename) + + def save_host_keys(self, filename): + """ + Save the host keys back to a file. Only the host keys loaded with + `load_host_keys` (plus any added directly) will be saved -- not any + host keys loaded with `load_system_host_keys`. + + :param str filename: the filename to save to + + :raises IOError: if the file could not be written + """ + + # update local host keys from file (in case other SSH clients + # have written to the known_hosts file meanwhile. + if self._host_keys_filename is not None: + self.load_host_keys(self._host_keys_filename) + + with open(filename, 'w') as f: + for hostname, keys in self._host_keys.items(): + for keytype, key in keys.items(): + f.write('%s %s %s\n' % (hostname, keytype, key.get_base64())) + + def get_host_keys(self): + """ + Get the local `.HostKeys` object. This can be used to examine the + local host keys or change them. + + :return: the local host keys as a `.HostKeys` object. + """ + return self._host_keys + + def set_log_channel(self, name): + """ + Set the channel for logging. The default is ``"paramiko.transport"`` + but it can be set to anything you want. + + :param str name: new channel name for logging + """ + self._log_channel = name + + def set_missing_host_key_policy(self, policy): + """ + Set the policy to use when connecting to a server that doesn't have a + host key in either the system or local `.HostKeys` objects. The + default policy is to reject all unknown servers (using `.RejectPolicy`). + You may substitute `.AutoAddPolicy` or write your own policy class. + + :param .MissingHostKeyPolicy policy: + the policy to use when receiving a host key from a + previously-unknown server + """ + self._policy = policy + + def connect(self, hostname, port=SSH_PORT, username=None, password=None, pkey=None, + key_filename=None, timeout=None, allow_agent=True, look_for_keys=True, + compress=False, sock=None): + """ + Connect to an SSH server and authenticate to it. The server's host key + is checked against the system host keys (see `load_system_host_keys`) + and any local host keys (`load_host_keys`). If the server's hostname + is not found in either set of host keys, the missing host key policy + is used (see `set_missing_host_key_policy`). The default policy is + to reject the key and raise an `.SSHException`. + + Authentication is attempted in the following order of priority: + + - The ``pkey`` or ``key_filename`` passed in (if any) + - Any key we can find through an SSH agent + - Any "id_rsa" or "id_dsa" key discoverable in ``~/.ssh/`` + - Plain username/password auth, if a password was given + + If a private key requires a password to unlock it, and a password is + passed in, that password will be used to attempt to unlock the key. + + :param str hostname: the server to connect to + :param int port: the server port to connect to + :param str username: + the username to authenticate as (defaults to the current local + username) + :param str password: + a password to use for authentication or for unlocking a private key + :param .PKey pkey: an optional private key to use for authentication + :param str key_filename: + the filename, or list of filenames, of optional private key(s) to + try for authentication + :param float timeout: an optional timeout (in seconds) for the TCP connect + :param bool allow_agent: set to False to disable connecting to the SSH agent + :param bool look_for_keys: + set to False to disable searching for discoverable private key + files in ``~/.ssh/`` + :param bool compress: set to True to turn on compression + :param socket sock: + an open socket or socket-like object (such as a `.Channel`) to use + for communication to the target host + + :raises BadHostKeyException: if the server's host key could not be + verified + :raises AuthenticationException: if authentication failed + :raises SSHException: if there was any other error connecting or + establishing an SSH session + :raises socket.error: if a socket error occurred while connecting + """ + if not sock: + for (family, socktype, proto, canonname, sockaddr) in socket.getaddrinfo(hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM): + if socktype == socket.SOCK_STREAM: + af = family + addr = sockaddr + break + else: + # some OS like AIX don't indicate SOCK_STREAM support, so just guess. :( + af, _, _, _, addr = socket.getaddrinfo(hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM) + sock = socket.socket(af, socket.SOCK_STREAM) + if timeout is not None: + try: + sock.settimeout(timeout) + except: + pass + retry_on_signal(lambda: sock.connect(addr)) + + t = self._transport = Transport(sock) + t.use_compression(compress=compress) + if self._log_channel is not None: + t.set_log_channel(self._log_channel) + t.start_client() + ResourceManager.register(self, t) + + server_key = t.get_remote_server_key() + keytype = server_key.get_name() + + if port == SSH_PORT: + server_hostkey_name = hostname + else: + server_hostkey_name = "[%s]:%d" % (hostname, port) + our_server_key = self._system_host_keys.get(server_hostkey_name, {}).get(keytype, None) + if our_server_key is None: + our_server_key = self._host_keys.get(server_hostkey_name, {}).get(keytype, None) + if our_server_key is None: + # will raise exception if the key is rejected; let that fall out + self._policy.missing_host_key(self, server_hostkey_name, server_key) + # if the callback returns, assume the key is ok + our_server_key = server_key + + if server_key != our_server_key: + raise BadHostKeyException(hostname, server_key, our_server_key) + + if username is None: + username = getpass.getuser() + + if key_filename is None: + key_filenames = [] + elif isinstance(key_filename, string_types): + key_filenames = [key_filename] + else: + key_filenames = key_filename + self._auth(username, password, pkey, key_filenames, allow_agent, look_for_keys) + + def close(self): + """ + Close this SSHClient and its underlying `.Transport`. + """ + if self._transport is None: + return + self._transport.close() + self._transport = None + + if self._agent is not None: + self._agent.close() + self._agent = None + + def exec_command(self, command, bufsize=-1, timeout=None, get_pty=False): + """ + Execute a command on the SSH server. A new `.Channel` is opened and + the requested command is executed. The command's input and output + streams are returned as Python ``file``-like objects representing + stdin, stdout, and stderr. + + :param str command: the command to execute + :param int bufsize: + interpreted the same way as by the built-in ``file()`` function in + Python + :param int timeout: + set command's channel timeout. See `Channel.settimeout`.settimeout + :return: + the stdin, stdout, and stderr of the executing command, as a + 3-tuple + + :raises SSHException: if the server fails to execute the command + """ + chan = self._transport.open_session() + if get_pty: + chan.get_pty() + chan.settimeout(timeout) + chan.exec_command(command) + stdin = chan.makefile('wb', bufsize) + stdout = chan.makefile('r', bufsize) + stderr = chan.makefile_stderr('r', bufsize) + return stdin, stdout, stderr + + def invoke_shell(self, term='vt100', width=80, height=24, width_pixels=0, + height_pixels=0): + """ + Start an interactive shell session on the SSH server. A new `.Channel` + is opened and connected to a pseudo-terminal using the requested + terminal type and size. + + :param str term: + the terminal type to emulate (for example, ``"vt100"``) + :param int width: the width (in characters) of the terminal window + :param int height: the height (in characters) of the terminal window + :param int width_pixels: the width (in pixels) of the terminal window + :param int height_pixels: the height (in pixels) of the terminal window + :return: a new `.Channel` connected to the remote shell + + :raises SSHException: if the server fails to invoke a shell + """ + chan = self._transport.open_session() + chan.get_pty(term, width, height, width_pixels, height_pixels) + chan.invoke_shell() + return chan + + def open_sftp(self): + """ + Open an SFTP session on the SSH server. + + :return: a new `.SFTPClient` session object + """ + return self._transport.open_sftp_client() + + def get_transport(self): + """ + Return the underlying `.Transport` object for this SSH connection. + This can be used to perform lower-level tasks, like opening specific + kinds of channels. + + :return: the `.Transport` for this connection + """ + return self._transport + + def _auth(self, username, password, pkey, key_filenames, allow_agent, look_for_keys): + """ + Try, in order: + + - The key passed in, if one was passed in. + - Any key we can find through an SSH agent (if allowed). + - Any "id_rsa" or "id_dsa" key discoverable in ~/.ssh/ (if allowed). + - Plain username/password auth, if a password was given. + + (The password might be needed to unlock a private key, or for + two-factor authentication [for which it is required].) + """ + saved_exception = None + two_factor = False + allowed_types = [] + + if pkey is not None: + try: + self._log(DEBUG, 'Trying SSH key %s' % hexlify(pkey.get_fingerprint())) + allowed_types = self._transport.auth_publickey(username, pkey) + two_factor = (allowed_types == ['password']) + if not two_factor: + return + except SSHException as e: + saved_exception = e + + if not two_factor: + for key_filename in key_filenames: + for pkey_class in (RSAKey, DSSKey): + try: + key = pkey_class.from_private_key_file(key_filename, password) + self._log(DEBUG, 'Trying key %s from %s' % (hexlify(key.get_fingerprint()), key_filename)) + self._transport.auth_publickey(username, key) + two_factor = (allowed_types == ['password']) + if not two_factor: + return + break + except SSHException as e: + saved_exception = e + + if not two_factor and allow_agent: + if self._agent is None: + self._agent = Agent() + + for key in self._agent.get_keys(): + try: + self._log(DEBUG, 'Trying SSH agent key %s' % hexlify(key.get_fingerprint())) + # for 2-factor auth a successfully auth'd key will result in ['password'] + allowed_types = self._transport.auth_publickey(username, key) + two_factor = (allowed_types == ['password']) + if not two_factor: + return + break + except SSHException as e: + saved_exception = e + + if not two_factor: + keyfiles = [] + rsa_key = os.path.expanduser('~/.ssh/id_rsa') + dsa_key = os.path.expanduser('~/.ssh/id_dsa') + if os.path.isfile(rsa_key): + keyfiles.append((RSAKey, rsa_key)) + if os.path.isfile(dsa_key): + keyfiles.append((DSSKey, dsa_key)) + # look in ~/ssh/ for windows users: + rsa_key = os.path.expanduser('~/ssh/id_rsa') + dsa_key = os.path.expanduser('~/ssh/id_dsa') + if os.path.isfile(rsa_key): + keyfiles.append((RSAKey, rsa_key)) + if os.path.isfile(dsa_key): + keyfiles.append((DSSKey, dsa_key)) + + if not look_for_keys: + keyfiles = [] + + for pkey_class, filename in keyfiles: + try: + key = pkey_class.from_private_key_file(filename, password) + self._log(DEBUG, 'Trying discovered key %s in %s' % (hexlify(key.get_fingerprint()), filename)) + # for 2-factor auth a successfully auth'd key will result in ['password'] + allowed_types = self._transport.auth_publickey(username, key) + two_factor = (allowed_types == ['password']) + if not two_factor: + return + break + except (SSHException, IOError) as e: + saved_exception = e + + if password is not None: + try: + self._transport.auth_password(username, password) + return + except SSHException as e: + saved_exception = e + elif two_factor: + raise SSHException('Two-factor authentication requires a password') + + # if we got an auth-failed exception earlier, re-raise it + if saved_exception is not None: + raise saved_exception + raise SSHException('No authentication methods available') + + def _log(self, level, msg): + self._transport._log(level, msg) + + +class MissingHostKeyPolicy (object): + """ + Interface for defining the policy that `.SSHClient` should use when the + SSH server's hostname is not in either the system host keys or the + application's keys. Pre-made classes implement policies for automatically + adding the key to the application's `.HostKeys` object (`.AutoAddPolicy`), + and for automatically rejecting the key (`.RejectPolicy`). + + This function may be used to ask the user to verify the key, for example. + """ + + def missing_host_key(self, client, hostname, key): + """ + Called when an `.SSHClient` receives a server key for a server that + isn't in either the system or local `.HostKeys` object. To accept + the key, simply return. To reject, raised an exception (which will + be passed to the calling application). + """ + pass + + +class AutoAddPolicy (MissingHostKeyPolicy): + """ + Policy for automatically adding the hostname and new host key to the + local `.HostKeys` object, and saving it. This is used by `.SSHClient`. + """ + + def missing_host_key(self, client, hostname, key): + client._host_keys.add(hostname, key.get_name(), key) + if client._host_keys_filename is not None: + client.save_host_keys(client._host_keys_filename) + client._log(DEBUG, 'Adding %s host key for %s: %s' % + (key.get_name(), hostname, hexlify(key.get_fingerprint()))) + + +class RejectPolicy (MissingHostKeyPolicy): + """ + Policy for automatically rejecting the unknown hostname & key. This is + used by `.SSHClient`. + """ + + def missing_host_key(self, client, hostname, key): + client._log(DEBUG, 'Rejecting %s host key for %s: %s' % + (key.get_name(), hostname, hexlify(key.get_fingerprint()))) + raise SSHException('Server %r not found in known_hosts' % hostname) + + +class WarningPolicy (MissingHostKeyPolicy): + """ + Policy for logging a Python-style warning for an unknown host key, but + accepting it. This is used by `.SSHClient`. + """ + def missing_host_key(self, client, hostname, key): + warnings.warn('Unknown %s host key for %s: %s' % + (key.get_name(), hostname, hexlify(key.get_fingerprint()))) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/common.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/common.py new file mode 100644 index 0000000..1829892 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/common.py @@ -0,0 +1,173 @@ +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +Common constants and global variables. +""" +import logging +from paramiko.py3compat import byte_chr, PY2, bytes_types, string_types, b, long + +MSG_DISCONNECT, MSG_IGNORE, MSG_UNIMPLEMENTED, MSG_DEBUG, MSG_SERVICE_REQUEST, \ + MSG_SERVICE_ACCEPT = range(1, 7) +MSG_KEXINIT, MSG_NEWKEYS = range(20, 22) +MSG_USERAUTH_REQUEST, MSG_USERAUTH_FAILURE, MSG_USERAUTH_SUCCESS, \ + MSG_USERAUTH_BANNER = range(50, 54) +MSG_USERAUTH_PK_OK = 60 +MSG_USERAUTH_INFO_REQUEST, MSG_USERAUTH_INFO_RESPONSE = range(60, 62) +MSG_GLOBAL_REQUEST, MSG_REQUEST_SUCCESS, MSG_REQUEST_FAILURE = range(80, 83) +MSG_CHANNEL_OPEN, MSG_CHANNEL_OPEN_SUCCESS, MSG_CHANNEL_OPEN_FAILURE, \ + MSG_CHANNEL_WINDOW_ADJUST, MSG_CHANNEL_DATA, MSG_CHANNEL_EXTENDED_DATA, \ + MSG_CHANNEL_EOF, MSG_CHANNEL_CLOSE, MSG_CHANNEL_REQUEST, \ + MSG_CHANNEL_SUCCESS, MSG_CHANNEL_FAILURE = range(90, 101) + +cMSG_DISCONNECT = byte_chr(MSG_DISCONNECT) +cMSG_IGNORE = byte_chr(MSG_IGNORE) +cMSG_UNIMPLEMENTED = byte_chr(MSG_UNIMPLEMENTED) +cMSG_DEBUG = byte_chr(MSG_DEBUG) +cMSG_SERVICE_REQUEST = byte_chr(MSG_SERVICE_REQUEST) +cMSG_SERVICE_ACCEPT = byte_chr(MSG_SERVICE_ACCEPT) +cMSG_KEXINIT = byte_chr(MSG_KEXINIT) +cMSG_NEWKEYS = byte_chr(MSG_NEWKEYS) +cMSG_USERAUTH_REQUEST = byte_chr(MSG_USERAUTH_REQUEST) +cMSG_USERAUTH_FAILURE = byte_chr(MSG_USERAUTH_FAILURE) +cMSG_USERAUTH_SUCCESS = byte_chr(MSG_USERAUTH_SUCCESS) +cMSG_USERAUTH_BANNER = byte_chr(MSG_USERAUTH_BANNER) +cMSG_USERAUTH_PK_OK = byte_chr(MSG_USERAUTH_PK_OK) +cMSG_USERAUTH_INFO_REQUEST = byte_chr(MSG_USERAUTH_INFO_REQUEST) +cMSG_USERAUTH_INFO_RESPONSE = byte_chr(MSG_USERAUTH_INFO_RESPONSE) +cMSG_GLOBAL_REQUEST = byte_chr(MSG_GLOBAL_REQUEST) +cMSG_REQUEST_SUCCESS = byte_chr(MSG_REQUEST_SUCCESS) +cMSG_REQUEST_FAILURE = byte_chr(MSG_REQUEST_FAILURE) +cMSG_CHANNEL_OPEN = byte_chr(MSG_CHANNEL_OPEN) +cMSG_CHANNEL_OPEN_SUCCESS = byte_chr(MSG_CHANNEL_OPEN_SUCCESS) +cMSG_CHANNEL_OPEN_FAILURE = byte_chr(MSG_CHANNEL_OPEN_FAILURE) +cMSG_CHANNEL_WINDOW_ADJUST = byte_chr(MSG_CHANNEL_WINDOW_ADJUST) +cMSG_CHANNEL_DATA = byte_chr(MSG_CHANNEL_DATA) +cMSG_CHANNEL_EXTENDED_DATA = byte_chr(MSG_CHANNEL_EXTENDED_DATA) +cMSG_CHANNEL_EOF = byte_chr(MSG_CHANNEL_EOF) +cMSG_CHANNEL_CLOSE = byte_chr(MSG_CHANNEL_CLOSE) +cMSG_CHANNEL_REQUEST = byte_chr(MSG_CHANNEL_REQUEST) +cMSG_CHANNEL_SUCCESS = byte_chr(MSG_CHANNEL_SUCCESS) +cMSG_CHANNEL_FAILURE = byte_chr(MSG_CHANNEL_FAILURE) + +# for debugging: +MSG_NAMES = { + MSG_DISCONNECT: 'disconnect', + MSG_IGNORE: 'ignore', + MSG_UNIMPLEMENTED: 'unimplemented', + MSG_DEBUG: 'debug', + MSG_SERVICE_REQUEST: 'service-request', + MSG_SERVICE_ACCEPT: 'service-accept', + MSG_KEXINIT: 'kexinit', + MSG_NEWKEYS: 'newkeys', + 30: 'kex30', + 31: 'kex31', + 32: 'kex32', + 33: 'kex33', + 34: 'kex34', + MSG_USERAUTH_REQUEST: 'userauth-request', + MSG_USERAUTH_FAILURE: 'userauth-failure', + MSG_USERAUTH_SUCCESS: 'userauth-success', + MSG_USERAUTH_BANNER: 'userauth--banner', + MSG_USERAUTH_PK_OK: 'userauth-60(pk-ok/info-request)', + MSG_USERAUTH_INFO_RESPONSE: 'userauth-info-response', + MSG_GLOBAL_REQUEST: 'global-request', + MSG_REQUEST_SUCCESS: 'request-success', + MSG_REQUEST_FAILURE: 'request-failure', + MSG_CHANNEL_OPEN: 'channel-open', + MSG_CHANNEL_OPEN_SUCCESS: 'channel-open-success', + MSG_CHANNEL_OPEN_FAILURE: 'channel-open-failure', + MSG_CHANNEL_WINDOW_ADJUST: 'channel-window-adjust', + MSG_CHANNEL_DATA: 'channel-data', + MSG_CHANNEL_EXTENDED_DATA: 'channel-extended-data', + MSG_CHANNEL_EOF: 'channel-eof', + MSG_CHANNEL_CLOSE: 'channel-close', + MSG_CHANNEL_REQUEST: 'channel-request', + MSG_CHANNEL_SUCCESS: 'channel-success', + MSG_CHANNEL_FAILURE: 'channel-failure' +} + + +# authentication request return codes: +AUTH_SUCCESSFUL, AUTH_PARTIALLY_SUCCESSFUL, AUTH_FAILED = range(3) + + +# channel request failed reasons: +(OPEN_SUCCEEDED, + OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED, + OPEN_FAILED_CONNECT_FAILED, + OPEN_FAILED_UNKNOWN_CHANNEL_TYPE, + OPEN_FAILED_RESOURCE_SHORTAGE) = range(0, 5) + + +CONNECTION_FAILED_CODE = { + 1: 'Administratively prohibited', + 2: 'Connect failed', + 3: 'Unknown channel type', + 4: 'Resource shortage' +} + + +DISCONNECT_SERVICE_NOT_AVAILABLE, DISCONNECT_AUTH_CANCELLED_BY_USER, \ + DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 7, 13, 14 + +zero_byte = byte_chr(0) +one_byte = byte_chr(1) +four_byte = byte_chr(4) +max_byte = byte_chr(0xff) +cr_byte = byte_chr(13) +linefeed_byte = byte_chr(10) +crlf = cr_byte + linefeed_byte + +if PY2: + cr_byte_value = cr_byte + linefeed_byte_value = linefeed_byte +else: + cr_byte_value = 13 + linefeed_byte_value = 10 + + +def asbytes(s): + if not isinstance(s, bytes_types): + if isinstance(s, string_types): + s = b(s) + else: + try: + s = s.asbytes() + except Exception: + raise Exception('Unknown type') + return s + +xffffffff = long(0xffffffff) +x80000000 = long(0x80000000) +o666 = 438 +o660 = 432 +o644 = 420 +o600 = 384 +o777 = 511 +o700 = 448 +o70 = 56 + +DEBUG = logging.DEBUG +INFO = logging.INFO +WARNING = logging.WARNING +ERROR = logging.ERROR +CRITICAL = logging.CRITICAL + +# Common IO/select/etc sleep period, in seconds +io_sleep = 0.01 diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/compress.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/compress.py new file mode 100644 index 0000000..b55f0b1 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/compress.py @@ -0,0 +1,39 @@ +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +Compression implementations for a Transport. +""" + +import zlib + + +class ZlibCompressor (object): + def __init__(self): + self.z = zlib.compressobj(9) + + def __call__(self, data): + return self.z.compress(data) + self.z.flush(zlib.Z_FULL_FLUSH) + + +class ZlibDecompressor (object): + def __init__(self): + self.z = zlib.decompressobj() + + def __call__(self, data): + return self.z.decompress(data) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/config.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/config.py new file mode 100644 index 0000000..77fa13d --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/config.py @@ -0,0 +1,261 @@ +# Copyright (C) 2006-2007 Robey Pointer +# Copyright (C) 2012 Olle Lundberg +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +Configuration file (aka ``ssh_config``) support. +""" + +import fnmatch +import os +import re +import socket + +SSH_PORT = 22 +proxy_re = re.compile(r"^(proxycommand)\s*=*\s*(.*)", re.I) + + +class SSHConfig (object): + """ + Representation of config information as stored in the format used by + OpenSSH. Queries can be made via `lookup`. The format is described in + OpenSSH's ``ssh_config`` man page. This class is provided primarily as a + convenience to posix users (since the OpenSSH format is a de-facto + standard on posix) but should work fine on Windows too. + + .. versionadded:: 1.6 + """ + + def __init__(self): + """ + Create a new OpenSSH config object. + """ + self._config = [] + + def parse(self, file_obj): + """ + Read an OpenSSH config from the given file object. + + :param file file_obj: a file-like object to read the config file from + """ + host = {"host": ['*'], "config": {}} + for line in file_obj: + line = line.rstrip('\n').lstrip() + if (line == '') or (line[0] == '#'): + continue + if '=' in line: + # Ensure ProxyCommand gets properly split + if line.lower().strip().startswith('proxycommand'): + match = proxy_re.match(line) + key, value = match.group(1).lower(), match.group(2) + else: + key, value = line.split('=', 1) + key = key.strip().lower() + else: + # find first whitespace, and split there + i = 0 + while (i < len(line)) and not line[i].isspace(): + i += 1 + if i == len(line): + raise Exception('Unparsable line: %r' % line) + key = line[:i].lower() + value = line[i:].lstrip() + + if key == 'host': + self._config.append(host) + value = value.split() + host = {key: value, 'config': {}} + #identityfile, localforward, remoteforward keys are special cases, since they are allowed to be + # specified multiple times and they should be tried in order + # of specification. + + elif key in ['identityfile', 'localforward', 'remoteforward']: + if key in host['config']: + host['config'][key].append(value) + else: + host['config'][key] = [value] + elif key not in host['config']: + host['config'].update({key: value}) + self._config.append(host) + + def lookup(self, hostname): + """ + Return a dict of config options for a given hostname. + + The host-matching rules of OpenSSH's ``ssh_config`` man page are used, + which means that all configuration options from matching host + specifications are merged, with more specific hostmasks taking + precedence. In other words, if ``"Port"`` is set under ``"Host *"`` + and also ``"Host *.example.com"``, and the lookup is for + ``"ssh.example.com"``, then the port entry for ``"Host *.example.com"`` + will win out. + + The keys in the returned dict are all normalized to lowercase (look for + ``"port"``, not ``"Port"``. The values are processed according to the + rules for substitution variable expansion in ``ssh_config``. + + :param str hostname: the hostname to lookup + """ + matches = [config for config in self._config if + self._allowed(hostname, config['host'])] + + ret = {} + for match in matches: + for key, value in match['config'].items(): + if key not in ret: + # Create a copy of the original value, + # else it will reference the original list + # in self._config and update that value too + # when the extend() is being called. + ret[key] = value[:] + elif key == 'identityfile': + ret[key].extend(value) + ret = self._expand_variables(ret, hostname) + return ret + + def _allowed(self, hostname, hosts): + match = False + for host in hosts: + if host.startswith('!') and fnmatch.fnmatch(hostname, host[1:]): + return False + elif fnmatch.fnmatch(hostname, host): + match = True + return match + + def _expand_variables(self, config, hostname): + """ + Return a dict of config options with expanded substitutions + for a given hostname. + + Please refer to man ``ssh_config`` for the parameters that + are replaced. + + :param dict config: the config for the hostname + :param str hostname: the hostname that the config belongs to + """ + + if 'hostname' in config: + config['hostname'] = config['hostname'].replace('%h', hostname) + else: + config['hostname'] = hostname + + if 'port' in config: + port = config['port'] + else: + port = SSH_PORT + + user = os.getenv('USER') + if 'user' in config: + remoteuser = config['user'] + else: + remoteuser = user + + host = socket.gethostname().split('.')[0] + fqdn = LazyFqdn(config, host) + homedir = os.path.expanduser('~') + replacements = {'controlpath': + [ + ('%h', config['hostname']), + ('%l', fqdn), + ('%L', host), + ('%n', hostname), + ('%p', port), + ('%r', remoteuser), + ('%u', user) + ], + 'identityfile': + [ + ('~', homedir), + ('%d', homedir), + ('%h', config['hostname']), + ('%l', fqdn), + ('%u', user), + ('%r', remoteuser) + ], + 'proxycommand': + [ + ('%h', config['hostname']), + ('%p', port), + ('%r', remoteuser) + ] + } + + for k in config: + if k in replacements: + for find, replace in replacements[k]: + if isinstance(config[k], list): + for item in range(len(config[k])): + config[k][item] = config[k][item].\ + replace(find, str(replace)) + else: + config[k] = config[k].replace(find, str(replace)) + return config + + +class LazyFqdn(object): + """ + Returns the host's fqdn on request as string. + """ + + def __init__(self, config, host=None): + self.fqdn = None + self.config = config + self.host = host + + def __str__(self): + if self.fqdn is None: + # + # If the SSH config contains AddressFamily, use that when + # determining the local host's FQDN. Using socket.getfqdn() from + # the standard library is the most general solution, but can + # result in noticeable delays on some platforms when IPv6 is + # misconfigured or not available, as it calls getaddrinfo with no + # address family specified, so both IPv4 and IPv6 are checked. + # + + # Handle specific option + fqdn = None + address_family = self.config.get('addressfamily', 'any').lower() + if address_family != 'any': + try: + family = socket.AF_INET if address_family == 'inet' \ + else socket.AF_INET6 + results = socket.getaddrinfo( + self.host, + None, + family, + socket.SOCK_DGRAM, + socket.IPPROTO_IP, + socket.AI_CANONNAME + ) + for res in results: + af, socktype, proto, canonname, sa = res + if canonname and '.' in canonname: + fqdn = canonname + break + # giaerror -> socket.getaddrinfo() can't resolve self.host + # (which is from socket.gethostname()). Fall back to the + # getfqdn() call below. + except socket.gaierror: + pass + # Handle 'any' / unspecified + if fqdn is None: + fqdn = socket.getfqdn() + # Cache + self.fqdn = fqdn + return self.fqdn diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/dsskey.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/dsskey.py new file mode 100644 index 0000000..1901596 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/dsskey.py @@ -0,0 +1,198 @@ +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +DSS keys. +""" + +import os +from hashlib import sha1 + +from Crypto.PublicKey import DSA + +from paramiko import util +from paramiko.common import zero_byte +from paramiko.py3compat import long +from paramiko.ssh_exception import SSHException +from paramiko.message import Message +from paramiko.ber import BER, BERException +from paramiko.pkey import PKey + + +class DSSKey (PKey): + """ + Representation of a DSS key which can be used to sign an verify SSH2 + data. + """ + + def __init__(self, msg=None, data=None, filename=None, password=None, vals=None, file_obj=None): + self.p = None + self.q = None + self.g = None + self.y = None + self.x = None + if file_obj is not None: + self._from_private_key(file_obj, password) + return + if filename is not None: + self._from_private_key_file(filename, password) + return + if (msg is None) and (data is not None): + msg = Message(data) + if vals is not None: + self.p, self.q, self.g, self.y = vals + else: + if msg is None: + raise SSHException('Key object may not be empty') + if msg.get_text() != 'ssh-dss': + raise SSHException('Invalid key') + self.p = msg.get_mpint() + self.q = msg.get_mpint() + self.g = msg.get_mpint() + self.y = msg.get_mpint() + self.size = util.bit_length(self.p) + + def asbytes(self): + m = Message() + m.add_string('ssh-dss') + m.add_mpint(self.p) + m.add_mpint(self.q) + m.add_mpint(self.g) + m.add_mpint(self.y) + return m.asbytes() + + def __str__(self): + return self.asbytes() + + def __hash__(self): + h = hash(self.get_name()) + h = h * 37 + hash(self.p) + h = h * 37 + hash(self.q) + h = h * 37 + hash(self.g) + h = h * 37 + hash(self.y) + # h might be a long by now... + return hash(h) + + def get_name(self): + return 'ssh-dss' + + def get_bits(self): + return self.size + + def can_sign(self): + return self.x is not None + + def sign_ssh_data(self, data): + digest = sha1(data).digest() + dss = DSA.construct((long(self.y), long(self.g), long(self.p), long(self.q), long(self.x))) + # generate a suitable k + qsize = len(util.deflate_long(self.q, 0)) + while True: + k = util.inflate_long(os.urandom(qsize), 1) + if (k > 2) and (k < self.q): + break + r, s = dss.sign(util.inflate_long(digest, 1), k) + m = Message() + m.add_string('ssh-dss') + # apparently, in rare cases, r or s may be shorter than 20 bytes! + rstr = util.deflate_long(r, 0) + sstr = util.deflate_long(s, 0) + if len(rstr) < 20: + rstr = zero_byte * (20 - len(rstr)) + rstr + if len(sstr) < 20: + sstr = zero_byte * (20 - len(sstr)) + sstr + m.add_string(rstr + sstr) + return m + + def verify_ssh_sig(self, data, msg): + if len(msg.asbytes()) == 40: + # spies.com bug: signature has no header + sig = msg.asbytes() + else: + kind = msg.get_text() + if kind != 'ssh-dss': + return 0 + sig = msg.get_binary() + + # pull out (r, s) which are NOT encoded as mpints + sigR = util.inflate_long(sig[:20], 1) + sigS = util.inflate_long(sig[20:], 1) + sigM = util.inflate_long(sha1(data).digest(), 1) + + dss = DSA.construct((long(self.y), long(self.g), long(self.p), long(self.q))) + return dss.verify(sigM, (sigR, sigS)) + + def _encode_key(self): + if self.x is None: + raise SSHException('Not enough key information') + keylist = [0, self.p, self.q, self.g, self.y, self.x] + try: + b = BER() + b.encode(keylist) + except BERException: + raise SSHException('Unable to create ber encoding of key') + return b.asbytes() + + def write_private_key_file(self, filename, password=None): + self._write_private_key_file('DSA', filename, self._encode_key(), password) + + def write_private_key(self, file_obj, password=None): + self._write_private_key('DSA', file_obj, self._encode_key(), password) + + def generate(bits=1024, progress_func=None): + """ + Generate a new private DSS key. This factory function can be used to + generate a new host key or authentication key. + + :param int bits: number of bits the generated key should be. + :param function progress_func: + an optional function to call at key points in key generation (used + by ``pyCrypto.PublicKey``). + :return: new `.DSSKey` private key + """ + dsa = DSA.generate(bits, os.urandom, progress_func) + key = DSSKey(vals=(dsa.p, dsa.q, dsa.g, dsa.y)) + key.x = dsa.x + return key + generate = staticmethod(generate) + + ### internals... + + def _from_private_key_file(self, filename, password): + data = self._read_private_key_file('DSA', filename, password) + self._decode_key(data) + + def _from_private_key(self, file_obj, password): + data = self._read_private_key('DSA', file_obj, password) + self._decode_key(data) + + def _decode_key(self, data): + # private key file contains: + # DSAPrivateKey = { version = 0, p, q, g, y, x } + try: + keylist = BER(data).decode() + except BERException as e: + raise SSHException('Unable to parse key file: ' + str(e)) + if (type(keylist) is not list) or (len(keylist) < 6) or (keylist[0] != 0): + raise SSHException('not a valid DSA private key file (bad ber encoding)') + self.p = keylist[1] + self.q = keylist[2] + self.g = keylist[3] + self.y = keylist[4] + self.x = keylist[5] + self.size = util.bit_length(self.p) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/ecdsakey.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/ecdsakey.py new file mode 100644 index 0000000..6736315 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/ecdsakey.py @@ -0,0 +1,180 @@ +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distrubuted in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +L{ECDSAKey} +""" + +import binascii +from hashlib import sha256 + +from ecdsa import SigningKey, VerifyingKey, der, curves +from ecdsa.test_pyecdsa import ECDSA + +from paramiko.common import four_byte, one_byte +from paramiko.message import Message +from paramiko.pkey import PKey +from paramiko.py3compat import byte_chr, u +from paramiko.ssh_exception import SSHException + + +class ECDSAKey (PKey): + """ + Representation of an ECDSA key which can be used to sign and verify SSH2 + data. + """ + + def __init__(self, msg=None, data=None, filename=None, password=None, vals=None, file_obj=None): + self.verifying_key = None + self.signing_key = None + if file_obj is not None: + self._from_private_key(file_obj, password) + return + if filename is not None: + self._from_private_key_file(filename, password) + return + if (msg is None) and (data is not None): + msg = Message(data) + if vals is not None: + self.verifying_key, self.signing_key = vals + else: + if msg is None: + raise SSHException('Key object may not be empty') + if msg.get_text() != 'ecdsa-sha2-nistp256': + raise SSHException('Invalid key') + curvename = msg.get_text() + if curvename != 'nistp256': + raise SSHException("Can't handle curve of type %s" % curvename) + + pointinfo = msg.get_binary() + if pointinfo[0:1] != four_byte: + raise SSHException('Point compression is being used: %s' % + binascii.hexlify(pointinfo)) + self.verifying_key = VerifyingKey.from_string(pointinfo[1:], + curve=curves.NIST256p) + self.size = 256 + + def asbytes(self): + key = self.verifying_key + m = Message() + m.add_string('ecdsa-sha2-nistp256') + m.add_string('nistp256') + + point_str = four_byte + key.to_string() + + m.add_string(point_str) + return m.asbytes() + + def __str__(self): + return self.asbytes() + + def __hash__(self): + h = hash(self.get_name()) + h = h * 37 + hash(self.verifying_key.pubkey.point.x()) + h = h * 37 + hash(self.verifying_key.pubkey.point.y()) + return hash(h) + + def get_name(self): + return 'ecdsa-sha2-nistp256' + + def get_bits(self): + return self.size + + def can_sign(self): + return self.signing_key is not None + + def sign_ssh_data(self, data): + sig = self.signing_key.sign_deterministic( + data, sigencode=self._sigencode, hashfunc=sha256) + m = Message() + m.add_string('ecdsa-sha2-nistp256') + m.add_string(sig) + return m + + def verify_ssh_sig(self, data, msg): + if msg.get_text() != 'ecdsa-sha2-nistp256': + return False + sig = msg.get_binary() + + # verify the signature by SHA'ing the data and encrypting it + # using the public key. + hash_obj = sha256(data).digest() + return self.verifying_key.verify_digest(sig, hash_obj, + sigdecode=self._sigdecode) + + def write_private_key_file(self, filename, password=None): + key = self.signing_key or self.verifying_key + self._write_private_key_file('EC', filename, key.to_der(), password) + + def write_private_key(self, file_obj, password=None): + key = self.signing_key or self.verifying_key + self._write_private_key('EC', file_obj, key.to_der(), password) + + def generate(bits, progress_func=None): + """ + Generate a new private RSA key. This factory function can be used to + generate a new host key or authentication key. + + @param bits: number of bits the generated key should be. + @type bits: int + @param progress_func: an optional function to call at key points in + key generation (used by C{pyCrypto.PublicKey}). + @type progress_func: function + @return: new private key + @rtype: L{RSAKey} + """ + signing_key = ECDSA.generate() + key = ECDSAKey(vals=(signing_key, signing_key.get_verifying_key())) + return key + generate = staticmethod(generate) + + ### internals... + + def _from_private_key_file(self, filename, password): + data = self._read_private_key_file('EC', filename, password) + self._decode_key(data) + + def _from_private_key(self, file_obj, password): + data = self._read_private_key('EC', file_obj, password) + self._decode_key(data) + + ALLOWED_PADDINGS = [one_byte, byte_chr(2) * 2, byte_chr(3) * 3, byte_chr(4) * 4, + byte_chr(5) * 5, byte_chr(6) * 6, byte_chr(7) * 7] + + def _decode_key(self, data): + s, padding = der.remove_sequence(data) + if padding: + if padding not in self.ALLOWED_PADDINGS: + raise ValueError("weird padding: %s" % u(binascii.hexlify(data))) + data = data[:-len(padding)] + key = SigningKey.from_der(data) + self.signing_key = key + self.verifying_key = key.get_verifying_key() + self.size = 256 + + def _sigencode(self, r, s, order): + msg = Message() + msg.add_mpint(r) + msg.add_mpint(s) + return msg.asbytes() + + def _sigdecode(self, sig, order): + msg = Message(sig) + r = msg.get_mpint() + s = msg.get_mpint() + return r, s diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/file.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/file.py new file mode 100644 index 0000000..2238f0b --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/file.py @@ -0,0 +1,468 @@ +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +from paramiko.common import linefeed_byte_value, crlf, cr_byte, linefeed_byte, \ + cr_byte_value +from paramiko.py3compat import BytesIO, PY2, u, b, bytes_types + + +class BufferedFile (object): + """ + Reusable base class to implement Python-style file buffering around a + simpler stream. + """ + + _DEFAULT_BUFSIZE = 8192 + + SEEK_SET = 0 + SEEK_CUR = 1 + SEEK_END = 2 + + FLAG_READ = 0x1 + FLAG_WRITE = 0x2 + FLAG_APPEND = 0x4 + FLAG_BINARY = 0x10 + FLAG_BUFFERED = 0x20 + FLAG_LINE_BUFFERED = 0x40 + FLAG_UNIVERSAL_NEWLINE = 0x80 + + def __init__(self): + self.newlines = None + self._flags = 0 + self._bufsize = self._DEFAULT_BUFSIZE + self._wbuffer = BytesIO() + self._rbuffer = bytes() + self._at_trailing_cr = False + self._closed = False + # pos - position within the file, according to the user + # realpos - position according the OS + # (these may be different because we buffer for line reading) + self._pos = self._realpos = 0 + # size only matters for seekable files + self._size = 0 + + def __del__(self): + self.close() + + def __iter__(self): + """ + Returns an iterator that can be used to iterate over the lines in this + file. This iterator happens to return the file itself, since a file is + its own iterator. + + :raises ValueError: if the file is closed. + """ + if self._closed: + raise ValueError('I/O operation on closed file') + return self + + def close(self): + """ + Close the file. Future read and write operations will fail. + """ + self.flush() + self._closed = True + + def flush(self): + """ + Write out any data in the write buffer. This may do nothing if write + buffering is not turned on. + """ + self._write_all(self._wbuffer.getvalue()) + self._wbuffer = BytesIO() + return + + if PY2: + def next(self): + """ + Returns the next line from the input, or raises + `~exceptions.StopIteration` when EOF is hit. Unlike Python file + objects, it's okay to mix calls to `next` and `readline`. + + :raises StopIteration: when the end of the file is reached. + + :return: a line (`str`) read from the file. + """ + line = self.readline() + if not line: + raise StopIteration + return line + else: + def __next__(self): + """ + Returns the next line from the input, or raises L{StopIteration} when + EOF is hit. Unlike python file objects, it's okay to mix calls to + C{next} and L{readline}. + + @raise StopIteration: when the end of the file is reached. + + @return: a line read from the file. + @rtype: str + """ + line = self.readline() + if not line: + raise StopIteration + return line + + def read(self, size=None): + """ + Read at most ``size`` bytes from the file (less if we hit the end of the + file first). If the ``size`` argument is negative or omitted, read all + the remaining data in the file. + + .. note:: + ``'b'`` mode flag is ignored (``self.FLAG_BINARY`` in + ``self._flags``), because SSH treats all files as binary, since we + have no idea what encoding the file is in, or even if the file is + text data. + + :param int size: maximum number of bytes to read + :return: + data read from the file (as bytes), or an empty string if EOF was + encountered immediately + """ + if self._closed: + raise IOError('File is closed') + if not (self._flags & self.FLAG_READ): + raise IOError('File is not open for reading') + if (size is None) or (size < 0): + # go for broke + result = self._rbuffer + self._rbuffer = bytes() + self._pos += len(result) + while True: + try: + new_data = self._read(self._DEFAULT_BUFSIZE) + except EOFError: + new_data = None + if (new_data is None) or (len(new_data) == 0): + break + result += new_data + self._realpos += len(new_data) + self._pos += len(new_data) + return result + if size <= len(self._rbuffer): + result = self._rbuffer[:size] + self._rbuffer = self._rbuffer[size:] + self._pos += len(result) + return result + while len(self._rbuffer) < size: + read_size = size - len(self._rbuffer) + if self._flags & self.FLAG_BUFFERED: + read_size = max(self._bufsize, read_size) + try: + new_data = self._read(read_size) + except EOFError: + new_data = None + if (new_data is None) or (len(new_data) == 0): + break + self._rbuffer += new_data + self._realpos += len(new_data) + result = self._rbuffer[:size] + self._rbuffer = self._rbuffer[size:] + self._pos += len(result) + return result + + def readline(self, size=None): + """ + Read one entire line from the file. A trailing newline character is + kept in the string (but may be absent when a file ends with an + incomplete line). If the size argument is present and non-negative, it + is a maximum byte count (including the trailing newline) and an + incomplete line may be returned. An empty string is returned only when + EOF is encountered immediately. + + .. note:: + Unlike stdio's ``fgets``, the returned string contains null + characters (``'\\0'``) if they occurred in the input. + + :param int size: maximum length of returned string. + :return: + next line of the file, or an empty string if the end of the + file has been reached. + + If the file was opened in binary (``'b'``) mode: bytes are returned + Else: the encoding of the file is assumed to be UTF-8 and character + strings (`str`) are returned + """ + # it's almost silly how complex this function is. + if self._closed: + raise IOError('File is closed') + if not (self._flags & self.FLAG_READ): + raise IOError('File not open for reading') + line = self._rbuffer + while True: + if self._at_trailing_cr and (self._flags & self.FLAG_UNIVERSAL_NEWLINE) and (len(line) > 0): + # edge case: the newline may be '\r\n' and we may have read + # only the first '\r' last time. + if line[0] == linefeed_byte_value: + line = line[1:] + self._record_newline(crlf) + else: + self._record_newline(cr_byte) + self._at_trailing_cr = False + # check size before looking for a linefeed, in case we already have + # enough. + if (size is not None) and (size >= 0): + if len(line) >= size: + # truncate line and return + self._rbuffer = line[size:] + line = line[:size] + self._pos += len(line) + return line if self._flags & self.FLAG_BINARY else u(line) + n = size - len(line) + else: + n = self._bufsize + if (linefeed_byte in line) or ((self._flags & self.FLAG_UNIVERSAL_NEWLINE) and (cr_byte in line)): + break + try: + new_data = self._read(n) + except EOFError: + new_data = None + if (new_data is None) or (len(new_data) == 0): + self._rbuffer = bytes() + self._pos += len(line) + return line if self._flags & self.FLAG_BINARY else u(line) + line += new_data + self._realpos += len(new_data) + # find the newline + pos = line.find(linefeed_byte) + if self._flags & self.FLAG_UNIVERSAL_NEWLINE: + rpos = line.find(cr_byte) + if (rpos >= 0) and (rpos < pos or pos < 0): + pos = rpos + xpos = pos + 1 + if (line[pos] == cr_byte_value) and (xpos < len(line)) and (line[xpos] == linefeed_byte_value): + xpos += 1 + self._rbuffer = line[xpos:] + lf = line[pos:xpos] + line = line[:pos] + linefeed_byte + if (len(self._rbuffer) == 0) and (lf == cr_byte): + # we could read the line up to a '\r' and there could still be a + # '\n' following that we read next time. note that and eat it. + self._at_trailing_cr = True + else: + self._record_newline(lf) + self._pos += len(line) + return line if self._flags & self.FLAG_BINARY else u(line) + + def readlines(self, sizehint=None): + """ + Read all remaining lines using `readline` and return them as a list. + If the optional ``sizehint`` argument is present, instead of reading up + to EOF, whole lines totalling approximately sizehint bytes (possibly + after rounding up to an internal buffer size) are read. + + :param int sizehint: desired maximum number of bytes to read. + :return: `list` of lines read from the file. + """ + lines = [] + byte_count = 0 + while True: + line = self.readline() + if len(line) == 0: + break + lines.append(line) + byte_count += len(line) + if (sizehint is not None) and (byte_count >= sizehint): + break + return lines + + def seek(self, offset, whence=0): + """ + Set the file's current position, like stdio's ``fseek``. Not all file + objects support seeking. + + .. note:: + If a file is opened in append mode (``'a'`` or ``'a+'``), any seek + operations will be undone at the next write (as the file position + will move back to the end of the file). + + :param int offset: + position to move to within the file, relative to ``whence``. + :param int whence: + type of movement: 0 = absolute; 1 = relative to the current + position; 2 = relative to the end of the file. + + :raises IOError: if the file doesn't support random access. + """ + raise IOError('File does not support seeking.') + + def tell(self): + """ + Return the file's current position. This may not be accurate or + useful if the underlying file doesn't support random access, or was + opened in append mode. + + :return: file position (`number ` of bytes). + """ + return self._pos + + def write(self, data): + """ + Write data to the file. If write buffering is on (``bufsize`` was + specified and non-zero), some or all of the data may not actually be + written yet. (Use `flush` or `close` to force buffered data to be + written out.) + + :param str data: data to write + """ + data = b(data) + if self._closed: + raise IOError('File is closed') + if not (self._flags & self.FLAG_WRITE): + raise IOError('File not open for writing') + if not (self._flags & self.FLAG_BUFFERED): + self._write_all(data) + return + self._wbuffer.write(data) + if self._flags & self.FLAG_LINE_BUFFERED: + # only scan the new data for linefeed, to avoid wasting time. + last_newline_pos = data.rfind(linefeed_byte) + if last_newline_pos >= 0: + wbuf = self._wbuffer.getvalue() + last_newline_pos += len(wbuf) - len(data) + self._write_all(wbuf[:last_newline_pos + 1]) + self._wbuffer = BytesIO() + self._wbuffer.write(wbuf[last_newline_pos + 1:]) + return + # even if we're line buffering, if the buffer has grown past the + # buffer size, force a flush. + if self._wbuffer.tell() >= self._bufsize: + self.flush() + return + + def writelines(self, sequence): + """ + Write a sequence of strings to the file. The sequence can be any + iterable object producing strings, typically a list of strings. (The + name is intended to match `readlines`; `writelines` does not add line + separators.) + + :param iterable sequence: an iterable sequence of strings. + """ + for line in sequence: + self.write(line) + return + + def xreadlines(self): + """ + Identical to ``iter(f)``. This is a deprecated file interface that + predates Python iterator support. + """ + return self + + @property + def closed(self): + return self._closed + + ### overrides... + + def _read(self, size): + """ + (subclass override) + Read data from the stream. Return ``None`` or raise ``EOFError`` to + indicate EOF. + """ + raise EOFError() + + def _write(self, data): + """ + (subclass override) + Write data into the stream. + """ + raise IOError('write not implemented') + + def _get_size(self): + """ + (subclass override) + Return the size of the file. This is called from within `_set_mode` + if the file is opened in append mode, so the file position can be + tracked and `seek` and `tell` will work correctly. If the file is + a stream that can't be randomly accessed, you don't need to override + this method, + """ + return 0 + + ### internals... + + def _set_mode(self, mode='r', bufsize=-1): + """ + Subclasses call this method to initialize the BufferedFile. + """ + # set bufsize in any event, because it's used for readline(). + self._bufsize = self._DEFAULT_BUFSIZE + if bufsize < 0: + # do no buffering by default, because otherwise writes will get + # buffered in a way that will probably confuse people. + bufsize = 0 + if bufsize == 1: + # apparently, line buffering only affects writes. reads are only + # buffered if you call readline (directly or indirectly: iterating + # over a file will indirectly call readline). + self._flags |= self.FLAG_BUFFERED | self.FLAG_LINE_BUFFERED + elif bufsize > 1: + self._bufsize = bufsize + self._flags |= self.FLAG_BUFFERED + self._flags &= ~self.FLAG_LINE_BUFFERED + elif bufsize == 0: + # unbuffered + self._flags &= ~(self.FLAG_BUFFERED | self.FLAG_LINE_BUFFERED) + + if ('r' in mode) or ('+' in mode): + self._flags |= self.FLAG_READ + if ('w' in mode) or ('+' in mode): + self._flags |= self.FLAG_WRITE + if 'a' in mode: + self._flags |= self.FLAG_WRITE | self.FLAG_APPEND + self._size = self._get_size() + self._pos = self._realpos = self._size + if 'b' in mode: + self._flags |= self.FLAG_BINARY + if 'U' in mode: + self._flags |= self.FLAG_UNIVERSAL_NEWLINE + # built-in file objects have this attribute to store which kinds of + # line terminations they've seen: + # + self.newlines = None + + def _write_all(self, data): + # the underlying stream may be something that does partial writes (like + # a socket). + while len(data) > 0: + count = self._write(data) + data = data[count:] + if self._flags & self.FLAG_APPEND: + self._size += count + self._pos = self._realpos = self._size + else: + self._pos += count + self._realpos += count + return None + + def _record_newline(self, newline): + # silliness about tracking what kinds of newlines we've seen. + # i don't understand why it can be None, a string, or a tuple, instead + # of just always being a tuple, but we'll emulate that behavior anyway. + if not (self._flags & self.FLAG_UNIVERSAL_NEWLINE): + return + if self.newlines is None: + self.newlines = newline + elif self.newlines != newline and isinstance(self.newlines, bytes_types): + self.newlines = (self.newlines, newline) + elif newline not in self.newlines: + self.newlines += (newline,) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/hostkeys.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/hostkeys.py new file mode 100644 index 0000000..c0caeda --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/hostkeys.py @@ -0,0 +1,353 @@ +# Copyright (C) 2006-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + + +import binascii +import os + +from hashlib import sha1 +from hmac import HMAC + +from paramiko.py3compat import b, u, encodebytes, decodebytes + +try: + from collections import MutableMapping +except ImportError: + # noinspection PyUnresolvedReferences + from UserDict import DictMixin as MutableMapping + +from paramiko.dsskey import DSSKey +from paramiko.rsakey import RSAKey +from paramiko.util import get_logger, constant_time_bytes_eq +from paramiko.ecdsakey import ECDSAKey + + +class HostKeys (MutableMapping): + """ + Representation of an OpenSSH-style "known hosts" file. Host keys can be + read from one or more files, and then individual hosts can be looked up to + verify server keys during SSH negotiation. + + A `.HostKeys` object can be treated like a dict; any dict lookup is + equivalent to calling `lookup`. + + .. versionadded:: 1.5.3 + """ + + def __init__(self, filename=None): + """ + Create a new HostKeys object, optionally loading keys from an OpenSSH + style host-key file. + + :param str filename: filename to load host keys from, or ``None`` + """ + # emulate a dict of { hostname: { keytype: PKey } } + self._entries = [] + if filename is not None: + self.load(filename) + + def add(self, hostname, keytype, key): + """ + Add a host key entry to the table. Any existing entry for a + ``(hostname, keytype)`` pair will be replaced. + + :param str hostname: the hostname (or IP) to add + :param str keytype: key type (``"ssh-rsa"`` or ``"ssh-dss"``) + :param .PKey key: the key to add + """ + for e in self._entries: + if (hostname in e.hostnames) and (e.key.get_name() == keytype): + e.key = key + return + self._entries.append(HostKeyEntry([hostname], key)) + + def load(self, filename): + """ + Read a file of known SSH host keys, in the format used by OpenSSH. + This type of file unfortunately doesn't exist on Windows, but on + posix, it will usually be stored in + ``os.path.expanduser("~/.ssh/known_hosts")``. + + If this method is called multiple times, the host keys are merged, + not cleared. So multiple calls to `load` will just call `add`, + replacing any existing entries and adding new ones. + + :param str filename: name of the file to read host keys from + + :raises IOError: if there was an error reading the file + """ + with open(filename, 'r') as f: + for lineno, line in enumerate(f): + line = line.strip() + if (len(line) == 0) or (line[0] == '#'): + continue + e = HostKeyEntry.from_line(line, lineno) + if e is not None: + _hostnames = e.hostnames + for h in _hostnames: + if self.check(h, e.key): + e.hostnames.remove(h) + if len(e.hostnames): + self._entries.append(e) + + def save(self, filename): + """ + Save host keys into a file, in the format used by OpenSSH. The order of + keys in the file will be preserved when possible (if these keys were + loaded from a file originally). The single exception is that combined + lines will be split into individual key lines, which is arguably a bug. + + :param str filename: name of the file to write + + :raises IOError: if there was an error writing the file + + .. versionadded:: 1.6.1 + """ + with open(filename, 'w') as f: + for e in self._entries: + line = e.to_line() + if line: + f.write(line) + + def lookup(self, hostname): + """ + Find a hostkey entry for a given hostname or IP. If no entry is found, + ``None`` is returned. Otherwise a dictionary of keytype to key is + returned. The keytype will be either ``"ssh-rsa"`` or ``"ssh-dss"``. + + :param str hostname: the hostname (or IP) to lookup + :return: dict of `str` -> `.PKey` keys associated with this host (or ``None``) + """ + class SubDict (MutableMapping): + def __init__(self, hostname, entries, hostkeys): + self._hostname = hostname + self._entries = entries + self._hostkeys = hostkeys + + def __iter__(self): + for k in self.keys(): + yield k + + def __len__(self): + return len(self.keys()) + + def __delitem__(self, key): + for e in list(self._entries): + if e.key.get_name() == key: + self._entries.remove(e) + else: + raise KeyError(key) + + def __getitem__(self, key): + for e in self._entries: + if e.key.get_name() == key: + return e.key + raise KeyError(key) + + def __setitem__(self, key, val): + for e in self._entries: + if e.key is None: + continue + if e.key.get_name() == key: + # replace + e.key = val + break + else: + # add a new one + e = HostKeyEntry([hostname], val) + self._entries.append(e) + self._hostkeys._entries.append(e) + + def keys(self): + return [e.key.get_name() for e in self._entries if e.key is not None] + + entries = [] + for e in self._entries: + for h in e.hostnames: + if h.startswith('|1|') and constant_time_bytes_eq(self.hash_host(hostname, h), h) or h == hostname: + entries.append(e) + if len(entries) == 0: + return None + return SubDict(hostname, entries, self) + + def check(self, hostname, key): + """ + Return True if the given key is associated with the given hostname + in this dictionary. + + :param str hostname: hostname (or IP) of the SSH server + :param .PKey key: the key to check + :return: + ``True`` if the key is associated with the hostname; else ``False`` + """ + k = self.lookup(hostname) + if k is None: + return False + host_key = k.get(key.get_name(), None) + if host_key is None: + return False + return host_key.asbytes() == key.asbytes() + + def clear(self): + """ + Remove all host keys from the dictionary. + """ + self._entries = [] + + def __iter__(self): + for k in self.keys(): + yield k + + def __len__(self): + return len(self.keys()) + + def __delitem__(self, key): + k = self[key] + + def __getitem__(self, key): + ret = self.lookup(key) + if ret is None: + raise KeyError(key) + return ret + + def __setitem__(self, hostname, entry): + # don't use this please. + if len(entry) == 0: + self._entries.append(HostKeyEntry([hostname], None)) + return + for key_type in entry.keys(): + found = False + for e in self._entries: + if (hostname in e.hostnames) and (e.key.get_name() == key_type): + # replace + e.key = entry[key_type] + found = True + if not found: + self._entries.append(HostKeyEntry([hostname], entry[key_type])) + + def keys(self): + # Python 2.4 sets would be nice here. + ret = [] + for e in self._entries: + for h in e.hostnames: + if h not in ret: + ret.append(h) + return ret + + def values(self): + ret = [] + for k in self.keys(): + ret.append(self.lookup(k)) + return ret + + def hash_host(hostname, salt=None): + """ + Return a "hashed" form of the hostname, as used by OpenSSH when storing + hashed hostnames in the known_hosts file. + + :param str hostname: the hostname to hash + :param str salt: optional salt to use when hashing (must be 20 bytes long) + :return: the hashed hostname as a `str` + """ + if salt is None: + salt = os.urandom(sha1().digest_size) + else: + if salt.startswith('|1|'): + salt = salt.split('|')[2] + salt = decodebytes(b(salt)) + assert len(salt) == sha1().digest_size + hmac = HMAC(salt, b(hostname), sha1).digest() + hostkey = '|1|%s|%s' % (u(encodebytes(salt)), u(encodebytes(hmac))) + return hostkey.replace('\n', '') + hash_host = staticmethod(hash_host) + + +class InvalidHostKey(Exception): + def __init__(self, line, exc): + self.line = line + self.exc = exc + self.args = (line, exc) + + +class HostKeyEntry: + """ + Representation of a line in an OpenSSH-style "known hosts" file. + """ + + def __init__(self, hostnames=None, key=None): + self.valid = (hostnames is not None) and (key is not None) + self.hostnames = hostnames + self.key = key + + def from_line(cls, line, lineno=None): + """ + Parses the given line of text to find the names for the host, + the type of key, and the key data. The line is expected to be in the + format used by the OpenSSH known_hosts file. + + Lines are expected to not have leading or trailing whitespace. + We don't bother to check for comments or empty lines. All of + that should be taken care of before sending the line to us. + + :param str line: a line from an OpenSSH known_hosts file + """ + log = get_logger('paramiko.hostkeys') + fields = line.split(' ') + if len(fields) < 3: + # Bad number of fields + log.info("Not enough fields found in known_hosts in line %s (%r)" % + (lineno, line)) + return None + fields = fields[:3] + + names, keytype, key = fields + names = names.split(',') + + # Decide what kind of key we're looking at and create an object + # to hold it accordingly. + try: + key = b(key) + if keytype == 'ssh-rsa': + key = RSAKey(data=decodebytes(key)) + elif keytype == 'ssh-dss': + key = DSSKey(data=decodebytes(key)) + elif keytype == 'ecdsa-sha2-nistp256': + key = ECDSAKey(data=decodebytes(key)) + else: + log.info("Unable to handle key of type %s" % (keytype,)) + return None + + except binascii.Error as e: + raise InvalidHostKey(line, e) + + return cls(names, key) + from_line = classmethod(from_line) + + def to_line(self): + """ + Returns a string in OpenSSH known_hosts file format, or None if + the object is not in a valid state. A trailing newline is + included. + """ + if self.valid: + return '%s %s %s\n' % (','.join(self.hostnames), self.key.get_name(), + self.key.get_base64()) + return None + + def __repr__(self): + return '' % (self.hostnames, self.key) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/kex_gex.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/kex_gex.py new file mode 100644 index 0000000..5ff8a28 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/kex_gex.py @@ -0,0 +1,244 @@ +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +Variant on `KexGroup1 ` where the prime "p" and +generator "g" are provided by the server. A bit more work is required on the +client side, and a B{lot} more on the server side. +""" + +import os +from hashlib import sha1 + +from paramiko import util +from paramiko.common import DEBUG +from paramiko.message import Message +from paramiko.py3compat import byte_chr, byte_ord, byte_mask +from paramiko.ssh_exception import SSHException + + +_MSG_KEXDH_GEX_REQUEST_OLD, _MSG_KEXDH_GEX_GROUP, _MSG_KEXDH_GEX_INIT, \ + _MSG_KEXDH_GEX_REPLY, _MSG_KEXDH_GEX_REQUEST = range(30, 35) +c_MSG_KEXDH_GEX_REQUEST_OLD, c_MSG_KEXDH_GEX_GROUP, c_MSG_KEXDH_GEX_INIT, \ + c_MSG_KEXDH_GEX_REPLY, c_MSG_KEXDH_GEX_REQUEST = [byte_chr(c) for c in range(30, 35)] + + +class KexGex (object): + + name = 'diffie-hellman-group-exchange-sha1' + min_bits = 1024 + max_bits = 8192 + preferred_bits = 2048 + + def __init__(self, transport): + self.transport = transport + self.p = None + self.q = None + self.g = None + self.x = None + self.e = None + self.f = None + self.old_style = False + + def start_kex(self, _test_old_style=False): + if self.transport.server_mode: + self.transport._expect_packet(_MSG_KEXDH_GEX_REQUEST, _MSG_KEXDH_GEX_REQUEST_OLD) + return + # request a bit range: we accept (min_bits) to (max_bits), but prefer + # (preferred_bits). according to the spec, we shouldn't pull the + # minimum up above 1024. + m = Message() + if _test_old_style: + # only used for unit tests: we shouldn't ever send this + m.add_byte(c_MSG_KEXDH_GEX_REQUEST_OLD) + m.add_int(self.preferred_bits) + self.old_style = True + else: + m.add_byte(c_MSG_KEXDH_GEX_REQUEST) + m.add_int(self.min_bits) + m.add_int(self.preferred_bits) + m.add_int(self.max_bits) + self.transport._send_message(m) + self.transport._expect_packet(_MSG_KEXDH_GEX_GROUP) + + def parse_next(self, ptype, m): + if ptype == _MSG_KEXDH_GEX_REQUEST: + return self._parse_kexdh_gex_request(m) + elif ptype == _MSG_KEXDH_GEX_GROUP: + return self._parse_kexdh_gex_group(m) + elif ptype == _MSG_KEXDH_GEX_INIT: + return self._parse_kexdh_gex_init(m) + elif ptype == _MSG_KEXDH_GEX_REPLY: + return self._parse_kexdh_gex_reply(m) + elif ptype == _MSG_KEXDH_GEX_REQUEST_OLD: + return self._parse_kexdh_gex_request_old(m) + raise SSHException('KexGex asked to handle packet type %d' % ptype) + + ### internals... + + def _generate_x(self): + # generate an "x" (1 < x < (p-1)/2). + q = (self.p - 1) // 2 + qnorm = util.deflate_long(q, 0) + qhbyte = byte_ord(qnorm[0]) + byte_count = len(qnorm) + qmask = 0xff + while not (qhbyte & 0x80): + qhbyte <<= 1 + qmask >>= 1 + while True: + x_bytes = os.urandom(byte_count) + x_bytes = byte_mask(x_bytes[0], qmask) + x_bytes[1:] + x = util.inflate_long(x_bytes, 1) + if (x > 1) and (x < q): + break + self.x = x + + def _parse_kexdh_gex_request(self, m): + minbits = m.get_int() + preferredbits = m.get_int() + maxbits = m.get_int() + # smoosh the user's preferred size into our own limits + if preferredbits > self.max_bits: + preferredbits = self.max_bits + if preferredbits < self.min_bits: + preferredbits = self.min_bits + # fix min/max if they're inconsistent. technically, we could just pout + # and hang up, but there's no harm in giving them the benefit of the + # doubt and just picking a bitsize for them. + if minbits > preferredbits: + minbits = preferredbits + if maxbits < preferredbits: + maxbits = preferredbits + # now save a copy + self.min_bits = minbits + self.preferred_bits = preferredbits + self.max_bits = maxbits + # generate prime + pack = self.transport._get_modulus_pack() + if pack is None: + raise SSHException('Can\'t do server-side gex with no modulus pack') + self.transport._log(DEBUG, 'Picking p (%d <= %d <= %d bits)' % (minbits, preferredbits, maxbits)) + self.g, self.p = pack.get_modulus(minbits, preferredbits, maxbits) + m = Message() + m.add_byte(c_MSG_KEXDH_GEX_GROUP) + m.add_mpint(self.p) + m.add_mpint(self.g) + self.transport._send_message(m) + self.transport._expect_packet(_MSG_KEXDH_GEX_INIT) + + def _parse_kexdh_gex_request_old(self, m): + # same as above, but without min_bits or max_bits (used by older clients like putty) + self.preferred_bits = m.get_int() + # smoosh the user's preferred size into our own limits + if self.preferred_bits > self.max_bits: + self.preferred_bits = self.max_bits + if self.preferred_bits < self.min_bits: + self.preferred_bits = self.min_bits + # generate prime + pack = self.transport._get_modulus_pack() + if pack is None: + raise SSHException('Can\'t do server-side gex with no modulus pack') + self.transport._log(DEBUG, 'Picking p (~ %d bits)' % (self.preferred_bits,)) + self.g, self.p = pack.get_modulus(self.min_bits, self.preferred_bits, self.max_bits) + m = Message() + m.add_byte(c_MSG_KEXDH_GEX_GROUP) + m.add_mpint(self.p) + m.add_mpint(self.g) + self.transport._send_message(m) + self.transport._expect_packet(_MSG_KEXDH_GEX_INIT) + self.old_style = True + + def _parse_kexdh_gex_group(self, m): + self.p = m.get_mpint() + self.g = m.get_mpint() + # reject if p's bit length < 1024 or > 8192 + bitlen = util.bit_length(self.p) + if (bitlen < 1024) or (bitlen > 8192): + raise SSHException('Server-generated gex p (don\'t ask) is out of range (%d bits)' % bitlen) + self.transport._log(DEBUG, 'Got server p (%d bits)' % bitlen) + self._generate_x() + # now compute e = g^x mod p + self.e = pow(self.g, self.x, self.p) + m = Message() + m.add_byte(c_MSG_KEXDH_GEX_INIT) + m.add_mpint(self.e) + self.transport._send_message(m) + self.transport._expect_packet(_MSG_KEXDH_GEX_REPLY) + + def _parse_kexdh_gex_init(self, m): + self.e = m.get_mpint() + if (self.e < 1) or (self.e > self.p - 1): + raise SSHException('Client kex "e" is out of range') + self._generate_x() + self.f = pow(self.g, self.x, self.p) + K = pow(self.e, self.x, self.p) + key = self.transport.get_server_key().asbytes() + # okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K) + hm = Message() + hm.add(self.transport.remote_version, self.transport.local_version, + self.transport.remote_kex_init, self.transport.local_kex_init, + key) + if not self.old_style: + hm.add_int(self.min_bits) + hm.add_int(self.preferred_bits) + if not self.old_style: + hm.add_int(self.max_bits) + hm.add_mpint(self.p) + hm.add_mpint(self.g) + hm.add_mpint(self.e) + hm.add_mpint(self.f) + hm.add_mpint(K) + H = sha1(hm.asbytes()).digest() + self.transport._set_K_H(K, H) + # sign it + sig = self.transport.get_server_key().sign_ssh_data(H) + # send reply + m = Message() + m.add_byte(c_MSG_KEXDH_GEX_REPLY) + m.add_string(key) + m.add_mpint(self.f) + m.add_string(sig) + self.transport._send_message(m) + self.transport._activate_outbound() + + def _parse_kexdh_gex_reply(self, m): + host_key = m.get_string() + self.f = m.get_mpint() + sig = m.get_string() + if (self.f < 1) or (self.f > self.p - 1): + raise SSHException('Server kex "f" is out of range') + K = pow(self.f, self.x, self.p) + # okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K) + hm = Message() + hm.add(self.transport.local_version, self.transport.remote_version, + self.transport.local_kex_init, self.transport.remote_kex_init, + host_key) + if not self.old_style: + hm.add_int(self.min_bits) + hm.add_int(self.preferred_bits) + if not self.old_style: + hm.add_int(self.max_bits) + hm.add_mpint(self.p) + hm.add_mpint(self.g) + hm.add_mpint(self.e) + hm.add_mpint(self.f) + hm.add_mpint(K) + self.transport._set_K_H(K, sha1(hm.asbytes()).digest()) + self.transport._verify_key(host_key, sig) + self.transport._activate_outbound() diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/kex_group1.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/kex_group1.py new file mode 100644 index 0000000..7ccceea --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/kex_group1.py @@ -0,0 +1,139 @@ +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +Standard SSH key exchange ("kex" if you wanna sound cool). Diffie-Hellman of +1024 bit key halves, using a known "p" prime and "g" generator. +""" + +import os +from hashlib import sha1 + +from paramiko import util +from paramiko.common import max_byte, zero_byte +from paramiko.message import Message +from paramiko.py3compat import byte_chr, long, byte_mask +from paramiko.ssh_exception import SSHException + + +_MSG_KEXDH_INIT, _MSG_KEXDH_REPLY = range(30, 32) +c_MSG_KEXDH_INIT, c_MSG_KEXDH_REPLY = [byte_chr(c) for c in range(30, 32)] + +# draft-ietf-secsh-transport-09.txt, page 17 +P = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF +G = 2 + +b7fffffffffffffff = byte_chr(0x7f) + max_byte * 7 +b0000000000000000 = zero_byte * 8 + + +class KexGroup1(object): + + name = 'diffie-hellman-group1-sha1' + + def __init__(self, transport): + self.transport = transport + self.x = long(0) + self.e = long(0) + self.f = long(0) + + def start_kex(self): + self._generate_x() + if self.transport.server_mode: + # compute f = g^x mod p, but don't send it yet + self.f = pow(G, self.x, P) + self.transport._expect_packet(_MSG_KEXDH_INIT) + return + # compute e = g^x mod p (where g=2), and send it + self.e = pow(G, self.x, P) + m = Message() + m.add_byte(c_MSG_KEXDH_INIT) + m.add_mpint(self.e) + self.transport._send_message(m) + self.transport._expect_packet(_MSG_KEXDH_REPLY) + + def parse_next(self, ptype, m): + if self.transport.server_mode and (ptype == _MSG_KEXDH_INIT): + return self._parse_kexdh_init(m) + elif not self.transport.server_mode and (ptype == _MSG_KEXDH_REPLY): + return self._parse_kexdh_reply(m) + raise SSHException('KexGroup1 asked to handle packet type %d' % ptype) + + ### internals... + + def _generate_x(self): + # generate an "x" (1 < x < q), where q is (p-1)/2. + # p is a 128-byte (1024-bit) number, where the first 64 bits are 1. + # therefore q can be approximated as a 2^1023. we drop the subset of + # potential x where the first 63 bits are 1, because some of those will be + # larger than q (but this is a tiny tiny subset of potential x). + while 1: + x_bytes = os.urandom(128) + x_bytes = byte_mask(x_bytes[0], 0x7f) + x_bytes[1:] + if (x_bytes[:8] != b7fffffffffffffff and + x_bytes[:8] != b0000000000000000): + break + self.x = util.inflate_long(x_bytes) + + def _parse_kexdh_reply(self, m): + # client mode + host_key = m.get_string() + self.f = m.get_mpint() + if (self.f < 1) or (self.f > P - 1): + raise SSHException('Server kex "f" is out of range') + sig = m.get_binary() + K = pow(self.f, self.x, P) + # okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || e || f || K) + hm = Message() + hm.add(self.transport.local_version, self.transport.remote_version, + self.transport.local_kex_init, self.transport.remote_kex_init) + hm.add_string(host_key) + hm.add_mpint(self.e) + hm.add_mpint(self.f) + hm.add_mpint(K) + self.transport._set_K_H(K, sha1(hm.asbytes()).digest()) + self.transport._verify_key(host_key, sig) + self.transport._activate_outbound() + + def _parse_kexdh_init(self, m): + # server mode + self.e = m.get_mpint() + if (self.e < 1) or (self.e > P - 1): + raise SSHException('Client kex "e" is out of range') + K = pow(self.e, self.x, P) + key = self.transport.get_server_key().asbytes() + # okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || e || f || K) + hm = Message() + hm.add(self.transport.remote_version, self.transport.local_version, + self.transport.remote_kex_init, self.transport.local_kex_init) + hm.add_string(key) + hm.add_mpint(self.e) + hm.add_mpint(self.f) + hm.add_mpint(K) + H = sha1(hm.asbytes()).digest() + self.transport._set_K_H(K, H) + # sign it + sig = self.transport.get_server_key().sign_ssh_data(H) + # send reply + m = Message() + m.add_byte(c_MSG_KEXDH_REPLY) + m.add_string(key) + m.add_mpint(self.f) + m.add_string(sig) + self.transport._send_message(m) + self.transport._activate_outbound() diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/message.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/message.py new file mode 100644 index 0000000..da6acf8 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/message.py @@ -0,0 +1,322 @@ +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +Implementation of an SSH2 "message". +""" + +import struct + +from paramiko import util +from paramiko.common import zero_byte, max_byte, one_byte, asbytes +from paramiko.py3compat import long, BytesIO, u, integer_types + + +class Message (object): + """ + An SSH2 message is a stream of bytes that encodes some combination of + strings, integers, bools, and infinite-precision integers (known in Python + as longs). This class builds or breaks down such a byte stream. + + Normally you don't need to deal with anything this low-level, but it's + exposed for people implementing custom extensions, or features that + paramiko doesn't support yet. + """ + + big_int = long(0xff000000) + + def __init__(self, content=None): + """ + Create a new SSH2 message. + + :param str content: + the byte stream to use as the message content (passed in only when + decomposing a message). + """ + if content is not None: + self.packet = BytesIO(content) + else: + self.packet = BytesIO() + + def __str__(self): + """ + Return the byte stream content of this message, as a string/bytes obj. + """ + return self.asbytes() + + def __repr__(self): + """ + Returns a string representation of this object, for debugging. + """ + return 'paramiko.Message(' + repr(self.packet.getvalue()) + ')' + + def asbytes(self): + """ + Return the byte stream content of this Message, as bytes. + """ + return self.packet.getvalue() + + def rewind(self): + """ + Rewind the message to the beginning as if no items had been parsed + out of it yet. + """ + self.packet.seek(0) + + def get_remainder(self): + """ + Return the bytes (as a `str`) of this message that haven't already been + parsed and returned. + """ + position = self.packet.tell() + remainder = self.packet.read() + self.packet.seek(position) + return remainder + + def get_so_far(self): + """ + Returns the `str` bytes of this message that have been parsed and + returned. The string passed into a message's constructor can be + regenerated by concatenating ``get_so_far`` and `get_remainder`. + """ + position = self.packet.tell() + self.rewind() + return self.packet.read(position) + + def get_bytes(self, n): + """ + Return the next ``n`` bytes of the message (as a `str`), without + decomposing into an int, decoded string, etc. Just the raw bytes are + returned. Returns a string of ``n`` zero bytes if there weren't ``n`` + bytes remaining in the message. + """ + b = self.packet.read(n) + max_pad_size = 1 << 20 # Limit padding to 1 MB + if len(b) < n < max_pad_size: + return b + zero_byte * (n - len(b)) + return b + + def get_byte(self): + """ + Return the next byte of the message, without decomposing it. This + is equivalent to `get_bytes(1) `. + + :return: + the next (`str`) byte of the message, or ``'\000'`` if there aren't + any bytes remaining. + """ + return self.get_bytes(1) + + def get_boolean(self): + """ + Fetch a boolean from the stream. + """ + b = self.get_bytes(1) + return b != zero_byte + + def get_int(self): + """ + Fetch an int from the stream. + + :return: a 32-bit unsigned `int`. + """ + byte = self.get_bytes(1) + if byte == max_byte: + return util.inflate_long(self.get_binary()) + byte += self.get_bytes(3) + return struct.unpack('>I', byte)[0] + + def get_size(self): + """ + Fetch an int from the stream. + + @return: a 32-bit unsigned integer. + @rtype: int + """ + return struct.unpack('>I', self.get_bytes(4))[0] + + def get_int64(self): + """ + Fetch a 64-bit int from the stream. + + :return: a 64-bit unsigned integer (`long`). + """ + return struct.unpack('>Q', self.get_bytes(8))[0] + + def get_mpint(self): + """ + Fetch a long int (mpint) from the stream. + + :return: an arbitrary-length integer (`long`). + """ + return util.inflate_long(self.get_binary()) + + def get_string(self): + """ + Fetch a `str` from the stream. This could be a byte string and may + contain unprintable characters. (It's not unheard of for a string to + contain another byte-stream message.) + """ + return self.get_bytes(self.get_size()) + + def get_text(self): + """ + Fetch a string from the stream. This could be a byte string and may + contain unprintable characters. (It's not unheard of for a string to + contain another byte-stream Message.) + + @return: a string. + @rtype: string + """ + return u(self.get_bytes(self.get_size())) + #return self.get_bytes(self.get_size()) + + def get_binary(self): + """ + Fetch a string from the stream. This could be a byte string and may + contain unprintable characters. (It's not unheard of for a string to + contain another byte-stream Message.) + + @return: a string. + @rtype: string + """ + return self.get_bytes(self.get_size()) + + def get_list(self): + """ + Fetch a `list` of `strings ` from the stream. + + These are trivially encoded as comma-separated values in a string. + """ + return self.get_text().split(',') + + def add_bytes(self, b): + """ + Write bytes to the stream, without any formatting. + + :param str b: bytes to add + """ + self.packet.write(b) + return self + + def add_byte(self, b): + """ + Write a single byte to the stream, without any formatting. + + :param str b: byte to add + """ + self.packet.write(b) + return self + + def add_boolean(self, b): + """ + Add a boolean value to the stream. + + :param bool b: boolean value to add + """ + if b: + self.packet.write(one_byte) + else: + self.packet.write(zero_byte) + return self + + def add_size(self, n): + """ + Add an integer to the stream. + + :param int n: integer to add + """ + self.packet.write(struct.pack('>I', n)) + return self + + def add_int(self, n): + """ + Add an integer to the stream. + + :param int n: integer to add + """ + if n >= Message.big_int: + self.packet.write(max_byte) + self.add_string(util.deflate_long(n)) + else: + self.packet.write(struct.pack('>I', n)) + return self + + def add_int64(self, n): + """ + Add a 64-bit int to the stream. + + :param long n: long int to add + """ + self.packet.write(struct.pack('>Q', n)) + return self + + def add_mpint(self, z): + """ + Add a long int to the stream, encoded as an infinite-precision + integer. This method only works on positive numbers. + + :param long z: long int to add + """ + self.add_string(util.deflate_long(z)) + return self + + def add_string(self, s): + """ + Add a string to the stream. + + :param str s: string to add + """ + s = asbytes(s) + self.add_size(len(s)) + self.packet.write(s) + return self + + def add_list(self, l): + """ + Add a list of strings to the stream. They are encoded identically to + a single string of values separated by commas. (Yes, really, that's + how SSH2 does it.) + + :param list l: list of strings to add + """ + self.add_string(','.join(l)) + return self + + def _add(self, i): + if type(i) is bool: + return self.add_boolean(i) + elif isinstance(i, integer_types): + return self.add_int(i) + elif type(i) is list: + return self.add_list(i) + else: + return self.add_string(i) + + def add(self, *seq): + """ + Add a sequence of items to the stream. The values are encoded based + on their type: str, int, bool, list, or long. + + .. warning:: + Longs are encoded non-deterministically. Don't use this method. + + :param seq: the sequence of items + """ + for item in seq: + self._add(item) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/packet.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/packet.py new file mode 100644 index 0000000..e97d92f --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/packet.py @@ -0,0 +1,459 @@ +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +Packet handling +""" + +import errno +import os +import socket +import struct +import threading +import time +from hmac import HMAC + +from paramiko import util +from paramiko.common import linefeed_byte, cr_byte_value, asbytes, MSG_NAMES, \ + DEBUG, xffffffff, zero_byte +from paramiko.py3compat import u, byte_ord +from paramiko.ssh_exception import SSHException, ProxyCommandFailure +from paramiko.message import Message + + +def compute_hmac(key, message, digest_class): + return HMAC(key, message, digest_class).digest() + + +class NeedRekeyException (Exception): + pass + + +class Packetizer (object): + """ + Implementation of the base SSH packet protocol. + """ + + # READ the secsh RFC's before raising these values. if anything, + # they should probably be lower. + REKEY_PACKETS = pow(2, 29) + REKEY_BYTES = pow(2, 29) + + REKEY_PACKETS_OVERFLOW_MAX = pow(2, 29) # Allow receiving this many packets after a re-key request before terminating + REKEY_BYTES_OVERFLOW_MAX = pow(2, 29) # Allow receiving this many bytes after a re-key request before terminating + + def __init__(self, socket): + self.__socket = socket + self.__logger = None + self.__closed = False + self.__dump_packets = False + self.__need_rekey = False + self.__init_count = 0 + self.__remainder = bytes() + + # used for noticing when to re-key: + self.__sent_bytes = 0 + self.__sent_packets = 0 + self.__received_bytes = 0 + self.__received_packets = 0 + self.__received_bytes_overflow = 0 + self.__received_packets_overflow = 0 + + # current inbound/outbound ciphering: + self.__block_size_out = 8 + self.__block_size_in = 8 + self.__mac_size_out = 0 + self.__mac_size_in = 0 + self.__block_engine_out = None + self.__block_engine_in = None + self.__sdctr_out = False + self.__mac_engine_out = None + self.__mac_engine_in = None + self.__mac_key_out = bytes() + self.__mac_key_in = bytes() + self.__compress_engine_out = None + self.__compress_engine_in = None + self.__sequence_number_out = 0 + self.__sequence_number_in = 0 + + # lock around outbound writes (packet computation) + self.__write_lock = threading.RLock() + + # keepalives: + self.__keepalive_interval = 0 + self.__keepalive_last = time.time() + self.__keepalive_callback = None + + def set_log(self, log): + """ + Set the Python log object to use for logging. + """ + self.__logger = log + + def set_outbound_cipher(self, block_engine, block_size, mac_engine, mac_size, mac_key, sdctr=False): + """ + Switch outbound data cipher. + """ + self.__block_engine_out = block_engine + self.__sdctr_out = sdctr + self.__block_size_out = block_size + self.__mac_engine_out = mac_engine + self.__mac_size_out = mac_size + self.__mac_key_out = mac_key + self.__sent_bytes = 0 + self.__sent_packets = 0 + # wait until the reset happens in both directions before clearing rekey flag + self.__init_count |= 1 + if self.__init_count == 3: + self.__init_count = 0 + self.__need_rekey = False + + def set_inbound_cipher(self, block_engine, block_size, mac_engine, mac_size, mac_key): + """ + Switch inbound data cipher. + """ + self.__block_engine_in = block_engine + self.__block_size_in = block_size + self.__mac_engine_in = mac_engine + self.__mac_size_in = mac_size + self.__mac_key_in = mac_key + self.__received_bytes = 0 + self.__received_packets = 0 + self.__received_bytes_overflow = 0 + self.__received_packets_overflow = 0 + # wait until the reset happens in both directions before clearing rekey flag + self.__init_count |= 2 + if self.__init_count == 3: + self.__init_count = 0 + self.__need_rekey = False + + def set_outbound_compressor(self, compressor): + self.__compress_engine_out = compressor + + def set_inbound_compressor(self, compressor): + self.__compress_engine_in = compressor + + def close(self): + self.__closed = True + self.__socket.close() + + def set_hexdump(self, hexdump): + self.__dump_packets = hexdump + + def get_hexdump(self): + return self.__dump_packets + + def get_mac_size_in(self): + return self.__mac_size_in + + def get_mac_size_out(self): + return self.__mac_size_out + + def need_rekey(self): + """ + Returns ``True`` if a new set of keys needs to be negotiated. This + will be triggered during a packet read or write, so it should be + checked after every read or write, or at least after every few. + """ + return self.__need_rekey + + def set_keepalive(self, interval, callback): + """ + Turn on/off the callback keepalive. If ``interval`` seconds pass with + no data read from or written to the socket, the callback will be + executed and the timer will be reset. + """ + self.__keepalive_interval = interval + self.__keepalive_callback = callback + self.__keepalive_last = time.time() + + def read_all(self, n, check_rekey=False): + """ + Read as close to N bytes as possible, blocking as long as necessary. + + :param int n: number of bytes to read + :return: the data read, as a `str` + + :raises EOFError: + if the socket was closed before all the bytes could be read + """ + out = bytes() + # handle over-reading from reading the banner line + if len(self.__remainder) > 0: + out = self.__remainder[:n] + self.__remainder = self.__remainder[n:] + n -= len(out) + while n > 0: + got_timeout = False + try: + x = self.__socket.recv(n) + if len(x) == 0: + raise EOFError() + out += x + n -= len(x) + except socket.timeout: + got_timeout = True + except socket.error as e: + # on Linux, sometimes instead of socket.timeout, we get + # EAGAIN. this is a bug in recent (> 2.6.9) kernels but + # we need to work around it. + if (type(e.args) is tuple) and (len(e.args) > 0) and (e.args[0] == errno.EAGAIN): + got_timeout = True + elif (type(e.args) is tuple) and (len(e.args) > 0) and (e.args[0] == errno.EINTR): + # syscall interrupted; try again + pass + elif self.__closed: + raise EOFError() + else: + raise + if got_timeout: + if self.__closed: + raise EOFError() + if check_rekey and (len(out) == 0) and self.__need_rekey: + raise NeedRekeyException() + self._check_keepalive() + return out + + def write_all(self, out): + self.__keepalive_last = time.time() + while len(out) > 0: + retry_write = False + try: + n = self.__socket.send(out) + except socket.timeout: + retry_write = True + except socket.error as e: + if (type(e.args) is tuple) and (len(e.args) > 0) and (e.args[0] == errno.EAGAIN): + retry_write = True + elif (type(e.args) is tuple) and (len(e.args) > 0) and (e.args[0] == errno.EINTR): + # syscall interrupted; try again + retry_write = True + else: + n = -1 + except ProxyCommandFailure: + raise # so it doesn't get swallowed by the below catchall + except Exception: + # could be: (32, 'Broken pipe') + n = -1 + if retry_write: + n = 0 + if self.__closed: + n = -1 + if n < 0: + raise EOFError() + if n == len(out): + break + out = out[n:] + return + + def readline(self, timeout): + """ + Read a line from the socket. We assume no data is pending after the + line, so it's okay to attempt large reads. + """ + buf = self.__remainder + while not linefeed_byte in buf: + buf += self._read_timeout(timeout) + n = buf.index(linefeed_byte) + self.__remainder = buf[n + 1:] + buf = buf[:n] + if (len(buf) > 0) and (buf[-1] == cr_byte_value): + buf = buf[:-1] + return u(buf) + + def send_message(self, data): + """ + Write a block of data using the current cipher, as an SSH block. + """ + # encrypt this sucka + data = asbytes(data) + cmd = byte_ord(data[0]) + if cmd in MSG_NAMES: + cmd_name = MSG_NAMES[cmd] + else: + cmd_name = '$%x' % cmd + orig_len = len(data) + self.__write_lock.acquire() + try: + if self.__compress_engine_out is not None: + data = self.__compress_engine_out(data) + packet = self._build_packet(data) + if self.__dump_packets: + self._log(DEBUG, 'Write packet <%s>, length %d' % (cmd_name, orig_len)) + self._log(DEBUG, util.format_binary(packet, 'OUT: ')) + if self.__block_engine_out is not None: + out = self.__block_engine_out.encrypt(packet) + else: + out = packet + # + mac + if self.__block_engine_out is not None: + payload = struct.pack('>I', self.__sequence_number_out) + packet + out += compute_hmac(self.__mac_key_out, payload, self.__mac_engine_out)[:self.__mac_size_out] + self.__sequence_number_out = (self.__sequence_number_out + 1) & xffffffff + self.write_all(out) + + self.__sent_bytes += len(out) + self.__sent_packets += 1 + if (self.__sent_packets >= self.REKEY_PACKETS or self.__sent_bytes >= self.REKEY_BYTES)\ + and not self.__need_rekey: + # only ask once for rekeying + self._log(DEBUG, 'Rekeying (hit %d packets, %d bytes sent)' % + (self.__sent_packets, self.__sent_bytes)) + self.__received_bytes_overflow = 0 + self.__received_packets_overflow = 0 + self._trigger_rekey() + finally: + self.__write_lock.release() + + def read_message(self): + """ + Only one thread should ever be in this function (no other locking is + done). + + :raises SSHException: if the packet is mangled + :raises NeedRekeyException: if the transport should rekey + """ + header = self.read_all(self.__block_size_in, check_rekey=True) + if self.__block_engine_in is not None: + header = self.__block_engine_in.decrypt(header) + if self.__dump_packets: + self._log(DEBUG, util.format_binary(header, 'IN: ')) + packet_size = struct.unpack('>I', header[:4])[0] + # leftover contains decrypted bytes from the first block (after the length field) + leftover = header[4:] + if (packet_size - len(leftover)) % self.__block_size_in != 0: + raise SSHException('Invalid packet blocking') + buf = self.read_all(packet_size + self.__mac_size_in - len(leftover)) + packet = buf[:packet_size - len(leftover)] + post_packet = buf[packet_size - len(leftover):] + if self.__block_engine_in is not None: + packet = self.__block_engine_in.decrypt(packet) + if self.__dump_packets: + self._log(DEBUG, util.format_binary(packet, 'IN: ')) + packet = leftover + packet + + if self.__mac_size_in > 0: + mac = post_packet[:self.__mac_size_in] + mac_payload = struct.pack('>II', self.__sequence_number_in, packet_size) + packet + my_mac = compute_hmac(self.__mac_key_in, mac_payload, self.__mac_engine_in)[:self.__mac_size_in] + if not util.constant_time_bytes_eq(my_mac, mac): + raise SSHException('Mismatched MAC') + padding = byte_ord(packet[0]) + payload = packet[1:packet_size - padding] + + if self.__dump_packets: + self._log(DEBUG, 'Got payload (%d bytes, %d padding)' % (packet_size, padding)) + + if self.__compress_engine_in is not None: + payload = self.__compress_engine_in(payload) + + msg = Message(payload[1:]) + msg.seqno = self.__sequence_number_in + self.__sequence_number_in = (self.__sequence_number_in + 1) & xffffffff + + # check for rekey + raw_packet_size = packet_size + self.__mac_size_in + 4 + self.__received_bytes += raw_packet_size + self.__received_packets += 1 + if self.__need_rekey: + # we've asked to rekey -- give them some packets to comply before + # dropping the connection + self.__received_bytes_overflow += raw_packet_size + self.__received_packets_overflow += 1 + if (self.__received_packets_overflow >= self.REKEY_PACKETS_OVERFLOW_MAX) or \ + (self.__received_bytes_overflow >= self.REKEY_BYTES_OVERFLOW_MAX): + raise SSHException('Remote transport is ignoring rekey requests') + elif (self.__received_packets >= self.REKEY_PACKETS) or \ + (self.__received_bytes >= self.REKEY_BYTES): + # only ask once for rekeying + self._log(DEBUG, 'Rekeying (hit %d packets, %d bytes received)' % + (self.__received_packets, self.__received_bytes)) + self.__received_bytes_overflow = 0 + self.__received_packets_overflow = 0 + self._trigger_rekey() + + cmd = byte_ord(payload[0]) + if cmd in MSG_NAMES: + cmd_name = MSG_NAMES[cmd] + else: + cmd_name = '$%x' % cmd + if self.__dump_packets: + self._log(DEBUG, 'Read packet <%s>, length %d' % (cmd_name, len(payload))) + return cmd, msg + + ########## protected + + def _log(self, level, msg): + if self.__logger is None: + return + if issubclass(type(msg), list): + for m in msg: + self.__logger.log(level, m) + else: + self.__logger.log(level, msg) + + def _check_keepalive(self): + if (not self.__keepalive_interval) or (not self.__block_engine_out) or \ + self.__need_rekey: + # wait till we're encrypting, and not in the middle of rekeying + return + now = time.time() + if now > self.__keepalive_last + self.__keepalive_interval: + self.__keepalive_callback() + self.__keepalive_last = now + + def _read_timeout(self, timeout): + start = time.time() + while True: + try: + x = self.__socket.recv(128) + if len(x) == 0: + raise EOFError() + break + except socket.timeout: + pass + except EnvironmentError as e: + if (type(e.args) is tuple and len(e.args) > 0 and + e.args[0] == errno.EINTR): + pass + else: + raise + if self.__closed: + raise EOFError() + now = time.time() + if now - start >= timeout: + raise socket.timeout() + return x + + def _build_packet(self, payload): + # pad up at least 4 bytes, to nearest block-size (usually 8) + bsize = self.__block_size_out + padding = 3 + bsize - ((len(payload) + 8) % bsize) + packet = struct.pack('>IB', len(payload) + padding + 1, padding) + packet += payload + if self.__sdctr_out or self.__block_engine_out is None: + # cute trick i caught openssh doing: if we're not encrypting or SDCTR mode (RFC4344), + # don't waste random bytes for the padding + packet += (zero_byte * padding) + else: + packet += os.urandom(padding) + return packet + + def _trigger_rekey(self): + # outside code should check for this flag + self.__need_rekey = True diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/pipe.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/pipe.py new file mode 100644 index 0000000..b0cfcf2 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/pipe.py @@ -0,0 +1,148 @@ +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +Abstraction of a one-way pipe where the read end can be used in +`select.select`. Normally this is trivial, but Windows makes it nearly +impossible. + +The pipe acts like an Event, which can be set or cleared. When set, the pipe +will trigger as readable in `select `. +""" + +import sys +import os +import socket + + +def make_pipe(): + if sys.platform[:3] != 'win': + p = PosixPipe() + else: + p = WindowsPipe() + return p + + +class PosixPipe (object): + def __init__(self): + self._rfd, self._wfd = os.pipe() + self._set = False + self._forever = False + self._closed = False + + def close(self): + os.close(self._rfd) + os.close(self._wfd) + # used for unit tests: + self._closed = True + + def fileno(self): + return self._rfd + + def clear(self): + if not self._set or self._forever: + return + os.read(self._rfd, 1) + self._set = False + + def set(self): + if self._set or self._closed: + return + self._set = True + os.write(self._wfd, b'*') + + def set_forever(self): + self._forever = True + self.set() + + +class WindowsPipe (object): + """ + On Windows, only an OS-level "WinSock" may be used in select(), but reads + and writes must be to the actual socket object. + """ + def __init__(self): + serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + serv.bind(('127.0.0.1', 0)) + serv.listen(1) + + # need to save sockets in _rsock/_wsock so they don't get closed + self._rsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self._rsock.connect(('127.0.0.1', serv.getsockname()[1])) + + self._wsock, addr = serv.accept() + serv.close() + self._set = False + self._forever = False + self._closed = False + + def close(self): + self._rsock.close() + self._wsock.close() + # used for unit tests: + self._closed = True + + def fileno(self): + return self._rsock.fileno() + + def clear (self): + if not self._set or self._forever: + return + self._rsock.recv(1) + self._set = False + + def set (self): + if self._set or self._closed: + return + self._set = True + self._wsock.send(b'*') + + def set_forever (self): + self._forever = True + self.set() + + +class OrPipe (object): + def __init__(self, pipe): + self._set = False + self._partner = None + self._pipe = pipe + + def set(self): + self._set = True + if not self._partner._set: + self._pipe.set() + + def clear(self): + self._set = False + if not self._partner._set: + self._pipe.clear() + + +def make_or_pipe(pipe): + """ + wraps a pipe into two pipe-like objects which are "or"d together to + affect the real pipe. if either returned pipe is set, the wrapped pipe + is set. when both are cleared, the wrapped pipe is cleared. + """ + p1 = OrPipe(pipe) + p2 = OrPipe(pipe) + p1._partner = p2 + p2._partner = p1 + return p1, p2 + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/pkey.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/pkey.py new file mode 100644 index 0000000..373563f --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/pkey.py @@ -0,0 +1,350 @@ +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +Common API for all public keys. +""" + +import base64 +from binascii import hexlify, unhexlify +import os +from hashlib import md5 + +from Crypto.Cipher import DES3, AES + +from paramiko import util +from paramiko.common import o600, zero_byte +from paramiko.py3compat import u, encodebytes, decodebytes, b +from paramiko.ssh_exception import SSHException, PasswordRequiredException + + +class PKey (object): + """ + Base class for public keys. + """ + + # known encryption types for private key files: + _CIPHER_TABLE = { + 'AES-128-CBC': {'cipher': AES, 'keysize': 16, 'blocksize': 16, 'mode': AES.MODE_CBC}, + 'DES-EDE3-CBC': {'cipher': DES3, 'keysize': 24, 'blocksize': 8, 'mode': DES3.MODE_CBC}, + } + + def __init__(self, msg=None, data=None): + """ + Create a new instance of this public key type. If ``msg`` is given, + the key's public part(s) will be filled in from the message. If + ``data`` is given, the key's public part(s) will be filled in from + the string. + + :param .Message msg: + an optional SSH `.Message` containing a public key of this type. + :param str data: an optional string containing a public key of this type + + :raises SSHException: + if a key cannot be created from the ``data`` or ``msg`` given, or + no key was passed in. + """ + pass + + def asbytes(self): + """ + Return a string of an SSH `.Message` made up of the public part(s) of + this key. This string is suitable for passing to `__init__` to + re-create the key object later. + """ + return bytes() + + def __str__(self): + return self.asbytes() + + # noinspection PyUnresolvedReferences + def __cmp__(self, other): + """ + Compare this key to another. Returns 0 if this key is equivalent to + the given key, or non-0 if they are different. Only the public parts + of the key are compared, so a public key will compare equal to its + corresponding private key. + + :param .Pkey other: key to compare to. + """ + hs = hash(self) + ho = hash(other) + if hs != ho: + return cmp(hs, ho) + return cmp(self.asbytes(), other.asbytes()) + + def __eq__(self, other): + return hash(self) == hash(other) + + def get_name(self): + """ + Return the name of this private key implementation. + + :return: + name of this private key type, in SSH terminology, as a `str` (for + example, ``"ssh-rsa"``). + """ + return '' + + def get_bits(self): + """ + Return the number of significant bits in this key. This is useful + for judging the relative security of a key. + + :return: bits in the key (as an `int`) + """ + return 0 + + def can_sign(self): + """ + Return ``True`` if this key has the private part necessary for signing + data. + """ + return False + + def get_fingerprint(self): + """ + Return an MD5 fingerprint of the public part of this key. Nothing + secret is revealed. + + :return: + a 16-byte `string ` (binary) of the MD5 fingerprint, in SSH + format. + """ + return md5(self.asbytes()).digest() + + def get_base64(self): + """ + Return a base64 string containing the public part of this key. Nothing + secret is revealed. This format is compatible with that used to store + public key files or recognized host keys. + + :return: a base64 `string ` containing the public part of the key. + """ + return u(encodebytes(self.asbytes())).replace('\n', '') + + def sign_ssh_data(self, data): + """ + Sign a blob of data with this private key, and return a `.Message` + representing an SSH signature message. + + :param str data: the data to sign. + :return: an SSH signature `message <.Message>`. + """ + return bytes() + + def verify_ssh_sig(self, data, msg): + """ + Given a blob of data, and an SSH message representing a signature of + that data, verify that it was signed with this key. + + :param str data: the data that was signed. + :param .Message msg: an SSH signature message + :return: + ``True`` if the signature verifies correctly; ``False`` otherwise. + """ + return False + + def from_private_key_file(cls, filename, password=None): + """ + Create a key object by reading a private key file. If the private + key is encrypted and ``password`` is not ``None``, the given password + will be used to decrypt the key (otherwise `.PasswordRequiredException` + is thrown). Through the magic of Python, this factory method will + exist in all subclasses of PKey (such as `.RSAKey` or `.DSSKey`), but + is useless on the abstract PKey class. + + :param str filename: name of the file to read + :param str password: an optional password to use to decrypt the key file, + if it's encrypted + :return: a new `.PKey` based on the given private key + + :raises IOError: if there was an error reading the file + :raises PasswordRequiredException: if the private key file is + encrypted, and ``password`` is ``None`` + :raises SSHException: if the key file is invalid + """ + key = cls(filename=filename, password=password) + return key + from_private_key_file = classmethod(from_private_key_file) + + def from_private_key(cls, file_obj, password=None): + """ + Create a key object by reading a private key from a file (or file-like) + object. If the private key is encrypted and ``password`` is not ``None``, + the given password will be used to decrypt the key (otherwise + `.PasswordRequiredException` is thrown). + + :param file file_obj: the file to read from + :param str password: + an optional password to use to decrypt the key, if it's encrypted + :return: a new `.PKey` based on the given private key + + :raises IOError: if there was an error reading the key + :raises PasswordRequiredException: if the private key file is encrypted, + and ``password`` is ``None`` + :raises SSHException: if the key file is invalid + """ + key = cls(file_obj=file_obj, password=password) + return key + from_private_key = classmethod(from_private_key) + + def write_private_key_file(self, filename, password=None): + """ + Write private key contents into a file. If the password is not + ``None``, the key is encrypted before writing. + + :param str filename: name of the file to write + :param str password: + an optional password to use to encrypt the key file + + :raises IOError: if there was an error writing the file + :raises SSHException: if the key is invalid + """ + raise Exception('Not implemented in PKey') + + def write_private_key(self, file_obj, password=None): + """ + Write private key contents into a file (or file-like) object. If the + password is not ``None``, the key is encrypted before writing. + + :param file file_obj: the file object to write into + :param str password: an optional password to use to encrypt the key + + :raises IOError: if there was an error writing to the file + :raises SSHException: if the key is invalid + """ + raise Exception('Not implemented in PKey') + + def _read_private_key_file(self, tag, filename, password=None): + """ + Read an SSH2-format private key file, looking for a string of the type + ``"BEGIN xxx PRIVATE KEY"`` for some ``xxx``, base64-decode the text we + find, and return it as a string. If the private key is encrypted and + ``password`` is not ``None``, the given password will be used to decrypt + the key (otherwise `.PasswordRequiredException` is thrown). + + :param str tag: ``"RSA"`` or ``"DSA"``, the tag used to mark the data block. + :param str filename: name of the file to read. + :param str password: + an optional password to use to decrypt the key file, if it's + encrypted. + :return: data blob (`str`) that makes up the private key. + + :raises IOError: if there was an error reading the file. + :raises PasswordRequiredException: if the private key file is + encrypted, and ``password`` is ``None``. + :raises SSHException: if the key file is invalid. + """ + with open(filename, 'r') as f: + data = self._read_private_key(tag, f, password) + return data + + def _read_private_key(self, tag, f, password=None): + lines = f.readlines() + start = 0 + while (start < len(lines)) and (lines[start].strip() != '-----BEGIN ' + tag + ' PRIVATE KEY-----'): + start += 1 + if start >= len(lines): + raise SSHException('not a valid ' + tag + ' private key file') + # parse any headers first + headers = {} + start += 1 + while start < len(lines): + l = lines[start].split(': ') + if len(l) == 1: + break + headers[l[0].lower()] = l[1].strip() + start += 1 + # find end + end = start + while (lines[end].strip() != '-----END ' + tag + ' PRIVATE KEY-----') and (end < len(lines)): + end += 1 + # if we trudged to the end of the file, just try to cope. + try: + data = decodebytes(b(''.join(lines[start:end]))) + except base64.binascii.Error as e: + raise SSHException('base64 decoding error: ' + str(e)) + if 'proc-type' not in headers: + # unencryped: done + return data + # encrypted keyfile: will need a password + if headers['proc-type'] != '4,ENCRYPTED': + raise SSHException('Unknown private key structure "%s"' % headers['proc-type']) + try: + encryption_type, saltstr = headers['dek-info'].split(',') + except: + raise SSHException("Can't parse DEK-info in private key file") + if encryption_type not in self._CIPHER_TABLE: + raise SSHException('Unknown private key cipher "%s"' % encryption_type) + # if no password was passed in, raise an exception pointing out that we need one + if password is None: + raise PasswordRequiredException('Private key file is encrypted') + cipher = self._CIPHER_TABLE[encryption_type]['cipher'] + keysize = self._CIPHER_TABLE[encryption_type]['keysize'] + mode = self._CIPHER_TABLE[encryption_type]['mode'] + salt = unhexlify(b(saltstr)) + key = util.generate_key_bytes(md5, salt, password, keysize) + return cipher.new(key, mode, salt).decrypt(data) + + def _write_private_key_file(self, tag, filename, data, password=None): + """ + Write an SSH2-format private key file in a form that can be read by + paramiko or openssh. If no password is given, the key is written in + a trivially-encoded format (base64) which is completely insecure. If + a password is given, DES-EDE3-CBC is used. + + :param str tag: ``"RSA"`` or ``"DSA"``, the tag used to mark the data block. + :param file filename: name of the file to write. + :param str data: data blob that makes up the private key. + :param str password: an optional password to use to encrypt the file. + + :raises IOError: if there was an error writing the file. + """ + with open(filename, 'w', o600) as f: + # grrr... the mode doesn't always take hold + os.chmod(filename, o600) + self._write_private_key(tag, f, data, password) + + def _write_private_key(self, tag, f, data, password=None): + f.write('-----BEGIN %s PRIVATE KEY-----\n' % tag) + if password is not None: + # since we only support one cipher here, use it + cipher_name = list(self._CIPHER_TABLE.keys())[0] + cipher = self._CIPHER_TABLE[cipher_name]['cipher'] + keysize = self._CIPHER_TABLE[cipher_name]['keysize'] + blocksize = self._CIPHER_TABLE[cipher_name]['blocksize'] + mode = self._CIPHER_TABLE[cipher_name]['mode'] + salt = os.urandom(16) + key = util.generate_key_bytes(md5, salt, password, keysize) + if len(data) % blocksize != 0: + n = blocksize - len(data) % blocksize + #data += os.urandom(n) + # that would make more sense ^, but it confuses openssh. + data += zero_byte * n + data = cipher.new(key, mode, salt).encrypt(data) + f.write('Proc-Type: 4,ENCRYPTED\n') + f.write('DEK-Info: %s,%s\n' % (cipher_name, u(hexlify(salt)).upper())) + f.write('\n') + s = u(encodebytes(data)) + # re-wrap to 64-char lines + s = ''.join(s.split('\n')) + s = '\n'.join([s[i: i + 64] for i in range(0, len(s), 64)]) + f.write(s) + f.write('\n') + f.write('-----END %s PRIVATE KEY-----\n' % tag) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/primes.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/primes.py new file mode 100644 index 0000000..8e02e80 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/primes.py @@ -0,0 +1,132 @@ +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +Utility functions for dealing with primes. +""" + +import os + +from paramiko import util +from paramiko.py3compat import byte_mask, long +from paramiko.ssh_exception import SSHException + + +def _roll_random(n): + """returns a random # from 0 to N-1""" + bits = util.bit_length(n - 1) + byte_count = (bits + 7) // 8 + hbyte_mask = pow(2, bits % 8) - 1 + + # so here's the plan: + # we fetch as many random bits as we'd need to fit N-1, and if the + # generated number is >= N, we try again. in the worst case (N-1 is a + # power of 2), we have slightly better than 50% odds of getting one that + # fits, so i can't guarantee that this loop will ever finish, but the odds + # of it looping forever should be infinitesimal. + while True: + x = os.urandom(byte_count) + if hbyte_mask > 0: + x = byte_mask(x[0], hbyte_mask) + x[1:] + num = util.inflate_long(x, 1) + if num < n: + break + return num + + +class ModulusPack (object): + """ + convenience object for holding the contents of the /etc/ssh/moduli file, + on systems that have such a file. + """ + + def __init__(self): + # pack is a hash of: bits -> [ (generator, modulus) ... ] + self.pack = {} + self.discarded = [] + + def _parse_modulus(self, line): + timestamp, mod_type, tests, tries, size, generator, modulus = line.split() + mod_type = int(mod_type) + tests = int(tests) + tries = int(tries) + size = int(size) + generator = int(generator) + modulus = long(modulus, 16) + + # weed out primes that aren't at least: + # type 2 (meets basic structural requirements) + # test 4 (more than just a small-prime sieve) + # tries < 100 if test & 4 (at least 100 tries of miller-rabin) + if (mod_type < 2) or (tests < 4) or ((tests & 4) and (tests < 8) and (tries < 100)): + self.discarded.append((modulus, 'does not meet basic requirements')) + return + if generator == 0: + generator = 2 + + # there's a bug in the ssh "moduli" file (yeah, i know: shock! dismay! + # call cnn!) where it understates the bit lengths of these primes by 1. + # this is okay. + bl = util.bit_length(modulus) + if (bl != size) and (bl != size + 1): + self.discarded.append((modulus, 'incorrectly reported bit length %d' % size)) + return + if bl not in self.pack: + self.pack[bl] = [] + self.pack[bl].append((generator, modulus)) + + def read_file(self, filename): + """ + :raises IOError: passed from any file operations that fail. + """ + self.pack = {} + with open(filename, 'r') as f: + for line in f: + line = line.strip() + if (len(line) == 0) or (line[0] == '#'): + continue + try: + self._parse_modulus(line) + except: + continue + + def get_modulus(self, min, prefer, max): + bitsizes = sorted(self.pack.keys()) + if len(bitsizes) == 0: + raise SSHException('no moduli available') + good = -1 + # find nearest bitsize >= preferred + for b in bitsizes: + if (b >= prefer) and (b < max) and (b < good or good == -1): + good = b + # if that failed, find greatest bitsize >= min + if good == -1: + for b in bitsizes: + if (b >= min) and (b < max) and (b > good): + good = b + if good == -1: + # their entire (min, max) range has no intersection with our range. + # if their range is below ours, pick the smallest. otherwise pick + # the largest. it'll be out of their range requirement either way, + # but we'll be sending them the closest one we have. + good = bitsizes[0] + if min > good: + good = bitsizes[-1] + # now pick a random modulus of this bitsize + n = _roll_random(len(self.pack[good])) + return self.pack[good][n] diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/proxy.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/proxy.py new file mode 100644 index 0000000..8959b24 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/proxy.py @@ -0,0 +1,105 @@ +# Copyright (C) 2012 Yipit, Inc +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distrubuted in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + + +from datetime import datetime +import os +from shlex import split as shlsplit +import signal +from subprocess import Popen, PIPE +from select import select +import socket + +from paramiko.ssh_exception import ProxyCommandFailure + + +class ProxyCommand(object): + """ + Wraps a subprocess running ProxyCommand-driven programs. + + This class implements a the socket-like interface needed by the + `.Transport` and `.Packetizer` classes. Using this class instead of a + regular socket makes it possible to talk with a Popen'd command that will + proxy traffic between the client and a server hosted in another machine. + """ + def __init__(self, command_line): + """ + Create a new CommandProxy instance. The instance created by this + class can be passed as an argument to the `.Transport` class. + + :param str command_line: + the command that should be executed and used as the proxy. + """ + self.cmd = shlsplit(command_line) + self.process = Popen(self.cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) + self.timeout = None + self.buffer = [] + + def send(self, content): + """ + Write the content received from the SSH client to the standard + input of the forked command. + + :param str content: string to be sent to the forked command + """ + try: + self.process.stdin.write(content) + except IOError as e: + # There was a problem with the child process. It probably + # died and we can't proceed. The best option here is to + # raise an exception informing the user that the informed + # ProxyCommand is not working. + raise ProxyCommandFailure(' '.join(self.cmd), e.strerror) + return len(content) + + def recv(self, size): + """ + Read from the standard output of the forked program. + + :param int size: how many chars should be read + + :return: the length of the read content, as an `int` + """ + try: + start = datetime.now() + while len(self.buffer) < size: + if self.timeout is not None: + elapsed = (datetime.now() - start).microseconds + timeout = self.timeout * 1000 * 1000 # to microseconds + if elapsed >= timeout: + raise socket.timeout() + r, w, x = select([self.process.stdout], [], [], 0.0) + if r and r[0] == self.process.stdout: + b = os.read(self.process.stdout.fileno(), 1) + # Store in class-level buffer for persistence across + # timeouts; this makes us act more like a real socket + # (where timeouts don't actually drop data.) + self.buffer.append(b) + result = ''.join(self.buffer) + self.buffer = [] + return result + except socket.timeout: + raise # socket.timeout is a subclass of IOError + except IOError as e: + raise ProxyCommandFailure(' '.join(self.cmd), e.strerror) + + def close(self): + os.kill(self.process.pid, signal.SIGTERM) + + def settimeout(self, timeout): + self.timeout = timeout diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/py3compat.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/py3compat.py new file mode 100644 index 0000000..8842b98 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/py3compat.py @@ -0,0 +1,162 @@ +import sys +import base64 + +__all__ = ['PY2', 'string_types', 'integer_types', 'text_type', 'bytes_types', 'bytes', 'long', 'input', + 'decodebytes', 'encodebytes', 'bytestring', 'byte_ord', 'byte_chr', 'byte_mask', + 'b', 'u', 'b2s', 'StringIO', 'BytesIO', 'is_callable', 'MAXSIZE', 'next'] + +PY2 = sys.version_info[0] < 3 + +if PY2: + string_types = basestring + text_type = unicode + bytes_types = str + bytes = str + integer_types = (int, long) + long = long + input = raw_input + decodebytes = base64.decodestring + encodebytes = base64.encodestring + + + def bytestring(s): # NOQA + if isinstance(s, unicode): + return s.encode('utf-8') + return s + + + byte_ord = ord # NOQA + byte_chr = chr # NOQA + + + def byte_mask(c, mask): + return chr(ord(c) & mask) + + + def b(s, encoding='utf8'): # NOQA + """cast unicode or bytes to bytes""" + if isinstance(s, str): + return s + elif isinstance(s, unicode): + return s.encode(encoding) + else: + raise TypeError("Expected unicode or bytes, got %r" % s) + + + def u(s, encoding='utf8'): # NOQA + """cast bytes or unicode to unicode""" + if isinstance(s, str): + return s.decode(encoding) + elif isinstance(s, unicode): + return s + else: + raise TypeError("Expected unicode or bytes, got %r" % s) + + + def b2s(s): + return s + + + try: + import cStringIO + + StringIO = cStringIO.StringIO # NOQA + except ImportError: + import StringIO + + StringIO = StringIO.StringIO # NOQA + + BytesIO = StringIO + + + def is_callable(c): # NOQA + return callable(c) + + + def get_next(c): # NOQA + return c.next + + + def next(c): + return c.next() + + # It's possible to have sizeof(long) != sizeof(Py_ssize_t). + class X(object): + def __len__(self): + return 1 << 31 + + + try: + len(X()) + except OverflowError: + # 32-bit + MAXSIZE = int((1 << 31) - 1) # NOQA + else: + # 64-bit + MAXSIZE = int((1 << 63) - 1) # NOQA + del X +else: + import collections + import struct + string_types = str + text_type = str + bytes = bytes + bytes_types = bytes + integer_types = int + class long(int): + pass + input = input + decodebytes = base64.decodebytes + encodebytes = base64.encodebytes + + def bytestring(s): + return s + + def byte_ord(c): + # In case we're handed a string instead of an int. + if not isinstance(c, int): + c = ord(c) + return c + + def byte_chr(c): + assert isinstance(c, int) + return struct.pack('B', c) + + def byte_mask(c, mask): + assert isinstance(c, int) + return struct.pack('B', c & mask) + + def b(s, encoding='utf8'): + """cast unicode or bytes to bytes""" + if isinstance(s, bytes): + return s + elif isinstance(s, str): + return s.encode(encoding) + else: + raise TypeError("Expected unicode or bytes, got %r" % s) + + def u(s, encoding='utf8'): + """cast bytes or unicode to unicode""" + if isinstance(s, bytes): + return s.decode(encoding) + elif isinstance(s, str): + return s + else: + raise TypeError("Expected unicode or bytes, got %r" % s) + + def b2s(s): + return s.decode() if isinstance(s, bytes) else s + + import io + StringIO = io.StringIO # NOQA + BytesIO = io.BytesIO # NOQA + + def is_callable(c): + return isinstance(c, collections.Callable) + + def get_next(c): + return c.__next__ + + next = next + + MAXSIZE = sys.maxsize # NOQA diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/resource.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/resource.py new file mode 100644 index 0000000..9809afb --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/resource.py @@ -0,0 +1,71 @@ +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +Resource manager. +""" + +import weakref + + +class ResourceManager (object): + """ + A registry of objects and resources that should be closed when those + objects are deleted. + + This is meant to be a safer alternative to Python's ``__del__`` method, + which can cause reference cycles to never be collected. Objects registered + with the ResourceManager can be collected but still free resources when + they die. + + Resources are registered using `register`, and when an object is garbage + collected, each registered resource is closed by having its ``close()`` + method called. Multiple resources may be registered per object, but a + resource will only be closed once, even if multiple objects register it. + (The last object to register it wins.) + """ + + def __init__(self): + self._table = {} + + def register(self, obj, resource): + """ + Register a resource to be closed with an object is collected. + + When the given ``obj`` is garbage-collected by the Python interpreter, + the ``resource`` will be closed by having its ``close()`` method called. + Any exceptions are ignored. + + :param object obj: the object to track + :param object resource: + the resource to close when the object is collected + """ + def callback(ref): + try: + resource.close() + except: + pass + del self._table[id(resource)] + + # keep the weakref in a table so it sticks around long enough to get + # its callback called. :) + self._table[id(resource)] = weakref.ref(obj, callback) + + +# singleton +ResourceManager = ResourceManager() diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/rsakey.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/rsakey.py new file mode 100644 index 0000000..d1f3ecf --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/rsakey.py @@ -0,0 +1,187 @@ +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +RSA keys. +""" + +import os +from hashlib import sha1 + +from Crypto.PublicKey import RSA + +from paramiko import util +from paramiko.common import max_byte, zero_byte, one_byte +from paramiko.message import Message +from paramiko.ber import BER, BERException +from paramiko.pkey import PKey +from paramiko.py3compat import long +from paramiko.ssh_exception import SSHException + +SHA1_DIGESTINFO = b'\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14' + + +class RSAKey (PKey): + """ + Representation of an RSA key which can be used to sign and verify SSH2 + data. + """ + + def __init__(self, msg=None, data=None, filename=None, password=None, vals=None, file_obj=None): + self.n = None + self.e = None + self.d = None + self.p = None + self.q = None + if file_obj is not None: + self._from_private_key(file_obj, password) + return + if filename is not None: + self._from_private_key_file(filename, password) + return + if (msg is None) and (data is not None): + msg = Message(data) + if vals is not None: + self.e, self.n = vals + else: + if msg is None: + raise SSHException('Key object may not be empty') + if msg.get_text() != 'ssh-rsa': + raise SSHException('Invalid key') + self.e = msg.get_mpint() + self.n = msg.get_mpint() + self.size = util.bit_length(self.n) + + def asbytes(self): + m = Message() + m.add_string('ssh-rsa') + m.add_mpint(self.e) + m.add_mpint(self.n) + return m.asbytes() + + def __str__(self): + return self.asbytes() + + def __hash__(self): + h = hash(self.get_name()) + h = h * 37 + hash(self.e) + h = h * 37 + hash(self.n) + return hash(h) + + def get_name(self): + return 'ssh-rsa' + + def get_bits(self): + return self.size + + def can_sign(self): + return self.d is not None + + def sign_ssh_data(self, data): + digest = sha1(data).digest() + rsa = RSA.construct((long(self.n), long(self.e), long(self.d))) + sig = util.deflate_long(rsa.sign(self._pkcs1imify(digest), bytes())[0], 0) + m = Message() + m.add_string('ssh-rsa') + m.add_string(sig) + return m + + def verify_ssh_sig(self, data, msg): + if msg.get_text() != 'ssh-rsa': + return False + sig = util.inflate_long(msg.get_binary(), True) + # verify the signature by SHA'ing the data and encrypting it using the + # public key. some wackiness ensues where we "pkcs1imify" the 20-byte + # hash into a string as long as the RSA key. + hash_obj = util.inflate_long(self._pkcs1imify(sha1(data).digest()), True) + rsa = RSA.construct((long(self.n), long(self.e))) + return rsa.verify(hash_obj, (sig,)) + + def _encode_key(self): + if (self.p is None) or (self.q is None): + raise SSHException('Not enough key info to write private key file') + keylist = [0, self.n, self.e, self.d, self.p, self.q, + self.d % (self.p - 1), self.d % (self.q - 1), + util.mod_inverse(self.q, self.p)] + try: + b = BER() + b.encode(keylist) + except BERException: + raise SSHException('Unable to create ber encoding of key') + return b.asbytes() + + def write_private_key_file(self, filename, password=None): + self._write_private_key_file('RSA', filename, self._encode_key(), password) + + def write_private_key(self, file_obj, password=None): + self._write_private_key('RSA', file_obj, self._encode_key(), password) + + def generate(bits, progress_func=None): + """ + Generate a new private RSA key. This factory function can be used to + generate a new host key or authentication key. + + :param int bits: number of bits the generated key should be. + :param function progress_func: + an optional function to call at key points in key generation (used + by ``pyCrypto.PublicKey``). + :return: new `.RSAKey` private key + """ + rsa = RSA.generate(bits, os.urandom, progress_func) + key = RSAKey(vals=(rsa.e, rsa.n)) + key.d = rsa.d + key.p = rsa.p + key.q = rsa.q + return key + generate = staticmethod(generate) + + ### internals... + + def _pkcs1imify(self, data): + """ + turn a 20-byte SHA1 hash into a blob of data as large as the key's N, + using PKCS1's \"emsa-pkcs1-v1_5\" encoding. totally bizarre. + """ + size = len(util.deflate_long(self.n, 0)) + filler = max_byte * (size - len(SHA1_DIGESTINFO) - len(data) - 3) + return zero_byte + one_byte + filler + zero_byte + SHA1_DIGESTINFO + data + + def _from_private_key_file(self, filename, password): + data = self._read_private_key_file('RSA', filename, password) + self._decode_key(data) + + def _from_private_key(self, file_obj, password): + data = self._read_private_key('RSA', file_obj, password) + self._decode_key(data) + + def _decode_key(self, data): + # private key file contains: + # RSAPrivateKey = { version = 0, n, e, d, p, q, d mod p-1, d mod q-1, q**-1 mod p } + try: + keylist = BER(data).decode() + except BERException: + raise SSHException('Unable to parse key file') + if (type(keylist) is not list) or (len(keylist) < 4) or (keylist[0] != 0): + raise SSHException('Not a valid RSA private key file (bad ber encoding)') + self.n = keylist[1] + self.e = keylist[2] + self.d = keylist[3] + # not really needed + self.p = keylist[4] + self.q = keylist[5] + self.size = util.bit_length(self.n) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/server.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/server.py new file mode 100644 index 0000000..496cd60 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/server.py @@ -0,0 +1,619 @@ +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +`.ServerInterface` is an interface to override for server support. +""" + +import threading +from paramiko import util +from paramiko.common import DEBUG, ERROR, OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED, AUTH_FAILED +from paramiko.py3compat import string_types + + +class ServerInterface (object): + """ + This class defines an interface for controlling the behavior of Paramiko + in server mode. + + Methods on this class are called from Paramiko's primary thread, so you + shouldn't do too much work in them. (Certainly nothing that blocks or + sleeps.) + """ + + def check_channel_request(self, kind, chanid): + """ + Determine if a channel request of a given type will be granted, and + return ``OPEN_SUCCEEDED`` or an error code. This method is + called in server mode when the client requests a channel, after + authentication is complete. + + If you allow channel requests (and an ssh server that didn't would be + useless), you should also override some of the channel request methods + below, which are used to determine which services will be allowed on + a given channel: + + - `check_channel_pty_request` + - `check_channel_shell_request` + - `check_channel_subsystem_request` + - `check_channel_window_change_request` + - `check_channel_x11_request` + - `check_channel_forward_agent_request` + + The ``chanid`` parameter is a small number that uniquely identifies the + channel within a `.Transport`. A `.Channel` object is not created + unless this method returns ``OPEN_SUCCEEDED`` -- once a + `.Channel` object is created, you can call `.Channel.get_id` to + retrieve the channel ID. + + The return value should either be ``OPEN_SUCCEEDED`` (or + ``0``) to allow the channel request, or one of the following error + codes to reject it: + + - ``OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED`` + - ``OPEN_FAILED_CONNECT_FAILED`` + - ``OPEN_FAILED_UNKNOWN_CHANNEL_TYPE`` + - ``OPEN_FAILED_RESOURCE_SHORTAGE`` + + The default implementation always returns + ``OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED``. + + :param str kind: + the kind of channel the client would like to open (usually + ``"session"``). + :param int chanid: ID of the channel + :return: an `int` success or failure code (listed above) + """ + return OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED + + def get_allowed_auths(self, username): + """ + Return a list of authentication methods supported by the server. + This list is sent to clients attempting to authenticate, to inform them + of authentication methods that might be successful. + + The "list" is actually a string of comma-separated names of types of + authentication. Possible values are ``"password"``, ``"publickey"``, + and ``"none"``. + + The default implementation always returns ``"password"``. + + :param str username: the username requesting authentication. + :return: a comma-separated `str` of authentication types + """ + return 'password' + + def check_auth_none(self, username): + """ + Determine if a client may open channels with no (further) + authentication. + + Return `.AUTH_FAILED` if the client must authenticate, or + `.AUTH_SUCCESSFUL` if it's okay for the client to not + authenticate. + + The default implementation always returns `.AUTH_FAILED`. + + :param str username: the username of the client. + :return: + `.AUTH_FAILED` if the authentication fails; `.AUTH_SUCCESSFUL` if + it succeeds. + :rtype: int + """ + return AUTH_FAILED + + def check_auth_password(self, username, password): + """ + Determine if a given username and password supplied by the client is + acceptable for use in authentication. + + Return `.AUTH_FAILED` if the password is not accepted, + `.AUTH_SUCCESSFUL` if the password is accepted and completes + the authentication, or `.AUTH_PARTIALLY_SUCCESSFUL` if your + authentication is stateful, and this key is accepted for + authentication, but more authentication is required. (In this latter + case, `get_allowed_auths` will be called to report to the client what + options it has for continuing the authentication.) + + The default implementation always returns `.AUTH_FAILED`. + + :param str username: the username of the authenticating client. + :param str password: the password given by the client. + :return: + `.AUTH_FAILED` if the authentication fails; `.AUTH_SUCCESSFUL` if + it succeeds; `.AUTH_PARTIALLY_SUCCESSFUL` if the password auth is + successful, but authentication must continue. + :rtype: int + """ + return AUTH_FAILED + + def check_auth_publickey(self, username, key): + """ + Determine if a given key supplied by the client is acceptable for use + in authentication. You should override this method in server mode to + check the username and key and decide if you would accept a signature + made using this key. + + Return `.AUTH_FAILED` if the key is not accepted, + `.AUTH_SUCCESSFUL` if the key is accepted and completes the + authentication, or `.AUTH_PARTIALLY_SUCCESSFUL` if your + authentication is stateful, and this password is accepted for + authentication, but more authentication is required. (In this latter + case, `get_allowed_auths` will be called to report to the client what + options it has for continuing the authentication.) + + Note that you don't have to actually verify any key signtature here. + If you're willing to accept the key, Paramiko will do the work of + verifying the client's signature. + + The default implementation always returns `.AUTH_FAILED`. + + :param str username: the username of the authenticating client + :param .PKey key: the key object provided by the client + :return: + `.AUTH_FAILED` if the client can't authenticate with this key; + `.AUTH_SUCCESSFUL` if it can; `.AUTH_PARTIALLY_SUCCESSFUL` if it + can authenticate with this key but must continue with + authentication + :rtype: int + """ + return AUTH_FAILED + + def check_auth_interactive(self, username, submethods): + """ + Begin an interactive authentication challenge, if supported. You + should override this method in server mode if you want to support the + ``"keyboard-interactive"`` auth type, which requires you to send a + series of questions for the client to answer. + + Return `.AUTH_FAILED` if this auth method isn't supported. Otherwise, + you should return an `.InteractiveQuery` object containing the prompts + and instructions for the user. The response will be sent via a call + to `check_auth_interactive_response`. + + The default implementation always returns `.AUTH_FAILED`. + + :param str username: the username of the authenticating client + :param str submethods: + a comma-separated list of methods preferred by the client (usually + empty) + :return: + `.AUTH_FAILED` if this auth method isn't supported; otherwise an + object containing queries for the user + :rtype: int or `.InteractiveQuery` + """ + return AUTH_FAILED + + def check_auth_interactive_response(self, responses): + """ + Continue or finish an interactive authentication challenge, if + supported. You should override this method in server mode if you want + to support the ``"keyboard-interactive"`` auth type. + + Return `.AUTH_FAILED` if the responses are not accepted, + `.AUTH_SUCCESSFUL` if the responses are accepted and complete + the authentication, or `.AUTH_PARTIALLY_SUCCESSFUL` if your + authentication is stateful, and this set of responses is accepted for + authentication, but more authentication is required. (In this latter + case, `get_allowed_auths` will be called to report to the client what + options it has for continuing the authentication.) + + If you wish to continue interactive authentication with more questions, + you may return an `.InteractiveQuery` object, which should cause the + client to respond with more answers, calling this method again. This + cycle can continue indefinitely. + + The default implementation always returns `.AUTH_FAILED`. + + :param list responses: list of `str` responses from the client + :return: + `.AUTH_FAILED` if the authentication fails; `.AUTH_SUCCESSFUL` if + it succeeds; `.AUTH_PARTIALLY_SUCCESSFUL` if the interactive auth + is successful, but authentication must continue; otherwise an + object containing queries for the user + :rtype: int or `.InteractiveQuery` + """ + return AUTH_FAILED + + def check_port_forward_request(self, address, port): + """ + Handle a request for port forwarding. The client is asking that + connections to the given address and port be forwarded back across + this ssh connection. An address of ``"0.0.0.0"`` indicates a global + address (any address associated with this server) and a port of ``0`` + indicates that no specific port is requested (usually the OS will pick + a port). + + The default implementation always returns ``False``, rejecting the + port forwarding request. If the request is accepted, you should return + the port opened for listening. + + :param str address: the requested address + :param int port: the requested port + :return: + the port number (`int`) that was opened for listening, or ``False`` + to reject + """ + return False + + def cancel_port_forward_request(self, address, port): + """ + The client would like to cancel a previous port-forwarding request. + If the given address and port is being forwarded across this ssh + connection, the port should be closed. + + :param str address: the forwarded address + :param int port: the forwarded port + """ + pass + + def check_global_request(self, kind, msg): + """ + Handle a global request of the given ``kind``. This method is called + in server mode and client mode, whenever the remote host makes a global + request. If there are any arguments to the request, they will be in + ``msg``. + + There aren't any useful global requests defined, aside from port + forwarding, so usually this type of request is an extension to the + protocol. + + If the request was successful and you would like to return contextual + data to the remote host, return a tuple. Items in the tuple will be + sent back with the successful result. (Note that the items in the + tuple can only be strings, ints, longs, or bools.) + + The default implementation always returns ``False``, indicating that it + does not support any global requests. + + .. note:: Port forwarding requests are handled separately, in + `check_port_forward_request`. + + :param str kind: the kind of global request being made. + :param .Message msg: any extra arguments to the request. + :return: + ``True`` or a `tuple` of data if the request was granted; ``False`` + otherwise. + """ + return False + + ### Channel requests + + def check_channel_pty_request(self, channel, term, width, height, pixelwidth, pixelheight, + modes): + """ + Determine if a pseudo-terminal of the given dimensions (usually + requested for shell access) can be provided on the given channel. + + The default implementation always returns ``False``. + + :param .Channel channel: the `.Channel` the pty request arrived on. + :param str term: type of terminal requested (for example, ``"vt100"``). + :param int width: width of screen in characters. + :param int height: height of screen in characters. + :param int pixelwidth: + width of screen in pixels, if known (may be ``0`` if unknown). + :param int pixelheight: + height of screen in pixels, if known (may be ``0`` if unknown). + :return: + ``True`` if the psuedo-terminal has been allocated; ``False`` + otherwise. + """ + return False + + def check_channel_shell_request(self, channel): + """ + Determine if a shell will be provided to the client on the given + channel. If this method returns ``True``, the channel should be + connected to the stdin/stdout of a shell (or something that acts like + a shell). + + The default implementation always returns ``False``. + + :param .Channel channel: the `.Channel` the request arrived on. + :return: + ``True`` if this channel is now hooked up to a shell; ``False`` if + a shell can't or won't be provided. + """ + return False + + def check_channel_exec_request(self, channel, command): + """ + Determine if a shell command will be executed for the client. If this + method returns ``True``, the channel should be connected to the stdin, + stdout, and stderr of the shell command. + + The default implementation always returns ``False``. + + :param .Channel channel: the `.Channel` the request arrived on. + :param str command: the command to execute. + :return: + ``True`` if this channel is now hooked up to the stdin, stdout, and + stderr of the executing command; ``False`` if the command will not + be executed. + + .. versionadded:: 1.1 + """ + return False + + def check_channel_subsystem_request(self, channel, name): + """ + Determine if a requested subsystem will be provided to the client on + the given channel. If this method returns ``True``, all future I/O + through this channel will be assumed to be connected to the requested + subsystem. An example of a subsystem is ``sftp``. + + The default implementation checks for a subsystem handler assigned via + `.Transport.set_subsystem_handler`. + If one has been set, the handler is invoked and this method returns + ``True``. Otherwise it returns ``False``. + + .. note:: Because the default implementation uses the `.Transport` to + identify valid subsystems, you probably won't need to override this + method. + + :param .Channel channel: the `.Channel` the pty request arrived on. + :param str name: name of the requested subsystem. + :return: + ``True`` if this channel is now hooked up to the requested + subsystem; ``False`` if that subsystem can't or won't be provided. + """ + handler_class, larg, kwarg = channel.get_transport()._get_subsystem_handler(name) + if handler_class is None: + return False + handler = handler_class(channel, name, self, *larg, **kwarg) + handler.start() + return True + + def check_channel_window_change_request(self, channel, width, height, pixelwidth, pixelheight): + """ + Determine if the pseudo-terminal on the given channel can be resized. + This only makes sense if a pty was previously allocated on it. + + The default implementation always returns ``False``. + + :param .Channel channel: the `.Channel` the pty request arrived on. + :param int width: width of screen in characters. + :param int height: height of screen in characters. + :param int pixelwidth: + width of screen in pixels, if known (may be ``0`` if unknown). + :param int pixelheight: + height of screen in pixels, if known (may be ``0`` if unknown). + :return: ``True`` if the terminal was resized; ``False`` if not. + """ + return False + + def check_channel_x11_request(self, channel, single_connection, auth_protocol, auth_cookie, screen_number): + """ + Determine if the client will be provided with an X11 session. If this + method returns ``True``, X11 applications should be routed through new + SSH channels, using `.Transport.open_x11_channel`. + + The default implementation always returns ``False``. + + :param .Channel channel: the `.Channel` the X11 request arrived on + :param bool single_connection: + ``True`` if only a single X11 channel should be opened, else + ``False``. + :param str auth_protocol: the protocol used for X11 authentication + :param str auth_cookie: the cookie used to authenticate to X11 + :param int screen_number: the number of the X11 screen to connect to + :return: ``True`` if the X11 session was opened; ``False`` if not + """ + return False + + def check_channel_forward_agent_request(self, channel): + """ + Determine if the client will be provided with an forward agent session. + If this method returns ``True``, the server will allow SSH Agent + forwarding. + + The default implementation always returns ``False``. + + :param .Channel channel: the `.Channel` the request arrived on + :return: ``True`` if the AgentForward was loaded; ``False`` if not + """ + return False + + def check_channel_direct_tcpip_request(self, chanid, origin, destination): + """ + Determine if a local port forwarding channel will be granted, and + return ``OPEN_SUCCEEDED`` or an error code. This method is + called in server mode when the client requests a channel, after + authentication is complete. + + The ``chanid`` parameter is a small number that uniquely identifies the + channel within a `.Transport`. A `.Channel` object is not created + unless this method returns ``OPEN_SUCCEEDED`` -- once a + `.Channel` object is created, you can call `.Channel.get_id` to + retrieve the channel ID. + + The origin and destination parameters are (ip_address, port) tuples + that correspond to both ends of the TCP connection in the forwarding + tunnel. + + The return value should either be ``OPEN_SUCCEEDED`` (or + ``0``) to allow the channel request, or one of the following error + codes to reject it: + + - ``OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED`` + - ``OPEN_FAILED_CONNECT_FAILED`` + - ``OPEN_FAILED_UNKNOWN_CHANNEL_TYPE`` + - ``OPEN_FAILED_RESOURCE_SHORTAGE`` + + The default implementation always returns + ``OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED``. + + :param int chanid: ID of the channel + :param tuple origin: + 2-tuple containing the IP address and port of the originator + (client side) + :param tuple destination: + 2-tuple containing the IP address and port of the destination + (server side) + :return: an `int` success or failure code (listed above) + """ + return OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED + + def check_channel_env_request(self, channel, name, value): + """ + Check whether a given environment variable can be specified for the + given channel. This method should return C{True} if the server + is willing to set the specified environment variable. Note that + some environment variables (e.g., PATH) can be exceedingly + dangerous, so blindly allowing the client to set the environment + is almost certainly not a good idea. + + The default implementation always returns C{False}. + + @param channel: the L{Channel} the env request arrived on + @type channel: L{Channel} + @param name: foo bar baz + @type name: str + @param value: flklj + @type value: str + @rtype: bool + """ + return False + + +class InteractiveQuery (object): + """ + A query (set of prompts) for a user during interactive authentication. + """ + + def __init__(self, name='', instructions='', *prompts): + """ + Create a new interactive query to send to the client. The name and + instructions are optional, but are generally displayed to the end + user. A list of prompts may be included, or they may be added via + the `add_prompt` method. + + :param str name: name of this query + :param str instructions: + user instructions (usually short) about this query + :param str prompts: one or more authentication prompts + """ + self.name = name + self.instructions = instructions + self.prompts = [] + for x in prompts: + if isinstance(x, string_types): + self.add_prompt(x) + else: + self.add_prompt(x[0], x[1]) + + def add_prompt(self, prompt, echo=True): + """ + Add a prompt to this query. The prompt should be a (reasonably short) + string. Multiple prompts can be added to the same query. + + :param str prompt: the user prompt + :param bool echo: + ``True`` (default) if the user's response should be echoed; + ``False`` if not (for a password or similar) + """ + self.prompts.append((prompt, echo)) + + +class SubsystemHandler (threading.Thread): + """ + Handler for a subsytem in server mode. If you create a subclass of this + class and pass it to `.Transport.set_subsystem_handler`, an object of this + class will be created for each request for this subsystem. Each new object + will be executed within its own new thread by calling `start_subsystem`. + When that method completes, the channel is closed. + + For example, if you made a subclass ``MP3Handler`` and registered it as the + handler for subsystem ``"mp3"``, then whenever a client has successfully + authenticated and requests subsytem ``"mp3"``, an object of class + ``MP3Handler`` will be created, and `start_subsystem` will be called on + it from a new thread. + """ + def __init__(self, channel, name, server): + """ + Create a new handler for a channel. This is used by `.ServerInterface` + to start up a new handler when a channel requests this subsystem. You + don't need to override this method, but if you do, be sure to pass the + ``channel`` and ``name`` parameters through to the original ``__init__`` + method here. + + :param .Channel channel: the channel associated with this subsystem request. + :param str name: name of the requested subsystem. + :param .ServerInterface server: + the server object for the session that started this subsystem + """ + threading.Thread.__init__(self, target=self._run) + self.__channel = channel + self.__transport = channel.get_transport() + self.__name = name + self.__server = server + + def get_server(self): + """ + Return the `.ServerInterface` object associated with this channel and + subsystem. + """ + return self.__server + + def _run(self): + try: + self.__transport._log(DEBUG, 'Starting handler for subsystem %s' % self.__name) + self.start_subsystem(self.__name, self.__transport, self.__channel) + except Exception as e: + self.__transport._log(ERROR, 'Exception in subsystem handler for "%s": %s' % + (self.__name, str(e))) + self.__transport._log(ERROR, util.tb_strings()) + try: + self.finish_subsystem() + except: + pass + + def start_subsystem(self, name, transport, channel): + """ + Process an ssh subsystem in server mode. This method is called on a + new object (and in a new thread) for each subsystem request. It is + assumed that all subsystem logic will take place here, and when the + subsystem is finished, this method will return. After this method + returns, the channel is closed. + + The combination of ``transport`` and ``channel`` are unique; this handler + corresponds to exactly one `.Channel` on one `.Transport`. + + .. note:: + It is the responsibility of this method to exit if the underlying + `.Transport` is closed. This can be done by checking + `.Transport.is_active` or noticing an EOF on the `.Channel`. If + this method loops forever without checking for this case, your + Python interpreter may refuse to exit because this thread will + still be running. + + :param str name: name of the requested subsystem. + :param .Transport transport: the server-mode `.Transport`. + :param .Channel channel: the channel associated with this subsystem request. + """ + pass + + def finish_subsystem(self): + """ + Perform any cleanup at the end of a subsystem. The default + implementation just closes the channel. + + .. versionadded:: 1.1 + """ + self.__channel.close() diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/sftp.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/sftp.py new file mode 100644 index 0000000..f44a804 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/sftp.py @@ -0,0 +1,186 @@ +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +import select +import socket +import struct + +from paramiko import util +from paramiko.common import asbytes, DEBUG +from paramiko.message import Message +from paramiko.py3compat import byte_chr, byte_ord + + +CMD_INIT, CMD_VERSION, CMD_OPEN, CMD_CLOSE, CMD_READ, CMD_WRITE, CMD_LSTAT, CMD_FSTAT, \ + CMD_SETSTAT, CMD_FSETSTAT, CMD_OPENDIR, CMD_READDIR, CMD_REMOVE, CMD_MKDIR, \ + CMD_RMDIR, CMD_REALPATH, CMD_STAT, CMD_RENAME, CMD_READLINK, CMD_SYMLINK = range(1, 21) +CMD_STATUS, CMD_HANDLE, CMD_DATA, CMD_NAME, CMD_ATTRS = range(101, 106) +CMD_EXTENDED, CMD_EXTENDED_REPLY = range(200, 202) + +SFTP_OK = 0 +SFTP_EOF, SFTP_NO_SUCH_FILE, SFTP_PERMISSION_DENIED, SFTP_FAILURE, SFTP_BAD_MESSAGE, \ + SFTP_NO_CONNECTION, SFTP_CONNECTION_LOST, SFTP_OP_UNSUPPORTED = range(1, 9) + +SFTP_DESC = ['Success', + 'End of file', + 'No such file', + 'Permission denied', + 'Failure', + 'Bad message', + 'No connection', + 'Connection lost', + 'Operation unsupported'] + +SFTP_FLAG_READ = 0x1 +SFTP_FLAG_WRITE = 0x2 +SFTP_FLAG_APPEND = 0x4 +SFTP_FLAG_CREATE = 0x8 +SFTP_FLAG_TRUNC = 0x10 +SFTP_FLAG_EXCL = 0x20 + +_VERSION = 3 + + +# for debugging +CMD_NAMES = { + CMD_INIT: 'init', + CMD_VERSION: 'version', + CMD_OPEN: 'open', + CMD_CLOSE: 'close', + CMD_READ: 'read', + CMD_WRITE: 'write', + CMD_LSTAT: 'lstat', + CMD_FSTAT: 'fstat', + CMD_SETSTAT: 'setstat', + CMD_FSETSTAT: 'fsetstat', + CMD_OPENDIR: 'opendir', + CMD_READDIR: 'readdir', + CMD_REMOVE: 'remove', + CMD_MKDIR: 'mkdir', + CMD_RMDIR: 'rmdir', + CMD_REALPATH: 'realpath', + CMD_STAT: 'stat', + CMD_RENAME: 'rename', + CMD_READLINK: 'readlink', + CMD_SYMLINK: 'symlink', + CMD_STATUS: 'status', + CMD_HANDLE: 'handle', + CMD_DATA: 'data', + CMD_NAME: 'name', + CMD_ATTRS: 'attrs', + CMD_EXTENDED: 'extended', + CMD_EXTENDED_REPLY: 'extended_reply' +} + + +class SFTPError (Exception): + pass + + +class BaseSFTP (object): + def __init__(self): + self.logger = util.get_logger('paramiko.sftp') + self.sock = None + self.ultra_debug = False + + ### internals... + + def _send_version(self): + self._send_packet(CMD_INIT, struct.pack('>I', _VERSION)) + t, data = self._read_packet() + if t != CMD_VERSION: + raise SFTPError('Incompatible sftp protocol') + version = struct.unpack('>I', data[:4])[0] + # if version != _VERSION: + # raise SFTPError('Incompatible sftp protocol') + return version + + def _send_server_version(self): + # winscp will freak out if the server sends version info before the + # client finishes sending INIT. + t, data = self._read_packet() + if t != CMD_INIT: + raise SFTPError('Incompatible sftp protocol') + version = struct.unpack('>I', data[:4])[0] + # advertise that we support "check-file" + extension_pairs = ['check-file', 'md5,sha1'] + msg = Message() + msg.add_int(_VERSION) + msg.add(*extension_pairs) + self._send_packet(CMD_VERSION, msg) + return version + + def _log(self, level, msg, *args): + self.logger.log(level, msg, *args) + + def _write_all(self, out): + while len(out) > 0: + n = self.sock.send(out) + if n <= 0: + raise EOFError() + if n == len(out): + return + out = out[n:] + return + + def _read_all(self, n): + out = bytes() + while n > 0: + if isinstance(self.sock, socket.socket): + # sometimes sftp is used directly over a socket instead of + # through a paramiko channel. in this case, check periodically + # if the socket is closed. (for some reason, recv() won't ever + # return or raise an exception, but calling select on a closed + # socket will.) + while True: + read, write, err = select.select([self.sock], [], [], 0.1) + if len(read) > 0: + x = self.sock.recv(n) + break + else: + x = self.sock.recv(n) + + if len(x) == 0: + raise EOFError() + out += x + n -= len(x) + return out + + def _send_packet(self, t, packet): + #self._log(DEBUG2, 'write: %s (len=%d)' % (CMD_NAMES.get(t, '0x%02x' % t), len(packet))) + packet = asbytes(packet) + out = struct.pack('>I', len(packet) + 1) + byte_chr(t) + packet + if self.ultra_debug: + self._log(DEBUG, util.format_binary(out, 'OUT: ')) + self._write_all(out) + + def _read_packet(self): + x = self._read_all(4) + # most sftp servers won't accept packets larger than about 32k, so + # anything with the high byte set (> 16MB) is just garbage. + if byte_ord(x[0]): + raise SFTPError('Garbage packet received') + size = struct.unpack('>I', x)[0] + data = self._read_all(size) + if self.ultra_debug: + self._log(DEBUG, util.format_binary(data, 'IN: ')) + if size > 0: + t = byte_ord(data[0]) + #self._log(DEBUG2, 'read: %s (len=%d)' % (CMD_NAMES.get(t), '0x%02x' % t, len(data)-1)) + return t, data[1:] + return 0, bytes() diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/sftp_attr.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/sftp_attr.py new file mode 100644 index 0000000..d12eff8 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/sftp_attr.py @@ -0,0 +1,221 @@ +# Copyright (C) 2003-2006 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +import stat +import time +from paramiko.common import x80000000, o700, o70, xffffffff +from paramiko.py3compat import long, b + + +class SFTPAttributes (object): + """ + Representation of the attributes of a file (or proxied file) for SFTP in + client or server mode. It attemps to mirror the object returned by + `os.stat` as closely as possible, so it may have the following fields, + with the same meanings as those returned by an `os.stat` object: + + - ``st_size`` + - ``st_uid`` + - ``st_gid`` + - ``st_mode`` + - ``st_atime`` + - ``st_mtime`` + + Because SFTP allows flags to have other arbitrary named attributes, these + are stored in a dict named ``attr``. Occasionally, the filename is also + stored, in ``filename``. + """ + + FLAG_SIZE = 1 + FLAG_UIDGID = 2 + FLAG_PERMISSIONS = 4 + FLAG_AMTIME = 8 + FLAG_EXTENDED = x80000000 + + def __init__(self): + """ + Create a new (empty) SFTPAttributes object. All fields will be empty. + """ + self._flags = 0 + self.st_size = None + self.st_uid = None + self.st_gid = None + self.st_mode = None + self.st_atime = None + self.st_mtime = None + self.attr = {} + + def from_stat(cls, obj, filename=None): + """ + Create an `.SFTPAttributes` object from an existing ``stat`` object (an + object returned by `os.stat`). + + :param object obj: an object returned by `os.stat` (or equivalent). + :param str filename: the filename associated with this file. + :return: new `.SFTPAttributes` object with the same attribute fields. + """ + attr = cls() + attr.st_size = obj.st_size + attr.st_uid = obj.st_uid + attr.st_gid = obj.st_gid + attr.st_mode = obj.st_mode + attr.st_atime = obj.st_atime + attr.st_mtime = obj.st_mtime + if filename is not None: + attr.filename = filename + return attr + from_stat = classmethod(from_stat) + + def __repr__(self): + return '' % self._debug_str() + + ### internals... + + def _from_msg(cls, msg, filename=None, longname=None): + attr = cls() + attr._unpack(msg) + if filename is not None: + attr.filename = filename + if longname is not None: + attr.longname = longname + return attr + _from_msg = classmethod(_from_msg) + + def _unpack(self, msg): + self._flags = msg.get_int() + if self._flags & self.FLAG_SIZE: + self.st_size = msg.get_int64() + if self._flags & self.FLAG_UIDGID: + self.st_uid = msg.get_int() + self.st_gid = msg.get_int() + if self._flags & self.FLAG_PERMISSIONS: + self.st_mode = msg.get_int() + if self._flags & self.FLAG_AMTIME: + self.st_atime = msg.get_int() + self.st_mtime = msg.get_int() + if self._flags & self.FLAG_EXTENDED: + count = msg.get_int() + for i in range(count): + self.attr[msg.get_string()] = msg.get_string() + + def _pack(self, msg): + self._flags = 0 + if self.st_size is not None: + self._flags |= self.FLAG_SIZE + if (self.st_uid is not None) and (self.st_gid is not None): + self._flags |= self.FLAG_UIDGID + if self.st_mode is not None: + self._flags |= self.FLAG_PERMISSIONS + if (self.st_atime is not None) and (self.st_mtime is not None): + self._flags |= self.FLAG_AMTIME + if len(self.attr) > 0: + self._flags |= self.FLAG_EXTENDED + msg.add_int(self._flags) + if self._flags & self.FLAG_SIZE: + msg.add_int64(self.st_size) + if self._flags & self.FLAG_UIDGID: + msg.add_int(self.st_uid) + msg.add_int(self.st_gid) + if self._flags & self.FLAG_PERMISSIONS: + msg.add_int(self.st_mode) + if self._flags & self.FLAG_AMTIME: + # throw away any fractional seconds + msg.add_int(long(self.st_atime)) + msg.add_int(long(self.st_mtime)) + if self._flags & self.FLAG_EXTENDED: + msg.add_int(len(self.attr)) + for key, val in self.attr.items(): + msg.add_string(key) + msg.add_string(val) + return + + def _debug_str(self): + out = '[ ' + if self.st_size is not None: + out += 'size=%d ' % self.st_size + if (self.st_uid is not None) and (self.st_gid is not None): + out += 'uid=%d gid=%d ' % (self.st_uid, self.st_gid) + if self.st_mode is not None: + out += 'mode=' + oct(self.st_mode) + ' ' + if (self.st_atime is not None) and (self.st_mtime is not None): + out += 'atime=%d mtime=%d ' % (self.st_atime, self.st_mtime) + for k, v in self.attr.items(): + out += '"%s"=%r ' % (str(k), v) + out += ']' + return out + + def _rwx(n, suid, sticky=False): + if suid: + suid = 2 + out = '-r'[n >> 2] + '-w'[(n >> 1) & 1] + if sticky: + out += '-xTt'[suid + (n & 1)] + else: + out += '-xSs'[suid + (n & 1)] + return out + _rwx = staticmethod(_rwx) + + def __str__(self): + """create a unix-style long description of the file (like ls -l)""" + if self.st_mode is not None: + kind = stat.S_IFMT(self.st_mode) + if kind == stat.S_IFIFO: + ks = 'p' + elif kind == stat.S_IFCHR: + ks = 'c' + elif kind == stat.S_IFDIR: + ks = 'd' + elif kind == stat.S_IFBLK: + ks = 'b' + elif kind == stat.S_IFREG: + ks = '-' + elif kind == stat.S_IFLNK: + ks = 'l' + elif kind == stat.S_IFSOCK: + ks = 's' + else: + ks = '?' + ks += self._rwx((self.st_mode & o700) >> 6, self.st_mode & stat.S_ISUID) + ks += self._rwx((self.st_mode & o70) >> 3, self.st_mode & stat.S_ISGID) + ks += self._rwx(self.st_mode & 7, self.st_mode & stat.S_ISVTX, True) + else: + ks = '?---------' + # compute display date + if (self.st_mtime is None) or (self.st_mtime == xffffffff): + # shouldn't really happen + datestr = '(unknown date)' + else: + if abs(time.time() - self.st_mtime) > 15552000: + # (15552000 = 6 months) + datestr = time.strftime('%d %b %Y', time.localtime(self.st_mtime)) + else: + datestr = time.strftime('%d %b %H:%M', time.localtime(self.st_mtime)) + filename = getattr(self, 'filename', '?') + + # not all servers support uid/gid + uid = self.st_uid + gid = self.st_gid + if uid is None: + uid = 0 + if gid is None: + gid = 0 + + return '%s 1 %-8d %-8d %8d %-12s %s' % (ks, uid, gid, self.st_size, datestr, filename) + + def asbytes(self): + return b(str(self)) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/sftp_client.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/sftp_client.py new file mode 100644 index 0000000..1caaf16 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/sftp_client.py @@ -0,0 +1,748 @@ +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of Paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + + +from binascii import hexlify +import errno +import os +import stat +import threading +import time +import weakref +from paramiko import util +from paramiko.channel import Channel +from paramiko.message import Message +from paramiko.common import INFO, DEBUG, o777 +from paramiko.py3compat import bytestring, b, u, long, string_types, bytes_types +from paramiko.sftp import BaseSFTP, CMD_OPENDIR, CMD_HANDLE, SFTPError, CMD_READDIR, \ + CMD_NAME, CMD_CLOSE, SFTP_FLAG_READ, SFTP_FLAG_WRITE, SFTP_FLAG_CREATE, \ + SFTP_FLAG_TRUNC, SFTP_FLAG_APPEND, SFTP_FLAG_EXCL, CMD_OPEN, CMD_REMOVE, \ + CMD_RENAME, CMD_MKDIR, CMD_RMDIR, CMD_STAT, CMD_ATTRS, CMD_LSTAT, \ + CMD_SYMLINK, CMD_SETSTAT, CMD_READLINK, CMD_REALPATH, CMD_STATUS, SFTP_OK, \ + SFTP_EOF, SFTP_NO_SUCH_FILE, SFTP_PERMISSION_DENIED + +from paramiko.sftp_attr import SFTPAttributes +from paramiko.ssh_exception import SSHException +from paramiko.sftp_file import SFTPFile + + +def _to_unicode(s): + """ + decode a string as ascii or utf8 if possible (as required by the sftp + protocol). if neither works, just return a byte string because the server + probably doesn't know the filename's encoding. + """ + try: + return s.encode('ascii') + except (UnicodeError, AttributeError): + try: + return s.decode('utf-8') + except UnicodeError: + return s + +b_slash = b'/' + + +class SFTPClient(BaseSFTP): + """ + SFTP client object. + + Used to open an SFTP session across an open SSH `.Transport` and perform + remote file operations. + """ + def __init__(self, sock): + """ + Create an SFTP client from an existing `.Channel`. The channel + should already have requested the ``"sftp"`` subsystem. + + An alternate way to create an SFTP client context is by using + `from_transport`. + + :param .Channel sock: an open `.Channel` using the ``"sftp"`` subsystem + + :raises SSHException: if there's an exception while negotiating + sftp + """ + BaseSFTP.__init__(self) + self.sock = sock + self.ultra_debug = False + self.request_number = 1 + # lock for request_number + self._lock = threading.Lock() + self._cwd = None + # request # -> SFTPFile + self._expecting = weakref.WeakValueDictionary() + if type(sock) is Channel: + # override default logger + transport = self.sock.get_transport() + self.logger = util.get_logger(transport.get_log_channel() + '.sftp') + self.ultra_debug = transport.get_hexdump() + try: + server_version = self._send_version() + except EOFError: + raise SSHException('EOF during negotiation') + self._log(INFO, 'Opened sftp connection (server version %d)' % server_version) + + def from_transport(cls, t): + """ + Create an SFTP client channel from an open `.Transport`. + + :param .Transport t: an open `.Transport` which is already authenticated + :return: + a new `.SFTPClient` object, referring to an sftp session (channel) + across the transport + """ + chan = t.open_session() + if chan is None: + return None + chan.invoke_subsystem('sftp') + return cls(chan) + from_transport = classmethod(from_transport) + + def _log(self, level, msg, *args): + if isinstance(msg, list): + for m in msg: + self._log(level, m, *args) + else: + # escape '%' in msg (they could come from file or directory names) before logging + msg = msg.replace('%','%%') + super(SFTPClient, self)._log(level, "[chan %s] " + msg, *([self.sock.get_name()] + list(args))) + + def close(self): + """ + Close the SFTP session and its underlying channel. + + .. versionadded:: 1.4 + """ + self._log(INFO, 'sftp session closed.') + self.sock.close() + + def get_channel(self): + """ + Return the underlying `.Channel` object for this SFTP session. This + might be useful for doing things like setting a timeout on the channel. + + .. versionadded:: 1.7.1 + """ + return self.sock + + def listdir(self, path='.'): + """ + Return a list containing the names of the entries in the given ``path``. + + The list is in arbitrary order. It does not include the special + entries ``'.'`` and ``'..'`` even if they are present in the folder. + This method is meant to mirror ``os.listdir`` as closely as possible. + For a list of full `.SFTPAttributes` objects, see `listdir_attr`. + + :param str path: path to list (defaults to ``'.'``) + """ + return [f.filename for f in self.listdir_attr(path)] + + def listdir_attr(self, path='.'): + """ + Return a list containing `.SFTPAttributes` objects corresponding to + files in the given ``path``. The list is in arbitrary order. It does + not include the special entries ``'.'`` and ``'..'`` even if they are + present in the folder. + + The returned `.SFTPAttributes` objects will each have an additional + field: ``longname``, which may contain a formatted string of the file's + attributes, in unix format. The content of this string will probably + depend on the SFTP server implementation. + + :param str path: path to list (defaults to ``'.'``) + :return: list of `.SFTPAttributes` objects + + .. versionadded:: 1.2 + """ + path = self._adjust_cwd(path) + self._log(DEBUG, 'listdir(%r)' % path) + t, msg = self._request(CMD_OPENDIR, path) + if t != CMD_HANDLE: + raise SFTPError('Expected handle') + handle = msg.get_binary() + filelist = [] + while True: + try: + t, msg = self._request(CMD_READDIR, handle) + except EOFError: + # done with handle + break + if t != CMD_NAME: + raise SFTPError('Expected name response') + count = msg.get_int() + for i in range(count): + filename = msg.get_text() + longname = msg.get_text() + attr = SFTPAttributes._from_msg(msg, filename, longname) + if (filename != '.') and (filename != '..'): + filelist.append(attr) + self._request(CMD_CLOSE, handle) + return filelist + + def open(self, filename, mode='r', bufsize=-1): + """ + Open a file on the remote server. The arguments are the same as for + Python's built-in `python:file` (aka `python:open`). A file-like + object is returned, which closely mimics the behavior of a normal + Python file object, including the ability to be used as a context + manager. + + The mode indicates how the file is to be opened: ``'r'`` for reading, + ``'w'`` for writing (truncating an existing file), ``'a'`` for + appending, ``'r+'`` for reading/writing, ``'w+'`` for reading/writing + (truncating an existing file), ``'a+'`` for reading/appending. The + Python ``'b'`` flag is ignored, since SSH treats all files as binary. + The ``'U'`` flag is supported in a compatible way. + + Since 1.5.2, an ``'x'`` flag indicates that the operation should only + succeed if the file was created and did not previously exist. This has + no direct mapping to Python's file flags, but is commonly known as the + ``O_EXCL`` flag in posix. + + The file will be buffered in standard Python style by default, but + can be altered with the ``bufsize`` parameter. ``0`` turns off + buffering, ``1`` uses line buffering, and any number greater than 1 + (``>1``) uses that specific buffer size. + + :param str filename: name of the file to open + :param str mode: mode (Python-style) to open in + :param int bufsize: desired buffering (-1 = default buffer size) + :return: an `.SFTPFile` object representing the open file + + :raises IOError: if the file could not be opened. + """ + filename = self._adjust_cwd(filename) + self._log(DEBUG, 'open(%r, %r)' % (filename, mode)) + imode = 0 + if ('r' in mode) or ('+' in mode): + imode |= SFTP_FLAG_READ + if ('w' in mode) or ('+' in mode) or ('a' in mode): + imode |= SFTP_FLAG_WRITE + if 'w' in mode: + imode |= SFTP_FLAG_CREATE | SFTP_FLAG_TRUNC + if 'a' in mode: + imode |= SFTP_FLAG_CREATE | SFTP_FLAG_APPEND + if 'x' in mode: + imode |= SFTP_FLAG_CREATE | SFTP_FLAG_EXCL + attrblock = SFTPAttributes() + t, msg = self._request(CMD_OPEN, filename, imode, attrblock) + if t != CMD_HANDLE: + raise SFTPError('Expected handle') + handle = msg.get_binary() + self._log(DEBUG, 'open(%r, %r) -> %s' % (filename, mode, hexlify(handle))) + return SFTPFile(self, handle, mode, bufsize) + + # Python continues to vacillate about "open" vs "file"... + file = open + + def remove(self, path): + """ + Remove the file at the given path. This only works on files; for + removing folders (directories), use `rmdir`. + + :param str path: path (absolute or relative) of the file to remove + + :raises IOError: if the path refers to a folder (directory) + """ + path = self._adjust_cwd(path) + self._log(DEBUG, 'remove(%r)' % path) + self._request(CMD_REMOVE, path) + + unlink = remove + + def rename(self, oldpath, newpath): + """ + Rename a file or folder from ``oldpath`` to ``newpath``. + + :param str oldpath: existing name of the file or folder + :param str newpath: new name for the file or folder + + :raises IOError: if ``newpath`` is a folder, or something else goes + wrong + """ + oldpath = self._adjust_cwd(oldpath) + newpath = self._adjust_cwd(newpath) + self._log(DEBUG, 'rename(%r, %r)' % (oldpath, newpath)) + self._request(CMD_RENAME, oldpath, newpath) + + def mkdir(self, path, mode=o777): + """ + Create a folder (directory) named ``path`` with numeric mode ``mode``. + The default mode is 0777 (octal). On some systems, mode is ignored. + Where it is used, the current umask value is first masked out. + + :param str path: name of the folder to create + :param int mode: permissions (posix-style) for the newly-created folder + """ + path = self._adjust_cwd(path) + self._log(DEBUG, 'mkdir(%r, %r)' % (path, mode)) + attr = SFTPAttributes() + attr.st_mode = mode + self._request(CMD_MKDIR, path, attr) + + def rmdir(self, path): + """ + Remove the folder named ``path``. + + :param str path: name of the folder to remove + """ + path = self._adjust_cwd(path) + self._log(DEBUG, 'rmdir(%r)' % path) + self._request(CMD_RMDIR, path) + + def stat(self, path): + """ + Retrieve information about a file on the remote system. The return + value is an object whose attributes correspond to the attributes of + Python's ``stat`` structure as returned by ``os.stat``, except that it + contains fewer fields. An SFTP server may return as much or as little + info as it wants, so the results may vary from server to server. + + Unlike a Python `python:stat` object, the result may not be accessed as + a tuple. This is mostly due to the author's slack factor. + + The fields supported are: ``st_mode``, ``st_size``, ``st_uid``, + ``st_gid``, ``st_atime``, and ``st_mtime``. + + :param str path: the filename to stat + :return: + an `.SFTPAttributes` object containing attributes about the given + file + """ + path = self._adjust_cwd(path) + self._log(DEBUG, 'stat(%r)' % path) + t, msg = self._request(CMD_STAT, path) + if t != CMD_ATTRS: + raise SFTPError('Expected attributes') + return SFTPAttributes._from_msg(msg) + + def lstat(self, path): + """ + Retrieve information about a file on the remote system, without + following symbolic links (shortcuts). This otherwise behaves exactly + the same as `stat`. + + :param str path: the filename to stat + :return: + an `.SFTPAttributes` object containing attributes about the given + file + """ + path = self._adjust_cwd(path) + self._log(DEBUG, 'lstat(%r)' % path) + t, msg = self._request(CMD_LSTAT, path) + if t != CMD_ATTRS: + raise SFTPError('Expected attributes') + return SFTPAttributes._from_msg(msg) + + def symlink(self, source, dest): + """ + Create a symbolic link (shortcut) of the ``source`` path at + ``destination``. + + :param str source: path of the original file + :param str dest: path of the newly created symlink + """ + dest = self._adjust_cwd(dest) + self._log(DEBUG, 'symlink(%r, %r)' % (source, dest)) + source = bytestring(source) + self._request(CMD_SYMLINK, source, dest) + + def chmod(self, path, mode): + """ + Change the mode (permissions) of a file. The permissions are + unix-style and identical to those used by Python's `os.chmod` + function. + + :param str path: path of the file to change the permissions of + :param int mode: new permissions + """ + path = self._adjust_cwd(path) + self._log(DEBUG, 'chmod(%r, %r)' % (path, mode)) + attr = SFTPAttributes() + attr.st_mode = mode + self._request(CMD_SETSTAT, path, attr) + + def chown(self, path, uid, gid): + """ + Change the owner (``uid``) and group (``gid``) of a file. As with + Python's `os.chown` function, you must pass both arguments, so if you + only want to change one, use `stat` first to retrieve the current + owner and group. + + :param str path: path of the file to change the owner and group of + :param int uid: new owner's uid + :param int gid: new group id + """ + path = self._adjust_cwd(path) + self._log(DEBUG, 'chown(%r, %r, %r)' % (path, uid, gid)) + attr = SFTPAttributes() + attr.st_uid, attr.st_gid = uid, gid + self._request(CMD_SETSTAT, path, attr) + + def utime(self, path, times): + """ + Set the access and modified times of the file specified by ``path``. If + ``times`` is ``None``, then the file's access and modified times are set + to the current time. Otherwise, ``times`` must be a 2-tuple of numbers, + of the form ``(atime, mtime)``, which is used to set the access and + modified times, respectively. This bizarre API is mimicked from Python + for the sake of consistency -- I apologize. + + :param str path: path of the file to modify + :param tuple times: + ``None`` or a tuple of (access time, modified time) in standard + internet epoch time (seconds since 01 January 1970 GMT) + """ + path = self._adjust_cwd(path) + if times is None: + times = (time.time(), time.time()) + self._log(DEBUG, 'utime(%r, %r)' % (path, times)) + attr = SFTPAttributes() + attr.st_atime, attr.st_mtime = times + self._request(CMD_SETSTAT, path, attr) + + def truncate(self, path, size): + """ + Change the size of the file specified by ``path``. This usually + extends or shrinks the size of the file, just like the `~file.truncate` + method on Python file objects. + + :param str path: path of the file to modify + :param size: the new size of the file + :type size: int or long + """ + path = self._adjust_cwd(path) + self._log(DEBUG, 'truncate(%r, %r)' % (path, size)) + attr = SFTPAttributes() + attr.st_size = size + self._request(CMD_SETSTAT, path, attr) + + def readlink(self, path): + """ + Return the target of a symbolic link (shortcut). You can use + `symlink` to create these. The result may be either an absolute or + relative pathname. + + :param str path: path of the symbolic link file + :return: target path, as a `str` + """ + path = self._adjust_cwd(path) + self._log(DEBUG, 'readlink(%r)' % path) + t, msg = self._request(CMD_READLINK, path) + if t != CMD_NAME: + raise SFTPError('Expected name response') + count = msg.get_int() + if count == 0: + return None + if count != 1: + raise SFTPError('Readlink returned %d results' % count) + return _to_unicode(msg.get_string()) + + def normalize(self, path): + """ + Return the normalized path (on the server) of a given path. This + can be used to quickly resolve symbolic links or determine what the + server is considering to be the "current folder" (by passing ``'.'`` + as ``path``). + + :param str path: path to be normalized + :return: normalized form of the given path (as a `str`) + + :raises IOError: if the path can't be resolved on the server + """ + path = self._adjust_cwd(path) + self._log(DEBUG, 'normalize(%r)' % path) + t, msg = self._request(CMD_REALPATH, path) + if t != CMD_NAME: + raise SFTPError('Expected name response') + count = msg.get_int() + if count != 1: + raise SFTPError('Realpath returned %d results' % count) + return msg.get_text() + + def chdir(self, path=None): + """ + Change the "current directory" of this SFTP session. Since SFTP + doesn't really have the concept of a current working directory, this is + emulated by Paramiko. Once you use this method to set a working + directory, all operations on this `.SFTPClient` object will be relative + to that path. You can pass in ``None`` to stop using a current working + directory. + + :param str path: new current working directory + + :raises IOError: if the requested path doesn't exist on the server + + .. versionadded:: 1.4 + """ + if path is None: + self._cwd = None + return + if not stat.S_ISDIR(self.stat(path).st_mode): + raise SFTPError(errno.ENOTDIR, "%s: %s" % (os.strerror(errno.ENOTDIR), path)) + self._cwd = b(self.normalize(path)) + + def getcwd(self): + """ + Return the "current working directory" for this SFTP session, as + emulated by Paramiko. If no directory has been set with `chdir`, + this method will return ``None``. + + .. versionadded:: 1.4 + """ + return self._cwd and u(self._cwd) + + def putfo(self, fl, remotepath, file_size=0, callback=None, confirm=True): + """ + Copy the contents of an open file object (``fl``) to the SFTP server as + ``remotepath``. Any exception raised by operations will be passed + through. + + The SFTP operations use pipelining for speed. + + :param file fl: opened file or file-like object to copy + :param str remotepath: the destination path on the SFTP server + :param int file_size: + optional size parameter passed to callback. If none is specified, + size defaults to 0 + :param callable callback: + optional callback function (form: ``func(int, int)``) that accepts + the bytes transferred so far and the total bytes to be transferred + (since 1.7.4) + :param bool confirm: + whether to do a stat() on the file afterwards to confirm the file + size (since 1.7.7) + + :return: + an `.SFTPAttributes` object containing attributes about the given + file. + + .. versionadded:: 1.4 + .. versionchanged:: 1.7.4 + Began returning rich attribute objects. + """ + with self.file(remotepath, 'wb') as fr: + fr.set_pipelined(True) + size = 0 + while True: + data = fl.read(32768) + fr.write(data) + size += len(data) + if callback is not None: + callback(size, file_size) + if len(data) == 0: + break + if confirm: + s = self.stat(remotepath) + if s.st_size != size: + raise IOError('size mismatch in put! %d != %d' % (s.st_size, size)) + else: + s = SFTPAttributes() + return s + + def put(self, localpath, remotepath, callback=None, confirm=True): + """ + Copy a local file (``localpath``) to the SFTP server as ``remotepath``. + Any exception raised by operations will be passed through. This + method is primarily provided as a convenience. + + The SFTP operations use pipelining for speed. + + :param str localpath: the local file to copy + :param str remotepath: the destination path on the SFTP server + :param callable callback: + optional callback function (form: ``func(int, int)``) that accepts + the bytes transferred so far and the total bytes to be transferred + :param bool confirm: + whether to do a stat() on the file afterwards to confirm the file + size + + :return: an `.SFTPAttributes` object containing attributes about the given file + + .. versionadded:: 1.4 + .. versionchanged:: 1.7.4 + ``callback`` and rich attribute return value added. + .. versionchanged:: 1.7.7 + ``confirm`` param added. + """ + file_size = os.stat(localpath).st_size + with open(localpath, 'rb') as fl: + return self.putfo(fl, remotepath, os.stat(localpath).st_size, callback, confirm) + + def getfo(self, remotepath, fl, callback=None): + """ + Copy a remote file (``remotepath``) from the SFTP server and write to + an open file or file-like object, ``fl``. Any exception raised by + operations will be passed through. This method is primarily provided + as a convenience. + + :param object remotepath: opened file or file-like object to copy to + :param str fl: + the destination path on the local host or open file object + :param callable callback: + optional callback function (form: ``func(int, int)``) that accepts + the bytes transferred so far and the total bytes to be transferred + :return: the `number ` of bytes written to the opened file object + + .. versionadded:: 1.4 + .. versionchanged:: 1.7.4 + Added the ``callable`` param. + """ + with self.open(remotepath, 'rb') as fr: + file_size = self.stat(remotepath).st_size + fr.prefetch() + size = 0 + while True: + data = fr.read(32768) + fl.write(data) + size += len(data) + if callback is not None: + callback(size, file_size) + if len(data) == 0: + break + return size + + def get(self, remotepath, localpath, callback=None): + """ + Copy a remote file (``remotepath``) from the SFTP server to the local + host as ``localpath``. Any exception raised by operations will be + passed through. This method is primarily provided as a convenience. + + :param str remotepath: the remote file to copy + :param str localpath: the destination path on the local host + :param callable callback: + optional callback function (form: ``func(int, int)``) that accepts + the bytes transferred so far and the total bytes to be transferred + + .. versionadded:: 1.4 + .. versionchanged:: 1.7.4 + Added the ``callback`` param + """ + file_size = self.stat(remotepath).st_size + with open(localpath, 'wb') as fl: + size = self.getfo(remotepath, fl, callback) + s = os.stat(localpath) + if s.st_size != size: + raise IOError('size mismatch in get! %d != %d' % (s.st_size, size)) + + ### internals... + + def _request(self, t, *arg): + num = self._async_request(type(None), t, *arg) + return self._read_response(num) + + def _async_request(self, fileobj, t, *arg): + # this method may be called from other threads (prefetch) + self._lock.acquire() + try: + msg = Message() + msg.add_int(self.request_number) + for item in arg: + if isinstance(item, long): + msg.add_int64(item) + elif isinstance(item, int): + msg.add_int(item) + elif isinstance(item, (string_types, bytes_types)): + msg.add_string(item) + elif isinstance(item, SFTPAttributes): + item._pack(msg) + else: + raise Exception('unknown type for %r type %r' % (item, type(item))) + num = self.request_number + self._expecting[num] = fileobj + self._send_packet(t, msg) + self.request_number += 1 + finally: + self._lock.release() + return num + + def _read_response(self, waitfor=None): + while True: + try: + t, data = self._read_packet() + except EOFError as e: + raise SSHException('Server connection dropped: %s' % str(e)) + msg = Message(data) + num = msg.get_int() + if num not in self._expecting: + # might be response for a file that was closed before responses came back + self._log(DEBUG, 'Unexpected response #%d' % (num,)) + if waitfor is None: + # just doing a single check + break + continue + fileobj = self._expecting[num] + del self._expecting[num] + if num == waitfor: + # synchronous + if t == CMD_STATUS: + self._convert_status(msg) + return t, msg + if fileobj is not type(None): + fileobj._async_response(t, msg, num) + if waitfor is None: + # just doing a single check + break + return None, None + + def _finish_responses(self, fileobj): + while fileobj in self._expecting.values(): + self._read_response() + fileobj._check_exception() + + def _convert_status(self, msg): + """ + Raises EOFError or IOError on error status; otherwise does nothing. + """ + code = msg.get_int() + text = msg.get_text() + if code == SFTP_OK: + return + elif code == SFTP_EOF: + raise EOFError(text) + elif code == SFTP_NO_SUCH_FILE: + # clever idea from john a. meinel: map the error codes to errno + raise IOError(errno.ENOENT, text) + elif code == SFTP_PERMISSION_DENIED: + raise IOError(errno.EACCES, text) + else: + raise IOError(text) + + def _adjust_cwd(self, path): + """ + Return an adjusted path if we're emulating a "current working + directory" for the server. + """ + path = b(path) + if self._cwd is None: + return path + if len(path) and path[0:1] == b_slash: + # absolute path + return path + if self._cwd == b_slash: + return self._cwd + path + return self._cwd + b_slash + path + + +class SFTP(SFTPClient): + """ + An alias for `.SFTPClient` for backwards compatability. + """ + pass diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/sftp_file.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/sftp_file.py new file mode 100644 index 0000000..03d67b3 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/sftp_file.py @@ -0,0 +1,496 @@ +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +SFTP file object +""" + +from __future__ import with_statement + +from binascii import hexlify +from collections import deque +import socket +import threading +import time +from paramiko.common import DEBUG + +from paramiko.file import BufferedFile +from paramiko.py3compat import long +from paramiko.sftp import CMD_CLOSE, CMD_READ, CMD_DATA, SFTPError, CMD_WRITE, \ + CMD_STATUS, CMD_FSTAT, CMD_ATTRS, CMD_FSETSTAT, CMD_EXTENDED +from paramiko.sftp_attr import SFTPAttributes + + +class SFTPFile (BufferedFile): + """ + Proxy object for a file on the remote server, in client mode SFTP. + + Instances of this class may be used as context managers in the same way + that built-in Python file objects are. + """ + + # Some sftp servers will choke if you send read/write requests larger than + # this size. + MAX_REQUEST_SIZE = 32768 + + def __init__(self, sftp, handle, mode='r', bufsize=-1): + BufferedFile.__init__(self) + self.sftp = sftp + self.handle = handle + BufferedFile._set_mode(self, mode, bufsize) + self.pipelined = False + self._prefetching = False + self._prefetch_done = False + self._prefetch_data = {} + self._prefetch_extents = {} + self._prefetch_lock = threading.Lock() + self._saved_exception = None + self._reqs = deque() + + def __del__(self): + self._close(async=True) + + def close(self): + """ + Close the file. + """ + self._close(async=False) + + def _close(self, async=False): + # We allow double-close without signaling an error, because real + # Python file objects do. However, we must protect against actually + # sending multiple CMD_CLOSE packets, because after we close our + # handle, the same handle may be re-allocated by the server, and we + # may end up mysteriously closing some random other file. (This is + # especially important because we unconditionally call close() from + # __del__.) + if self._closed: + return + self.sftp._log(DEBUG, 'close(%s)' % hexlify(self.handle)) + if self.pipelined: + self.sftp._finish_responses(self) + BufferedFile.close(self) + try: + if async: + # GC'd file handle could be called from an arbitrary thread -- don't wait for a response + self.sftp._async_request(type(None), CMD_CLOSE, self.handle) + else: + self.sftp._request(CMD_CLOSE, self.handle) + except EOFError: + # may have outlived the Transport connection + pass + except (IOError, socket.error): + # may have outlived the Transport connection + pass + + def _data_in_prefetch_requests(self, offset, size): + k = [x for x in list(self._prefetch_extents.values()) if x[0] <= offset] + if len(k) == 0: + return False + k.sort(key=lambda x: x[0]) + buf_offset, buf_size = k[-1] + if buf_offset + buf_size <= offset: + # prefetch request ends before this one begins + return False + if buf_offset + buf_size >= offset + size: + # inclusive + return True + # well, we have part of the request. see if another chunk has the rest. + return self._data_in_prefetch_requests(buf_offset + buf_size, offset + size - buf_offset - buf_size) + + def _data_in_prefetch_buffers(self, offset): + """ + if a block of data is present in the prefetch buffers, at the given + offset, return the offset of the relevant prefetch buffer. otherwise, + return None. this guarantees nothing about the number of bytes + collected in the prefetch buffer so far. + """ + k = [i for i in self._prefetch_data.keys() if i <= offset] + if len(k) == 0: + return None + index = max(k) + buf_offset = offset - index + if buf_offset >= len(self._prefetch_data[index]): + # it's not here + return None + return index + + def _read_prefetch(self, size): + """ + read data out of the prefetch buffer, if possible. if the data isn't + in the buffer, return None. otherwise, behaves like a normal read. + """ + # while not closed, and haven't fetched past the current position, and haven't reached EOF... + while True: + offset = self._data_in_prefetch_buffers(self._realpos) + if offset is not None: + break + if self._prefetch_done or self._closed: + break + self.sftp._read_response() + self._check_exception() + if offset is None: + self._prefetching = False + return None + prefetch = self._prefetch_data[offset] + del self._prefetch_data[offset] + + buf_offset = self._realpos - offset + if buf_offset > 0: + self._prefetch_data[offset] = prefetch[:buf_offset] + prefetch = prefetch[buf_offset:] + if size < len(prefetch): + self._prefetch_data[self._realpos + size] = prefetch[size:] + prefetch = prefetch[:size] + return prefetch + + def _read(self, size): + size = min(size, self.MAX_REQUEST_SIZE) + if self._prefetching: + data = self._read_prefetch(size) + if data is not None: + return data + t, msg = self.sftp._request(CMD_READ, self.handle, long(self._realpos), int(size)) + if t != CMD_DATA: + raise SFTPError('Expected data') + return msg.get_string() + + def _write(self, data): + # may write less than requested if it would exceed max packet size + chunk = min(len(data), self.MAX_REQUEST_SIZE) + self._reqs.append(self.sftp._async_request(type(None), CMD_WRITE, self.handle, long(self._realpos), data[:chunk])) + if not self.pipelined or (len(self._reqs) > 100 and self.sftp.sock.recv_ready()): + while len(self._reqs): + req = self._reqs.popleft() + t, msg = self.sftp._read_response(req) + if t != CMD_STATUS: + raise SFTPError('Expected status') + # convert_status already called + return chunk + + def settimeout(self, timeout): + """ + Set a timeout on read/write operations on the underlying socket or + ssh `.Channel`. + + :param float timeout: + seconds to wait for a pending read/write operation before raising + ``socket.timeout``, or ``None`` for no timeout + + .. seealso:: `.Channel.settimeout` + """ + self.sftp.sock.settimeout(timeout) + + def gettimeout(self): + """ + Returns the timeout in seconds (as a `float`) associated with the + socket or ssh `.Channel` used for this file. + + .. seealso:: `.Channel.gettimeout` + """ + return self.sftp.sock.gettimeout() + + def setblocking(self, blocking): + """ + Set blocking or non-blocking mode on the underiying socket or ssh + `.Channel`. + + :param int blocking: + 0 to set non-blocking mode; non-0 to set blocking mode. + + .. seealso:: `.Channel.setblocking` + """ + self.sftp.sock.setblocking(blocking) + + def seek(self, offset, whence=0): + self.flush() + if whence == self.SEEK_SET: + self._realpos = self._pos = offset + elif whence == self.SEEK_CUR: + self._pos += offset + self._realpos = self._pos + else: + self._realpos = self._pos = self._get_size() + offset + self._rbuffer = bytes() + + def stat(self): + """ + Retrieve information about this file from the remote system. This is + exactly like `.SFTPClient.stat`, except that it operates on an + already-open file. + + :return: an `.SFTPAttributes` object containing attributes about this file. + """ + t, msg = self.sftp._request(CMD_FSTAT, self.handle) + if t != CMD_ATTRS: + raise SFTPError('Expected attributes') + return SFTPAttributes._from_msg(msg) + + def chmod(self, mode): + """ + Change the mode (permissions) of this file. The permissions are + unix-style and identical to those used by Python's `os.chmod` + function. + + :param int mode: new permissions + """ + self.sftp._log(DEBUG, 'chmod(%s, %r)' % (hexlify(self.handle), mode)) + attr = SFTPAttributes() + attr.st_mode = mode + self.sftp._request(CMD_FSETSTAT, self.handle, attr) + + def chown(self, uid, gid): + """ + Change the owner (``uid``) and group (``gid``) of this file. As with + Python's `os.chown` function, you must pass both arguments, so if you + only want to change one, use `stat` first to retrieve the current + owner and group. + + :param int uid: new owner's uid + :param int gid: new group id + """ + self.sftp._log(DEBUG, 'chown(%s, %r, %r)' % (hexlify(self.handle), uid, gid)) + attr = SFTPAttributes() + attr.st_uid, attr.st_gid = uid, gid + self.sftp._request(CMD_FSETSTAT, self.handle, attr) + + def utime(self, times): + """ + Set the access and modified times of this file. If + ``times`` is ``None``, then the file's access and modified times are set + to the current time. Otherwise, ``times`` must be a 2-tuple of numbers, + of the form ``(atime, mtime)``, which is used to set the access and + modified times, respectively. This bizarre API is mimicked from Python + for the sake of consistency -- I apologize. + + :param tuple times: + ``None`` or a tuple of (access time, modified time) in standard + internet epoch time (seconds since 01 January 1970 GMT) + """ + if times is None: + times = (time.time(), time.time()) + self.sftp._log(DEBUG, 'utime(%s, %r)' % (hexlify(self.handle), times)) + attr = SFTPAttributes() + attr.st_atime, attr.st_mtime = times + self.sftp._request(CMD_FSETSTAT, self.handle, attr) + + def truncate(self, size): + """ + Change the size of this file. This usually extends + or shrinks the size of the file, just like the ``truncate()`` method on + Python file objects. + + :param size: the new size of the file + :type size: int or long + """ + self.sftp._log(DEBUG, 'truncate(%s, %r)' % (hexlify(self.handle), size)) + attr = SFTPAttributes() + attr.st_size = size + self.sftp._request(CMD_FSETSTAT, self.handle, attr) + + def check(self, hash_algorithm, offset=0, length=0, block_size=0): + """ + Ask the server for a hash of a section of this file. This can be used + to verify a successful upload or download, or for various rsync-like + operations. + + The file is hashed from ``offset``, for ``length`` bytes. If ``length`` + is 0, the remainder of the file is hashed. Thus, if both ``offset`` + and ``length`` are zero, the entire file is hashed. + + Normally, ``block_size`` will be 0 (the default), and this method will + return a byte string representing the requested hash (for example, a + string of length 16 for MD5, or 20 for SHA-1). If a non-zero + ``block_size`` is given, each chunk of the file (from ``offset`` to + ``offset + length``) of ``block_size`` bytes is computed as a separate + hash. The hash results are all concatenated and returned as a single + string. + + For example, ``check('sha1', 0, 1024, 512)`` will return a string of + length 40. The first 20 bytes will be the SHA-1 of the first 512 bytes + of the file, and the last 20 bytes will be the SHA-1 of the next 512 + bytes. + + :param str hash_algorithm: + the name of the hash algorithm to use (normally ``"sha1"`` or + ``"md5"``) + :param offset: + offset into the file to begin hashing (0 means to start from the + beginning) + :type offset: int or long + :param length: + number of bytes to hash (0 means continue to the end of the file) + :type length: int or long + :param int block_size: + number of bytes to hash per result (must not be less than 256; 0 + means to compute only one hash of the entire segment) + :type block_size: int + :return: + `str` of bytes representing the hash of each block, concatenated + together + + :raises IOError: if the server doesn't support the "check-file" + extension, or possibly doesn't support the hash algorithm + requested + + .. note:: Many (most?) servers don't support this extension yet. + + .. versionadded:: 1.4 + """ + t, msg = self.sftp._request(CMD_EXTENDED, 'check-file', self.handle, + hash_algorithm, long(offset), long(length), block_size) + ext = msg.get_text() + alg = msg.get_text() + data = msg.get_remainder() + return data + + def set_pipelined(self, pipelined=True): + """ + Turn on/off the pipelining of write operations to this file. When + pipelining is on, paramiko won't wait for the server response after + each write operation. Instead, they're collected as they come in. At + the first non-write operation (including `.close`), all remaining + server responses are collected. This means that if there was an error + with one of your later writes, an exception might be thrown from within + `.close` instead of `.write`. + + By default, files are not pipelined. + + :param bool pipelined: + ``True`` if pipelining should be turned on for this file; ``False`` + otherwise + + .. versionadded:: 1.5 + """ + self.pipelined = pipelined + + def prefetch(self): + """ + Pre-fetch the remaining contents of this file in anticipation of future + `.read` calls. If reading the entire file, pre-fetching can + dramatically improve the download speed by avoiding roundtrip latency. + The file's contents are incrementally buffered in a background thread. + + The prefetched data is stored in a buffer until read via the `.read` + method. Once data has been read, it's removed from the buffer. The + data may be read in a random order (using `.seek`); chunks of the + buffer that haven't been read will continue to be buffered. + + .. versionadded:: 1.5.1 + """ + size = self.stat().st_size + # queue up async reads for the rest of the file + chunks = [] + n = self._realpos + while n < size: + chunk = min(self.MAX_REQUEST_SIZE, size - n) + chunks.append((n, chunk)) + n += chunk + if len(chunks) > 0: + self._start_prefetch(chunks) + + def readv(self, chunks): + """ + Read a set of blocks from the file by (offset, length). This is more + efficient than doing a series of `.seek` and `.read` calls, since the + prefetch machinery is used to retrieve all the requested blocks at + once. + + :param chunks: + a list of (offset, length) tuples indicating which sections of the + file to read + :type chunks: list(tuple(long, int)) + :return: a list of blocks read, in the same order as in ``chunks`` + + .. versionadded:: 1.5.4 + """ + self.sftp._log(DEBUG, 'readv(%s, %r)' % (hexlify(self.handle), chunks)) + + read_chunks = [] + for offset, size in chunks: + # don't fetch data that's already in the prefetch buffer + if self._data_in_prefetch_buffers(offset) or self._data_in_prefetch_requests(offset, size): + continue + + # break up anything larger than the max read size + while size > 0: + chunk_size = min(size, self.MAX_REQUEST_SIZE) + read_chunks.append((offset, chunk_size)) + offset += chunk_size + size -= chunk_size + + self._start_prefetch(read_chunks) + # now we can just devolve to a bunch of read()s :) + for x in chunks: + self.seek(x[0]) + yield self.read(x[1]) + + ### internals... + + def _get_size(self): + try: + return self.stat().st_size + except: + return 0 + + def _start_prefetch(self, chunks): + self._prefetching = True + self._prefetch_done = False + + t = threading.Thread(target=self._prefetch_thread, args=(chunks,)) + t.setDaemon(True) + t.start() + + def _prefetch_thread(self, chunks): + # do these read requests in a temporary thread because there may be + # a lot of them, so it may block. + for offset, length in chunks: + with self._prefetch_lock: + num = self.sftp._async_request(self, CMD_READ, self.handle, long(offset), int(length)) + self._prefetch_extents[num] = (offset, length) + + def _async_response(self, t, msg, num): + if t == CMD_STATUS: + # save exception and re-raise it on next file operation + try: + self.sftp._convert_status(msg) + except Exception as e: + self._saved_exception = e + return + if t != CMD_DATA: + raise SFTPError('Expected data') + data = msg.get_string() + with self._prefetch_lock: + offset, length = self._prefetch_extents[num] + self._prefetch_data[offset] = data + del self._prefetch_extents[num] + if len(self._prefetch_extents) == 0: + self._prefetch_done = True + + def _check_exception(self): + """if there's a saved exception, raise & clear it""" + if self._saved_exception is not None: + x = self._saved_exception + self._saved_exception = None + raise x + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + self.close() diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/sftp_handle.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/sftp_handle.py new file mode 100644 index 0000000..92dd9cf --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/sftp_handle.py @@ -0,0 +1,193 @@ +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +Abstraction of an SFTP file handle (for server mode). +""" + +import os +from paramiko.sftp import SFTP_OP_UNSUPPORTED, SFTP_OK + + +class SFTPHandle (object): + """ + Abstract object representing a handle to an open file (or folder) in an + SFTP server implementation. Each handle has a string representation used + by the client to refer to the underlying file. + + Server implementations can (and should) subclass SFTPHandle to implement + features of a file handle, like `stat` or `chattr`. + """ + def __init__(self, flags=0): + """ + Create a new file handle representing a local file being served over + SFTP. If ``flags`` is passed in, it's used to determine if the file + is open in append mode. + + :param int flags: optional flags as passed to `.SFTPServerInterface.open` + """ + self.__flags = flags + self.__name = None + # only for handles to folders: + self.__files = {} + self.__tell = None + + def close(self): + """ + When a client closes a file, this method is called on the handle. + Normally you would use this method to close the underlying OS level + file object(s). + + The default implementation checks for attributes on ``self`` named + ``readfile`` and/or ``writefile``, and if either or both are present, + their ``close()`` methods are called. This means that if you are + using the default implementations of `read` and `write`, this + method's default implementation should be fine also. + """ + readfile = getattr(self, 'readfile', None) + if readfile is not None: + readfile.close() + writefile = getattr(self, 'writefile', None) + if writefile is not None: + writefile.close() + + def read(self, offset, length): + """ + Read up to ``length`` bytes from this file, starting at position + ``offset``. The offset may be a Python long, since SFTP allows it + to be 64 bits. + + If the end of the file has been reached, this method may return an + empty string to signify EOF, or it may also return `.SFTP_EOF`. + + The default implementation checks for an attribute on ``self`` named + ``readfile``, and if present, performs the read operation on the Python + file-like object found there. (This is meant as a time saver for the + common case where you are wrapping a Python file object.) + + :param offset: position in the file to start reading from. + :type offset: int or long + :param int length: number of bytes to attempt to read. + :return: data read from the file, or an SFTP error code, as a `str`. + """ + readfile = getattr(self, 'readfile', None) + if readfile is None: + return SFTP_OP_UNSUPPORTED + try: + if self.__tell is None: + self.__tell = readfile.tell() + if offset != self.__tell: + readfile.seek(offset) + self.__tell = offset + data = readfile.read(length) + except IOError as e: + self.__tell = None + return SFTPServer.convert_errno(e.errno) + self.__tell += len(data) + return data + + def write(self, offset, data): + """ + Write ``data`` into this file at position ``offset``. Extending the + file past its original end is expected. Unlike Python's normal + ``write()`` methods, this method cannot do a partial write: it must + write all of ``data`` or else return an error. + + The default implementation checks for an attribute on ``self`` named + ``writefile``, and if present, performs the write operation on the + Python file-like object found there. The attribute is named + differently from ``readfile`` to make it easy to implement read-only + (or write-only) files, but if both attributes are present, they should + refer to the same file. + + :param offset: position in the file to start reading from. + :type offset: int or long + :param str data: data to write into the file. + :return: an SFTP error code like `.SFTP_OK`. + """ + writefile = getattr(self, 'writefile', None) + if writefile is None: + return SFTP_OP_UNSUPPORTED + try: + # in append mode, don't care about seeking + if (self.__flags & os.O_APPEND) == 0: + if self.__tell is None: + self.__tell = writefile.tell() + if offset != self.__tell: + writefile.seek(offset) + self.__tell = offset + writefile.write(data) + writefile.flush() + except IOError as e: + self.__tell = None + return SFTPServer.convert_errno(e.errno) + if self.__tell is not None: + self.__tell += len(data) + return SFTP_OK + + def stat(self): + """ + Return an `.SFTPAttributes` object referring to this open file, or an + error code. This is equivalent to `.SFTPServerInterface.stat`, except + it's called on an open file instead of a path. + + :return: + an attributes object for the given file, or an SFTP error code + (like `.SFTP_PERMISSION_DENIED`). + :rtype: `.SFTPAttributes` or error code + """ + return SFTP_OP_UNSUPPORTED + + def chattr(self, attr): + """ + Change the attributes of this file. The ``attr`` object will contain + only those fields provided by the client in its request, so you should + check for the presence of fields before using them. + + :param .SFTPAttributes attr: the attributes to change on this file. + :return: an `int` error code like `.SFTP_OK`. + """ + return SFTP_OP_UNSUPPORTED + + ### internals... + + def _set_files(self, files): + """ + Used by the SFTP server code to cache a directory listing. (In + the SFTP protocol, listing a directory is a multi-stage process + requiring a temporary handle.) + """ + self.__files = files + + def _get_next_files(self): + """ + Used by the SFTP server code to retreive a cached directory + listing. + """ + fnlist = self.__files[:16] + self.__files = self.__files[16:] + return fnlist + + def _get_name(self): + return self.__name + + def _set_name(self, name): + self.__name = name + + +from paramiko.sftp_server import SFTPServer diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/sftp_server.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/sftp_server.py new file mode 100644 index 0000000..2d8d190 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/sftp_server.py @@ -0,0 +1,448 @@ +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +Server-mode SFTP support. +""" + +import os +import errno +import sys +from hashlib import md5, sha1 + +from paramiko import util +from paramiko.sftp import BaseSFTP, Message, SFTP_FAILURE, \ + SFTP_PERMISSION_DENIED, SFTP_NO_SUCH_FILE +from paramiko.sftp_si import SFTPServerInterface +from paramiko.sftp_attr import SFTPAttributes +from paramiko.common import DEBUG +from paramiko.py3compat import long, string_types, bytes_types, b +from paramiko.server import SubsystemHandler + + +# known hash algorithms for the "check-file" extension +from paramiko.sftp import CMD_HANDLE, SFTP_DESC, CMD_STATUS, SFTP_EOF, CMD_NAME, \ + SFTP_BAD_MESSAGE, CMD_EXTENDED_REPLY, SFTP_FLAG_READ, SFTP_FLAG_WRITE, \ + SFTP_FLAG_APPEND, SFTP_FLAG_CREATE, SFTP_FLAG_TRUNC, SFTP_FLAG_EXCL, \ + CMD_NAMES, CMD_OPEN, CMD_CLOSE, SFTP_OK, CMD_READ, CMD_DATA, CMD_WRITE, \ + CMD_REMOVE, CMD_RENAME, CMD_MKDIR, CMD_RMDIR, CMD_OPENDIR, CMD_READDIR, \ + CMD_STAT, CMD_ATTRS, CMD_LSTAT, CMD_FSTAT, CMD_SETSTAT, CMD_FSETSTAT, \ + CMD_READLINK, CMD_SYMLINK, CMD_REALPATH, CMD_EXTENDED, SFTP_OP_UNSUPPORTED + +_hash_class = { + 'sha1': sha1, + 'md5': md5, +} + + +class SFTPServer (BaseSFTP, SubsystemHandler): + """ + Server-side SFTP subsystem support. Since this is a `.SubsystemHandler`, + it can be (and is meant to be) set as the handler for ``"sftp"`` requests. + Use `.Transport.set_subsystem_handler` to activate this class. + """ + + def __init__(self, channel, name, server, sftp_si=SFTPServerInterface, *largs, **kwargs): + """ + The constructor for SFTPServer is meant to be called from within the + `.Transport` as a subsystem handler. ``server`` and any additional + parameters or keyword parameters are passed from the original call to + `.Transport.set_subsystem_handler`. + + :param .Channel channel: channel passed from the `.Transport`. + :param str name: name of the requested subsystem. + :param .ServerInterface server: + the server object associated with this channel and subsystem + :param class sftp_si: + a subclass of `.SFTPServerInterface` to use for handling individual + requests. + """ + BaseSFTP.__init__(self) + SubsystemHandler.__init__(self, channel, name, server) + transport = channel.get_transport() + self.logger = util.get_logger(transport.get_log_channel() + '.sftp') + self.ultra_debug = transport.get_hexdump() + self.next_handle = 1 + # map of handle-string to SFTPHandle for files & folders: + self.file_table = {} + self.folder_table = {} + self.server = sftp_si(server, *largs, **kwargs) + + def _log(self, level, msg): + if issubclass(type(msg), list): + for m in msg: + super(SFTPServer, self)._log(level, "[chan " + self.sock.get_name() + "] " + m) + else: + super(SFTPServer, self)._log(level, "[chan " + self.sock.get_name() + "] " + msg) + + def start_subsystem(self, name, transport, channel): + self.sock = channel + self._log(DEBUG, 'Started sftp server on channel %s' % repr(channel)) + self._send_server_version() + self.server.session_started() + while True: + try: + t, data = self._read_packet() + except EOFError: + self._log(DEBUG, 'EOF -- end of session') + return + except Exception as e: + self._log(DEBUG, 'Exception on channel: ' + str(e)) + self._log(DEBUG, util.tb_strings()) + return + msg = Message(data) + request_number = msg.get_int() + try: + self._process(t, request_number, msg) + except Exception as e: + self._log(DEBUG, 'Exception in server processing: ' + str(e)) + self._log(DEBUG, util.tb_strings()) + # send some kind of failure message, at least + try: + self._send_status(request_number, SFTP_FAILURE) + except: + pass + + def finish_subsystem(self): + self.server.session_ended() + super(SFTPServer, self).finish_subsystem() + # close any file handles that were left open (so we can return them to the OS quickly) + for f in self.file_table.values(): + f.close() + for f in self.folder_table.values(): + f.close() + self.file_table = {} + self.folder_table = {} + + def convert_errno(e): + """ + Convert an errno value (as from an ``OSError`` or ``IOError``) into a + standard SFTP result code. This is a convenience function for trapping + exceptions in server code and returning an appropriate result. + + :param int e: an errno code, as from ``OSError.errno``. + :return: an `int` SFTP error code like ``SFTP_NO_SUCH_FILE``. + """ + if e == errno.EACCES: + # permission denied + return SFTP_PERMISSION_DENIED + elif (e == errno.ENOENT) or (e == errno.ENOTDIR): + # no such file + return SFTP_NO_SUCH_FILE + else: + return SFTP_FAILURE + convert_errno = staticmethod(convert_errno) + + def set_file_attr(filename, attr): + """ + Change a file's attributes on the local filesystem. The contents of + ``attr`` are used to change the permissions, owner, group ownership, + and/or modification & access time of the file, depending on which + attributes are present in ``attr``. + + This is meant to be a handy helper function for translating SFTP file + requests into local file operations. + + :param str filename: + name of the file to alter (should usually be an absolute path). + :param .SFTPAttributes attr: attributes to change. + """ + if sys.platform != 'win32': + # mode operations are meaningless on win32 + if attr._flags & attr.FLAG_PERMISSIONS: + os.chmod(filename, attr.st_mode) + if attr._flags & attr.FLAG_UIDGID: + os.chown(filename, attr.st_uid, attr.st_gid) + if attr._flags & attr.FLAG_AMTIME: + os.utime(filename, (attr.st_atime, attr.st_mtime)) + if attr._flags & attr.FLAG_SIZE: + with open(filename, 'w+') as f: + f.truncate(attr.st_size) + set_file_attr = staticmethod(set_file_attr) + + ### internals... + + def _response(self, request_number, t, *arg): + msg = Message() + msg.add_int(request_number) + for item in arg: + if isinstance(item, long): + msg.add_int64(item) + elif isinstance(item, int): + msg.add_int(item) + elif isinstance(item, (string_types, bytes_types)): + msg.add_string(item) + elif type(item) is SFTPAttributes: + item._pack(msg) + else: + raise Exception('unknown type for ' + repr(item) + ' type ' + repr(type(item))) + self._send_packet(t, msg) + + def _send_handle_response(self, request_number, handle, folder=False): + if not issubclass(type(handle), SFTPHandle): + # must be error code + self._send_status(request_number, handle) + return + handle._set_name(b('hx%d' % self.next_handle)) + self.next_handle += 1 + if folder: + self.folder_table[handle._get_name()] = handle + else: + self.file_table[handle._get_name()] = handle + self._response(request_number, CMD_HANDLE, handle._get_name()) + + def _send_status(self, request_number, code, desc=None): + if desc is None: + try: + desc = SFTP_DESC[code] + except IndexError: + desc = 'Unknown' + # some clients expect a "langauge" tag at the end (but don't mind it being blank) + self._response(request_number, CMD_STATUS, code, desc, '') + + def _open_folder(self, request_number, path): + resp = self.server.list_folder(path) + if issubclass(type(resp), list): + # got an actual list of filenames in the folder + folder = SFTPHandle() + folder._set_files(resp) + self._send_handle_response(request_number, folder, True) + return + # must be an error code + self._send_status(request_number, resp) + + def _read_folder(self, request_number, folder): + flist = folder._get_next_files() + if len(flist) == 0: + self._send_status(request_number, SFTP_EOF) + return + msg = Message() + msg.add_int(request_number) + msg.add_int(len(flist)) + for attr in flist: + msg.add_string(attr.filename) + msg.add_string(attr) + attr._pack(msg) + self._send_packet(CMD_NAME, msg) + + def _check_file(self, request_number, msg): + # this extension actually comes from v6 protocol, but since it's an + # extension, i feel like we can reasonably support it backported. + # it's very useful for verifying uploaded files or checking for + # rsync-like differences between local and remote files. + handle = msg.get_binary() + alg_list = msg.get_list() + start = msg.get_int64() + length = msg.get_int64() + block_size = msg.get_int() + if handle not in self.file_table: + self._send_status(request_number, SFTP_BAD_MESSAGE, 'Invalid handle') + return + f = self.file_table[handle] + for x in alg_list: + if x in _hash_class: + algname = x + alg = _hash_class[x] + break + else: + self._send_status(request_number, SFTP_FAILURE, 'No supported hash types found') + return + if length == 0: + st = f.stat() + if not issubclass(type(st), SFTPAttributes): + self._send_status(request_number, st, 'Unable to stat file') + return + length = st.st_size - start + if block_size == 0: + block_size = length + if block_size < 256: + self._send_status(request_number, SFTP_FAILURE, 'Block size too small') + return + + sum_out = bytes() + offset = start + while offset < start + length: + blocklen = min(block_size, start + length - offset) + # don't try to read more than about 64KB at a time + chunklen = min(blocklen, 65536) + count = 0 + hash_obj = alg() + while count < blocklen: + data = f.read(offset, chunklen) + if not isinstance(data, bytes_types): + self._send_status(request_number, data, 'Unable to hash file') + return + hash_obj.update(data) + count += len(data) + offset += count + sum_out += hash_obj.digest() + + msg = Message() + msg.add_int(request_number) + msg.add_string('check-file') + msg.add_string(algname) + msg.add_bytes(sum_out) + self._send_packet(CMD_EXTENDED_REPLY, msg) + + def _convert_pflags(self, pflags): + """convert SFTP-style open() flags to Python's os.open() flags""" + if (pflags & SFTP_FLAG_READ) and (pflags & SFTP_FLAG_WRITE): + flags = os.O_RDWR + elif pflags & SFTP_FLAG_WRITE: + flags = os.O_WRONLY + else: + flags = os.O_RDONLY + if pflags & SFTP_FLAG_APPEND: + flags |= os.O_APPEND + if pflags & SFTP_FLAG_CREATE: + flags |= os.O_CREAT + if pflags & SFTP_FLAG_TRUNC: + flags |= os.O_TRUNC + if pflags & SFTP_FLAG_EXCL: + flags |= os.O_EXCL + return flags + + def _process(self, t, request_number, msg): + self._log(DEBUG, 'Request: %s' % CMD_NAMES[t]) + if t == CMD_OPEN: + path = msg.get_text() + flags = self._convert_pflags(msg.get_int()) + attr = SFTPAttributes._from_msg(msg) + self._send_handle_response(request_number, self.server.open(path, flags, attr)) + elif t == CMD_CLOSE: + handle = msg.get_binary() + if handle in self.folder_table: + del self.folder_table[handle] + self._send_status(request_number, SFTP_OK) + return + if handle in self.file_table: + self.file_table[handle].close() + del self.file_table[handle] + self._send_status(request_number, SFTP_OK) + return + self._send_status(request_number, SFTP_BAD_MESSAGE, 'Invalid handle') + elif t == CMD_READ: + handle = msg.get_binary() + offset = msg.get_int64() + length = msg.get_int() + if handle not in self.file_table: + self._send_status(request_number, SFTP_BAD_MESSAGE, 'Invalid handle') + return + data = self.file_table[handle].read(offset, length) + if isinstance(data, (bytes_types, string_types)): + if len(data) == 0: + self._send_status(request_number, SFTP_EOF) + else: + self._response(request_number, CMD_DATA, data) + else: + self._send_status(request_number, data) + elif t == CMD_WRITE: + handle = msg.get_binary() + offset = msg.get_int64() + data = msg.get_binary() + if handle not in self.file_table: + self._send_status(request_number, SFTP_BAD_MESSAGE, 'Invalid handle') + return + self._send_status(request_number, self.file_table[handle].write(offset, data)) + elif t == CMD_REMOVE: + path = msg.get_text() + self._send_status(request_number, self.server.remove(path)) + elif t == CMD_RENAME: + oldpath = msg.get_text() + newpath = msg.get_text() + self._send_status(request_number, self.server.rename(oldpath, newpath)) + elif t == CMD_MKDIR: + path = msg.get_text() + attr = SFTPAttributes._from_msg(msg) + self._send_status(request_number, self.server.mkdir(path, attr)) + elif t == CMD_RMDIR: + path = msg.get_text() + self._send_status(request_number, self.server.rmdir(path)) + elif t == CMD_OPENDIR: + path = msg.get_text() + self._open_folder(request_number, path) + return + elif t == CMD_READDIR: + handle = msg.get_binary() + if handle not in self.folder_table: + self._send_status(request_number, SFTP_BAD_MESSAGE, 'Invalid handle') + return + folder = self.folder_table[handle] + self._read_folder(request_number, folder) + elif t == CMD_STAT: + path = msg.get_text() + resp = self.server.stat(path) + if issubclass(type(resp), SFTPAttributes): + self._response(request_number, CMD_ATTRS, resp) + else: + self._send_status(request_number, resp) + elif t == CMD_LSTAT: + path = msg.get_text() + resp = self.server.lstat(path) + if issubclass(type(resp), SFTPAttributes): + self._response(request_number, CMD_ATTRS, resp) + else: + self._send_status(request_number, resp) + elif t == CMD_FSTAT: + handle = msg.get_binary() + if handle not in self.file_table: + self._send_status(request_number, SFTP_BAD_MESSAGE, 'Invalid handle') + return + resp = self.file_table[handle].stat() + if issubclass(type(resp), SFTPAttributes): + self._response(request_number, CMD_ATTRS, resp) + else: + self._send_status(request_number, resp) + elif t == CMD_SETSTAT: + path = msg.get_text() + attr = SFTPAttributes._from_msg(msg) + self._send_status(request_number, self.server.chattr(path, attr)) + elif t == CMD_FSETSTAT: + handle = msg.get_binary() + attr = SFTPAttributes._from_msg(msg) + if handle not in self.file_table: + self._response(request_number, SFTP_BAD_MESSAGE, 'Invalid handle') + return + self._send_status(request_number, self.file_table[handle].chattr(attr)) + elif t == CMD_READLINK: + path = msg.get_text() + resp = self.server.readlink(path) + if isinstance(resp, (bytes_types, string_types)): + self._response(request_number, CMD_NAME, 1, resp, '', SFTPAttributes()) + else: + self._send_status(request_number, resp) + elif t == CMD_SYMLINK: + # the sftp 2 draft is incorrect here! path always follows target_path + target_path = msg.get_text() + path = msg.get_text() + self._send_status(request_number, self.server.symlink(target_path, path)) + elif t == CMD_REALPATH: + path = msg.get_text() + rpath = self.server.canonicalize(path) + self._response(request_number, CMD_NAME, 1, rpath, '', SFTPAttributes()) + elif t == CMD_EXTENDED: + tag = msg.get_text() + if tag == 'check-file': + self._check_file(request_number, msg) + else: + self._send_status(request_number, SFTP_OP_UNSUPPORTED) + else: + self._send_status(request_number, SFTP_OP_UNSUPPORTED) + + +from paramiko.sftp_handle import SFTPHandle diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/sftp_si.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/sftp_si.py new file mode 100644 index 0000000..61db956 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/sftp_si.py @@ -0,0 +1,296 @@ +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +An interface to override for SFTP server support. +""" + +import os +import sys +from paramiko.sftp import SFTP_OP_UNSUPPORTED + + +class SFTPServerInterface (object): + """ + This class defines an interface for controlling the behavior of paramiko + when using the `.SFTPServer` subsystem to provide an SFTP server. + + Methods on this class are called from the SFTP session's thread, so you can + block as long as necessary without affecting other sessions (even other + SFTP sessions). However, raising an exception will usually cause the SFTP + session to abruptly end, so you will usually want to catch exceptions and + return an appropriate error code. + + All paths are in string form instead of unicode because not all SFTP + clients & servers obey the requirement that paths be encoded in UTF-8. + """ + + def __init__(self, server, *largs, **kwargs): + """ + Create a new SFTPServerInterface object. This method does nothing by + default and is meant to be overridden by subclasses. + + :param .ServerInterface server: + the server object associated with this channel and SFTP subsystem + """ + super(SFTPServerInterface, self).__init__(*largs, **kwargs) + + def session_started(self): + """ + The SFTP server session has just started. This method is meant to be + overridden to perform any necessary setup before handling callbacks + from SFTP operations. + """ + pass + + def session_ended(self): + """ + The SFTP server session has just ended, either cleanly or via an + exception. This method is meant to be overridden to perform any + necessary cleanup before this `.SFTPServerInterface` object is + destroyed. + """ + pass + + def open(self, path, flags, attr): + """ + Open a file on the server and create a handle for future operations + on that file. On success, a new object subclassed from `.SFTPHandle` + should be returned. This handle will be used for future operations + on the file (read, write, etc). On failure, an error code such as + `.SFTP_PERMISSION_DENIED` should be returned. + + ``flags`` contains the requested mode for opening (read-only, + write-append, etc) as a bitset of flags from the ``os`` module: + + - ``os.O_RDONLY`` + - ``os.O_WRONLY`` + - ``os.O_RDWR`` + - ``os.O_APPEND`` + - ``os.O_CREAT`` + - ``os.O_TRUNC`` + - ``os.O_EXCL`` + + (One of ``os.O_RDONLY``, ``os.O_WRONLY``, or ``os.O_RDWR`` will always + be set.) + + The ``attr`` object contains requested attributes of the file if it + has to be created. Some or all attribute fields may be missing if + the client didn't specify them. + + .. note:: The SFTP protocol defines all files to be in "binary" mode. + There is no equivalent to Python's "text" mode. + + :param str path: + the requested path (relative or absolute) of the file to be opened. + :param int flags: + flags or'd together from the ``os`` module indicating the requested + mode for opening the file. + :param .SFTPAttributes attr: + requested attributes of the file if it is newly created. + :return: a new `.SFTPHandle` or error code. + """ + return SFTP_OP_UNSUPPORTED + + def list_folder(self, path): + """ + Return a list of files within a given folder. The ``path`` will use + posix notation (``"/"`` separates folder names) and may be an absolute + or relative path. + + The list of files is expected to be a list of `.SFTPAttributes` + objects, which are similar in structure to the objects returned by + ``os.stat``. In addition, each object should have its ``filename`` + field filled in, since this is important to a directory listing and + not normally present in ``os.stat`` results. The method + `.SFTPAttributes.from_stat` will usually do what you want. + + In case of an error, you should return one of the ``SFTP_*`` error + codes, such as `.SFTP_PERMISSION_DENIED`. + + :param str path: the requested path (relative or absolute) to be listed. + :return: + a list of the files in the given folder, using `.SFTPAttributes` + objects. + + .. note:: + You should normalize the given ``path`` first (see the `os.path` + module) and check appropriate permissions before returning the list + of files. Be careful of malicious clients attempting to use + relative paths to escape restricted folders, if you're doing a + direct translation from the SFTP server path to your local + filesystem. + """ + return SFTP_OP_UNSUPPORTED + + def stat(self, path): + """ + Return an `.SFTPAttributes` object for a path on the server, or an + error code. If your server supports symbolic links (also known as + "aliases"), you should follow them. (`lstat` is the corresponding + call that doesn't follow symlinks/aliases.) + + :param str path: + the requested path (relative or absolute) to fetch file statistics + for. + :return: + an `.SFTPAttributes` object for the given file, or an SFTP error + code (like `.SFTP_PERMISSION_DENIED`). + """ + return SFTP_OP_UNSUPPORTED + + def lstat(self, path): + """ + Return an `.SFTPAttributes` object for a path on the server, or an + error code. If your server supports symbolic links (also known as + "aliases"), you should not follow them -- instead, you should + return data on the symlink or alias itself. (`stat` is the + corresponding call that follows symlinks/aliases.) + + :param str path: + the requested path (relative or absolute) to fetch file statistics + for. + :type path: str + :return: + an `.SFTPAttributes` object for the given file, or an SFTP error + code (like `.SFTP_PERMISSION_DENIED`). + """ + return SFTP_OP_UNSUPPORTED + + def remove(self, path): + """ + Delete a file, if possible. + + :param str path: + the requested path (relative or absolute) of the file to delete. + :return: an SFTP error code `int` like `.SFTP_OK`. + """ + return SFTP_OP_UNSUPPORTED + + def rename(self, oldpath, newpath): + """ + Rename (or move) a file. The SFTP specification implies that this + method can be used to move an existing file into a different folder, + and since there's no other (easy) way to move files via SFTP, it's + probably a good idea to implement "move" in this method too, even for + files that cross disk partition boundaries, if at all possible. + + .. note:: You should return an error if a file with the same name as + ``newpath`` already exists. (The rename operation should be + non-desctructive.) + + :param str oldpath: + the requested path (relative or absolute) of the existing file. + :param str newpath: the requested new path of the file. + :return: an SFTP error code `int` like `.SFTP_OK`. + """ + return SFTP_OP_UNSUPPORTED + + def mkdir(self, path, attr): + """ + Create a new directory with the given attributes. The ``attr`` + object may be considered a "hint" and ignored. + + The ``attr`` object will contain only those fields provided by the + client in its request, so you should use ``hasattr`` to check for + the presense of fields before using them. In some cases, the ``attr`` + object may be completely empty. + + :param str path: + requested path (relative or absolute) of the new folder. + :param .SFTPAttributes attr: requested attributes of the new folder. + :return: an SFTP error code `int` like `.SFTP_OK`. + """ + return SFTP_OP_UNSUPPORTED + + def rmdir(self, path): + """ + Remove a directory if it exists. The ``path`` should refer to an + existing, empty folder -- otherwise this method should return an + error. + + :param str path: + requested path (relative or absolute) of the folder to remove. + :return: an SFTP error code `int` like `.SFTP_OK`. + """ + return SFTP_OP_UNSUPPORTED + + def chattr(self, path, attr): + """ + Change the attributes of a file. The ``attr`` object will contain + only those fields provided by the client in its request, so you + should check for the presence of fields before using them. + + :param str path: + requested path (relative or absolute) of the file to change. + :param attr: + requested attributes to change on the file (an `.SFTPAttributes` + object) + :return: an error code `int` like `.SFTP_OK`. + """ + return SFTP_OP_UNSUPPORTED + + def canonicalize(self, path): + """ + Return the canonical form of a path on the server. For example, + if the server's home folder is ``/home/foo``, the path + ``"../betty"`` would be canonicalized to ``"/home/betty"``. Note + the obvious security issues: if you're serving files only from a + specific folder, you probably don't want this method to reveal path + names outside that folder. + + You may find the Python methods in ``os.path`` useful, especially + ``os.path.normpath`` and ``os.path.realpath``. + + The default implementation returns ``os.path.normpath('/' + path)``. + """ + if os.path.isabs(path): + out = os.path.normpath(path) + else: + out = os.path.normpath('/' + path) + if sys.platform == 'win32': + # on windows, normalize backslashes to sftp/posix format + out = out.replace('\\', '/') + return out + + def readlink(self, path): + """ + Return the target of a symbolic link (or shortcut) on the server. + If the specified path doesn't refer to a symbolic link, an error + should be returned. + + :param str path: path (relative or absolute) of the symbolic link. + :return: + the target `str` path of the symbolic link, or an error code like + `.SFTP_NO_SUCH_FILE`. + """ + return SFTP_OP_UNSUPPORTED + + def symlink(self, target_path, path): + """ + Create a symbolic link on the server, as new pathname ``path``, + with ``target_path`` as the target of the link. + + :param str target_path: + path (relative or absolute) of the target for this new symbolic + link. + :param str path: + path (relative or absolute) of the symbolic link to create. + :return: an error code `int` like ``SFTP_OK``. + """ + return SFTP_OP_UNSUPPORTED diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/ssh_exception.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/ssh_exception.py new file mode 100644 index 0000000..b99e42b --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/ssh_exception.py @@ -0,0 +1,131 @@ +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + + +class SSHException (Exception): + """ + Exception raised by failures in SSH2 protocol negotiation or logic errors. + """ + pass + + +class AuthenticationException (SSHException): + """ + Exception raised when authentication failed for some reason. It may be + possible to retry with different credentials. (Other classes specify more + specific reasons.) + + .. versionadded:: 1.6 + """ + pass + + +class PasswordRequiredException (AuthenticationException): + """ + Exception raised when a password is needed to unlock a private key file. + """ + pass + + +class BadAuthenticationType (AuthenticationException): + """ + Exception raised when an authentication type (like password) is used, but + the server isn't allowing that type. (It may only allow public-key, for + example.) + + :ivar list allowed_types: + list of allowed authentication types provided by the server (possible + values are: ``"none"``, ``"password"``, and ``"publickey"``). + + .. versionadded:: 1.1 + """ + allowed_types = [] + + def __init__(self, explanation, types): + AuthenticationException.__init__(self, explanation) + self.allowed_types = types + # for unpickling + self.args = (explanation, types, ) + + def __str__(self): + return SSHException.__str__(self) + ' (allowed_types=%r)' % self.allowed_types + + +class PartialAuthentication (AuthenticationException): + """ + An internal exception thrown in the case of partial authentication. + """ + allowed_types = [] + + def __init__(self, types): + AuthenticationException.__init__(self, 'partial authentication') + self.allowed_types = types + # for unpickling + self.args = (types, ) + + +class ChannelException (SSHException): + """ + Exception raised when an attempt to open a new `.Channel` fails. + + :ivar int code: the error code returned by the server + + .. versionadded:: 1.6 + """ + def __init__(self, code, text): + SSHException.__init__(self, text) + self.code = code + # for unpickling + self.args = (code, text, ) + + +class BadHostKeyException (SSHException): + """ + The host key given by the SSH server did not match what we were expecting. + + :ivar str hostname: the hostname of the SSH server + :ivar PKey got_key: the host key presented by the server + :ivar PKey expected_key: the host key expected + + .. versionadded:: 1.6 + """ + def __init__(self, hostname, got_key, expected_key): + SSHException.__init__(self, 'Host key for server %s does not match!' % hostname) + self.hostname = hostname + self.key = got_key + self.expected_key = expected_key + # for unpickling + self.args = (hostname, got_key, expected_key, ) + + +class ProxyCommandFailure (SSHException): + """ + The "ProxyCommand" found in the .ssh/config file returned an error. + + :ivar str command: The command line that is generating this exception. + :ivar str error: The error captured from the proxy command output. + """ + def __init__(self, command, error): + SSHException.__init__(self, + '"ProxyCommand (%s)" returned non-zero exit status: %s' % ( + command, error + ) + ) + self.error = error + # for unpickling + self.args = (command, error, ) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/transport.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/transport.py new file mode 100644 index 0000000..406626a --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/transport.py @@ -0,0 +1,2134 @@ +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +Core protocol implementation +""" + +import os +import socket +import sys +import threading +import time +import weakref +from hashlib import md5, sha1 + +import paramiko +from paramiko import util +from paramiko.auth_handler import AuthHandler +from paramiko.channel import Channel +from paramiko.common import xffffffff, cMSG_CHANNEL_OPEN, cMSG_IGNORE, \ + cMSG_GLOBAL_REQUEST, DEBUG, MSG_KEXINIT, MSG_IGNORE, MSG_DISCONNECT, \ + MSG_DEBUG, ERROR, WARNING, cMSG_UNIMPLEMENTED, INFO, cMSG_KEXINIT, \ + cMSG_NEWKEYS, MSG_NEWKEYS, cMSG_REQUEST_SUCCESS, cMSG_REQUEST_FAILURE, \ + CONNECTION_FAILED_CODE, OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED, \ + OPEN_SUCCEEDED, cMSG_CHANNEL_OPEN_FAILURE, cMSG_CHANNEL_OPEN_SUCCESS, \ + MSG_GLOBAL_REQUEST, MSG_REQUEST_SUCCESS, MSG_REQUEST_FAILURE, \ + MSG_CHANNEL_OPEN_SUCCESS, MSG_CHANNEL_OPEN_FAILURE, MSG_CHANNEL_OPEN, \ + MSG_CHANNEL_SUCCESS, MSG_CHANNEL_FAILURE, MSG_CHANNEL_DATA, \ + MSG_CHANNEL_EXTENDED_DATA, MSG_CHANNEL_WINDOW_ADJUST, MSG_CHANNEL_REQUEST, \ + MSG_CHANNEL_EOF, MSG_CHANNEL_CLOSE +from paramiko.compress import ZlibCompressor, ZlibDecompressor +from paramiko.dsskey import DSSKey +from paramiko.kex_gex import KexGex +from paramiko.kex_group1 import KexGroup1 +from paramiko.message import Message +from paramiko.packet import Packetizer, NeedRekeyException +from paramiko.primes import ModulusPack +from paramiko.py3compat import string_types, long, byte_ord, b +from paramiko.rsakey import RSAKey +from paramiko.ecdsakey import ECDSAKey +from paramiko.server import ServerInterface +from paramiko.sftp_client import SFTPClient +from paramiko.ssh_exception import (SSHException, BadAuthenticationType, + ChannelException, ProxyCommandFailure) +from paramiko.util import retry_on_signal + +from Crypto.Cipher import Blowfish, AES, DES3, ARC4 +try: + from Crypto.Util import Counter +except ImportError: + from paramiko.util import Counter + + +# for thread cleanup +_active_threads = [] + +def _join_lingering_threads(): + for thr in _active_threads: + thr.stop_thread() + +import atexit +atexit.register(_join_lingering_threads) + + +class Transport (threading.Thread): + """ + An SSH Transport attaches to a stream (usually a socket), negotiates an + encrypted session, authenticates, and then creates stream tunnels, called + `channels <.Channel>`, across the session. Multiple channels can be + multiplexed across a single session (and often are, in the case of port + forwardings). + """ + _PROTO_ID = '2.0' + _CLIENT_ID = 'paramiko_%s' % paramiko.__version__ + + _preferred_ciphers = ('aes128-ctr', 'aes256-ctr', 'aes128-cbc', 'blowfish-cbc', + 'aes256-cbc', '3des-cbc', 'arcfour128', 'arcfour256') + _preferred_macs = ('hmac-sha1', 'hmac-md5', 'hmac-sha1-96', 'hmac-md5-96') + _preferred_keys = ('ssh-rsa', 'ssh-dss', 'ecdsa-sha2-nistp256') + _preferred_kex = ('diffie-hellman-group1-sha1', 'diffie-hellman-group-exchange-sha1') + _preferred_compression = ('none',) + + _cipher_info = { + 'aes128-ctr': {'class': AES, 'mode': AES.MODE_CTR, 'block-size': 16, 'key-size': 16}, + 'aes256-ctr': {'class': AES, 'mode': AES.MODE_CTR, 'block-size': 16, 'key-size': 32}, + 'blowfish-cbc': {'class': Blowfish, 'mode': Blowfish.MODE_CBC, 'block-size': 8, 'key-size': 16}, + 'aes128-cbc': {'class': AES, 'mode': AES.MODE_CBC, 'block-size': 16, 'key-size': 16}, + 'aes256-cbc': {'class': AES, 'mode': AES.MODE_CBC, 'block-size': 16, 'key-size': 32}, + '3des-cbc': {'class': DES3, 'mode': DES3.MODE_CBC, 'block-size': 8, 'key-size': 24}, + 'arcfour128': {'class': ARC4, 'mode': None, 'block-size': 8, 'key-size': 16}, + 'arcfour256': {'class': ARC4, 'mode': None, 'block-size': 8, 'key-size': 32}, + } + + _mac_info = { + 'hmac-sha1': {'class': sha1, 'size': 20}, + 'hmac-sha1-96': {'class': sha1, 'size': 12}, + 'hmac-md5': {'class': md5, 'size': 16}, + 'hmac-md5-96': {'class': md5, 'size': 12}, + } + + _key_info = { + 'ssh-rsa': RSAKey, + 'ssh-dss': DSSKey, + 'ecdsa-sha2-nistp256': ECDSAKey, + } + + _kex_info = { + 'diffie-hellman-group1-sha1': KexGroup1, + 'diffie-hellman-group-exchange-sha1': KexGex, + } + + _compression_info = { + # zlib@openssh.com is just zlib, but only turned on after a successful + # authentication. openssh servers may only offer this type because + # they've had troubles with security holes in zlib in the past. + 'zlib@openssh.com': (ZlibCompressor, ZlibDecompressor), + 'zlib': (ZlibCompressor, ZlibDecompressor), + 'none': (None, None), + } + + _modulus_pack = None + + def __init__(self, sock): + """ + Create a new SSH session over an existing socket, or socket-like + object. This only creates the `.Transport` object; it doesn't begin the + SSH session yet. Use `connect` or `start_client` to begin a client + session, or `start_server` to begin a server session. + + If the object is not actually a socket, it must have the following + methods: + + - ``send(str)``: Writes from 1 to ``len(str)`` bytes, and returns an + int representing the number of bytes written. Returns + 0 or raises ``EOFError`` if the stream has been closed. + - ``recv(int)``: Reads from 1 to ``int`` bytes and returns them as a + string. Returns 0 or raises ``EOFError`` if the stream has been + closed. + - ``close()``: Closes the socket. + - ``settimeout(n)``: Sets a (float) timeout on I/O operations. + + For ease of use, you may also pass in an address (as a tuple) or a host + string as the ``sock`` argument. (A host string is a hostname with an + optional port (separated by ``":"``) which will be converted into a + tuple of ``(hostname, port)``.) A socket will be connected to this + address and used for communication. Exceptions from the ``socket`` + call may be thrown in this case. + + :param socket sock: + a socket or socket-like object to create the session over. + """ + if isinstance(sock, string_types): + # convert "host:port" into (host, port) + hl = sock.split(':', 1) + if len(hl) == 1: + sock = (hl[0], 22) + else: + sock = (hl[0], int(hl[1])) + if type(sock) is tuple: + # connect to the given (host, port) + hostname, port = sock + reason = 'No suitable address family' + for (family, socktype, proto, canonname, sockaddr) in socket.getaddrinfo(hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM): + if socktype == socket.SOCK_STREAM: + af = family + addr = sockaddr + sock = socket.socket(af, socket.SOCK_STREAM) + try: + retry_on_signal(lambda: sock.connect((hostname, port))) + except socket.error as e: + reason = str(e) + else: + break + else: + raise SSHException( + 'Unable to connect to %s: %s' % (hostname, reason)) + # okay, normal socket-ish flow here... + threading.Thread.__init__(self) + self.setDaemon(True) + self.sock = sock + # Python < 2.3 doesn't have the settimeout method - RogerB + try: + # we set the timeout so we can check self.active periodically to + # see if we should bail. socket.timeout exception is never + # propagated. + self.sock.settimeout(0.1) + except AttributeError: + pass + + # negotiated crypto parameters + self.packetizer = Packetizer(sock) + self.local_version = 'SSH-' + self._PROTO_ID + '-' + self._CLIENT_ID + self.remote_version = '' + self.local_cipher = self.remote_cipher = '' + self.local_kex_init = self.remote_kex_init = None + self.local_mac = self.remote_mac = None + self.local_compression = self.remote_compression = None + self.session_id = None + self.host_key_type = None + self.host_key = None + + # state used during negotiation + self.kex_engine = None + self.H = None + self.K = None + + self.active = False + self.initial_kex_done = False + self.in_kex = False + self.authenticated = False + self._expected_packet = tuple() + self.lock = threading.Lock() # synchronization (always higher level than write_lock) + + # tracking open channels + self._channels = ChannelMap() + self.channel_events = {} # (id -> Event) + self.channels_seen = {} # (id -> True) + self._channel_counter = 1 + self.window_size = 65536 + self.max_packet_size = 34816 + self._forward_agent_handler = None + self._x11_handler = None + self._tcp_handler = None + + self.saved_exception = None + self.clear_to_send = threading.Event() + self.clear_to_send_lock = threading.Lock() + self.clear_to_send_timeout = 30.0 + self.log_name = 'paramiko.transport' + self.logger = util.get_logger(self.log_name) + self.packetizer.set_log(self.logger) + self.auth_handler = None + self.global_response = None # response Message from an arbitrary global request + self.completion_event = None # user-defined event callbacks + self.banner_timeout = 15 # how long (seconds) to wait for the SSH banner + + # server mode: + self.server_mode = False + self.server_object = None + self.server_key_dict = {} + self.server_accepts = [] + self.server_accept_cv = threading.Condition(self.lock) + self.subsystem_table = {} + + def __repr__(self): + """ + Returns a string representation of this object, for debugging. + """ + out = '` or + `auth_publickey `. + + .. note:: `connect` is a simpler method for connecting as a client. + + .. note:: After calling this method (or `start_server` or `connect`), + you should no longer directly read from or write to the original + socket object. + + :param .threading.Event event: + an event to trigger when negotiation is complete (optional) + + :raises SSHException: if negotiation fails (and no ``event`` was passed + in) + """ + self.active = True + if event is not None: + # async, return immediately and let the app poll for completion + self.completion_event = event + self.start() + return + + # synchronous, wait for a result + self.completion_event = event = threading.Event() + self.start() + while True: + event.wait(0.1) + if not self.active: + e = self.get_exception() + if e is not None: + raise e + raise SSHException('Negotiation failed.') + if event.isSet(): + break + + def start_server(self, event=None, server=None): + """ + Negotiate a new SSH2 session as a server. This is the first step after + creating a new `.Transport` and setting up your server host key(s). A + separate thread is created for protocol negotiation. + + If an event is passed in, this method returns immediately. When + negotiation is done (successful or not), the given ``Event`` will + be triggered. On failure, `is_active` will return ``False``. + + (Since 1.4) If ``event`` is ``None``, this method will not return until + negotation is done. On success, the method returns normally. + Otherwise an SSHException is raised. + + After a successful negotiation, the client will need to authenticate. + Override the methods `get_allowed_auths + <.ServerInterface.get_allowed_auths>`, `check_auth_none + <.ServerInterface.check_auth_none>`, `check_auth_password + <.ServerInterface.check_auth_password>`, and `check_auth_publickey + <.ServerInterface.check_auth_publickey>` in the given ``server`` object + to control the authentication process. + + After a successful authentication, the client should request to open a + channel. Override `check_channel_request + <.ServerInterface.check_channel_request>` in the given ``server`` + object to allow channels to be opened. + + .. note:: + After calling this method (or `start_client` or `connect`), you + should no longer directly read from or write to the original socket + object. + + :param .threading.Event event: + an event to trigger when negotiation is complete. + :param .ServerInterface server: + an object used to perform authentication and create `channels + <.Channel>` + + :raises SSHException: if negotiation fails (and no ``event`` was passed + in) + """ + if server is None: + server = ServerInterface() + self.server_mode = True + self.server_object = server + self.active = True + if event is not None: + # async, return immediately and let the app poll for completion + self.completion_event = event + self.start() + return + + # synchronous, wait for a result + self.completion_event = event = threading.Event() + self.start() + while True: + event.wait(0.1) + if not self.active: + e = self.get_exception() + if e is not None: + raise e + raise SSHException('Negotiation failed.') + if event.isSet(): + break + + def add_server_key(self, key): + """ + Add a host key to the list of keys used for server mode. When behaving + as a server, the host key is used to sign certain packets during the + SSH2 negotiation, so that the client can trust that we are who we say + we are. Because this is used for signing, the key must contain private + key info, not just the public half. Only one key of each type (RSA or + DSS) is kept. + + :param .PKey key: + the host key to add, usually an `.RSAKey` or `.DSSKey`. + """ + self.server_key_dict[key.get_name()] = key + + def get_server_key(self): + """ + Return the active host key, in server mode. After negotiating with the + client, this method will return the negotiated host key. If only one + type of host key was set with `add_server_key`, that's the only key + that will ever be returned. But in cases where you have set more than + one type of host key (for example, an RSA key and a DSS key), the key + type will be negotiated by the client, and this method will return the + key of the type agreed on. If the host key has not been negotiated + yet, ``None`` is returned. In client mode, the behavior is undefined. + + :return: + host key (`.PKey`) of the type negotiated by the client, or + ``None``. + """ + try: + return self.server_key_dict[self.host_key_type] + except KeyError: + pass + return None + + def load_server_moduli(filename=None): + """ + (optional) + Load a file of prime moduli for use in doing group-exchange key + negotiation in server mode. It's a rather obscure option and can be + safely ignored. + + In server mode, the remote client may request "group-exchange" key + negotiation, which asks the server to send a random prime number that + fits certain criteria. These primes are pretty difficult to compute, + so they can't be generated on demand. But many systems contain a file + of suitable primes (usually named something like ``/etc/ssh/moduli``). + If you call `load_server_moduli` and it returns ``True``, then this + file of primes has been loaded and we will support "group-exchange" in + server mode. Otherwise server mode will just claim that it doesn't + support that method of key negotiation. + + :param str filename: + optional path to the moduli file, if you happen to know that it's + not in a standard location. + :return: + True if a moduli file was successfully loaded; False otherwise. + + .. note:: This has no effect when used in client mode. + """ + Transport._modulus_pack = ModulusPack() + # places to look for the openssh "moduli" file + file_list = ['/etc/ssh/moduli', '/usr/local/etc/moduli'] + if filename is not None: + file_list.insert(0, filename) + for fn in file_list: + try: + Transport._modulus_pack.read_file(fn) + return True + except IOError: + pass + # none succeeded + Transport._modulus_pack = None + return False + load_server_moduli = staticmethod(load_server_moduli) + + def close(self): + """ + Close this session, and any open channels that are tied to it. + """ + if not self.active: + return + self.stop_thread() + for chan in list(self._channels.values()): + chan._unlink() + self.sock.close() + + def get_remote_server_key(self): + """ + Return the host key of the server (in client mode). + + .. note:: + Previously this call returned a tuple of ``(key type, key + string)``. You can get the same effect by calling `.PKey.get_name` + for the key type, and ``str(key)`` for the key string. + + :raises SSHException: if no session is currently active. + + :return: public key (`.PKey`) of the remote server + """ + if (not self.active) or (not self.initial_kex_done): + raise SSHException('No existing session') + return self.host_key + + def is_active(self): + """ + Return true if this session is active (open). + + :return: + True if the session is still active (open); False if the session is + closed + """ + return self.active + + def open_session(self): + """ + Request a new channel to the server, of type ``"session"``. This is + just an alias for calling `open_channel` with an argument of + ``"session"``. + + :return: a new `.Channel` + + :raises SSHException: if the request is rejected or the session ends + prematurely + """ + return self.open_channel('session') + + def open_x11_channel(self, src_addr=None): + """ + Request a new channel to the client, of type ``"x11"``. This + is just an alias for ``open_channel('x11', src_addr=src_addr)``. + + :param tuple src_addr: + the source address (``(str, int)``) of the x11 server (port is the + x11 port, ie. 6010) + :return: a new `.Channel` + + :raises SSHException: if the request is rejected or the session ends + prematurely + """ + return self.open_channel('x11', src_addr=src_addr) + + def open_forward_agent_channel(self): + """ + Request a new channel to the client, of type + ``"auth-agent@openssh.com"``. + + This is just an alias for ``open_channel('auth-agent@openssh.com')``. + + :return: a new `.Channel` + + :raises SSHException: + if the request is rejected or the session ends prematurely + """ + return self.open_channel('auth-agent@openssh.com') + + def open_forwarded_tcpip_channel(self, src_addr, dest_addr): + """ + Request a new channel back to the client, of type ``"forwarded-tcpip"``. + This is used after a client has requested port forwarding, for sending + incoming connections back to the client. + + :param src_addr: originator's address + :param dest_addr: local (server) connected address + """ + return self.open_channel('forwarded-tcpip', dest_addr, src_addr) + + def open_channel(self, kind, dest_addr=None, src_addr=None): + """ + Request a new channel to the server. `Channels <.Channel>` are + socket-like objects used for the actual transfer of data across the + session. You may only request a channel after negotiating encryption + (using `connect` or `start_client`) and authenticating. + + :param str kind: + the kind of channel requested (usually ``"session"``, + ``"forwarded-tcpip"``, ``"direct-tcpip"``, or ``"x11"``) + :param tuple dest_addr: + the destination address (address + port tuple) of this port + forwarding, if ``kind`` is ``"forwarded-tcpip"`` or + ``"direct-tcpip"`` (ignored for other channel types) + :param src_addr: the source address of this port forwarding, if + ``kind`` is ``"forwarded-tcpip"``, ``"direct-tcpip"``, or ``"x11"`` + :return: a new `.Channel` on success + + :raises SSHException: if the request is rejected or the session ends + prematurely + """ + if not self.active: + raise SSHException('SSH session not active') + self.lock.acquire() + try: + chanid = self._next_channel() + m = Message() + m.add_byte(cMSG_CHANNEL_OPEN) + m.add_string(kind) + m.add_int(chanid) + m.add_int(self.window_size) + m.add_int(self.max_packet_size) + if (kind == 'forwarded-tcpip') or (kind == 'direct-tcpip'): + m.add_string(dest_addr[0]) + m.add_int(dest_addr[1]) + m.add_string(src_addr[0]) + m.add_int(src_addr[1]) + elif kind == 'x11': + m.add_string(src_addr[0]) + m.add_int(src_addr[1]) + chan = Channel(chanid) + self._channels.put(chanid, chan) + self.channel_events[chanid] = event = threading.Event() + self.channels_seen[chanid] = True + chan._set_transport(self) + chan._set_window(self.window_size, self.max_packet_size) + finally: + self.lock.release() + self._send_user_message(m) + while True: + event.wait(0.1) + if not self.active: + e = self.get_exception() + if e is None: + e = SSHException('Unable to open channel.') + raise e + if event.isSet(): + break + chan = self._channels.get(chanid) + if chan is not None: + return chan + e = self.get_exception() + if e is None: + e = SSHException('Unable to open channel.') + raise e + + def request_port_forward(self, address, port, handler=None): + """ + Ask the server to forward TCP connections from a listening port on + the server, across this SSH session. + + If a handler is given, that handler is called from a different thread + whenever a forwarded connection arrives. The handler parameters are:: + + handler(channel, (origin_addr, origin_port), (server_addr, server_port)) + + where ``server_addr`` and ``server_port`` are the address and port that + the server was listening on. + + If no handler is set, the default behavior is to send new incoming + forwarded connections into the accept queue, to be picked up via + `accept`. + + :param str address: the address to bind when forwarding + :param int port: + the port to forward, or 0 to ask the server to allocate any port + :param callable handler: + optional handler for incoming forwarded connections, of the form + ``func(Channel, (str, int), (str, int))``. + :return: the port number (`int`) allocated by the server + + :raises SSHException: if the server refused the TCP forward request + """ + if not self.active: + raise SSHException('SSH session not active') + port = int(port) + response = self.global_request('tcpip-forward', (address, port), wait=True) + if response is None: + raise SSHException('TCP forwarding request denied') + if port == 0: + port = response.get_int() + if handler is None: + def default_handler(channel, src_addr, dest_addr_port): + #src_addr, src_port = src_addr_port + #dest_addr, dest_port = dest_addr_port + self._queue_incoming_channel(channel) + handler = default_handler + self._tcp_handler = handler + return port + + def cancel_port_forward(self, address, port): + """ + Ask the server to cancel a previous port-forwarding request. No more + connections to the given address & port will be forwarded across this + ssh connection. + + :param str address: the address to stop forwarding + :param int port: the port to stop forwarding + """ + if not self.active: + return + self._tcp_handler = None + self.global_request('cancel-tcpip-forward', (address, port), wait=True) + + def open_sftp_client(self): + """ + Create an SFTP client channel from an open transport. On success, an + SFTP session will be opened with the remote host, and a new + `.SFTPClient` object will be returned. + + :return: + a new `.SFTPClient` referring to an sftp session (channel) across + this transport + """ + return SFTPClient.from_transport(self) + + def send_ignore(self, byte_count=None): + """ + Send a junk packet across the encrypted link. This is sometimes used + to add "noise" to a connection to confuse would-be attackers. It can + also be used as a keep-alive for long lived connections traversing + firewalls. + + :param int byte_count: + the number of random bytes to send in the payload of the ignored + packet -- defaults to a random number from 10 to 41. + """ + m = Message() + m.add_byte(cMSG_IGNORE) + if byte_count is None: + byte_count = (byte_ord(os.urandom(1)) % 32) + 10 + m.add_bytes(os.urandom(byte_count)) + self._send_user_message(m) + + def renegotiate_keys(self): + """ + Force this session to switch to new keys. Normally this is done + automatically after the session hits a certain number of packets or + bytes sent or received, but this method gives you the option of forcing + new keys whenever you want. Negotiating new keys causes a pause in + traffic both ways as the two sides swap keys and do computations. This + method returns when the session has switched to new keys. + + :raises SSHException: if the key renegotiation failed (which causes the + session to end) + """ + self.completion_event = threading.Event() + self._send_kex_init() + while True: + self.completion_event.wait(0.1) + if not self.active: + e = self.get_exception() + if e is not None: + raise e + raise SSHException('Negotiation failed.') + if self.completion_event.isSet(): + break + return + + def set_keepalive(self, interval): + """ + Turn on/off keepalive packets (default is off). If this is set, after + ``interval`` seconds without sending any data over the connection, a + "keepalive" packet will be sent (and ignored by the remote host). This + can be useful to keep connections alive over a NAT, for example. + + :param int interval: + seconds to wait before sending a keepalive packet (or + 0 to disable keepalives). + """ + self.packetizer.set_keepalive(interval, + lambda x=weakref.proxy(self): x.global_request('keepalive@lag.net', wait=False)) + + def global_request(self, kind, data=None, wait=True): + """ + Make a global request to the remote host. These are normally + extensions to the SSH2 protocol. + + :param str kind: name of the request. + :param tuple data: + an optional tuple containing additional data to attach to the + request. + :param bool wait: + ``True`` if this method should not return until a response is + received; ``False`` otherwise. + :return: + a `.Message` containing possible additional data if the request was + successful (or an empty `.Message` if ``wait`` was ``False``); + ``None`` if the request was denied. + """ + if wait: + self.completion_event = threading.Event() + m = Message() + m.add_byte(cMSG_GLOBAL_REQUEST) + m.add_string(kind) + m.add_boolean(wait) + if data is not None: + m.add(*data) + self._log(DEBUG, 'Sending global request "%s"' % kind) + self._send_user_message(m) + if not wait: + return None + while True: + self.completion_event.wait(0.1) + if not self.active: + return None + if self.completion_event.isSet(): + break + return self.global_response + + def accept(self, timeout=None): + """ + Return the next channel opened by the client over this transport, in + server mode. If no channel is opened before the given timeout, ``None`` + is returned. + + :param int timeout: + seconds to wait for a channel, or ``None`` to wait forever + :return: a new `.Channel` opened by the client + """ + self.lock.acquire() + try: + if len(self.server_accepts) > 0: + chan = self.server_accepts.pop(0) + else: + self.server_accept_cv.wait(timeout) + if len(self.server_accepts) > 0: + chan = self.server_accepts.pop(0) + else: + # timeout + chan = None + finally: + self.lock.release() + return chan + + def connect(self, hostkey=None, username='', password=None, pkey=None): + """ + Negotiate an SSH2 session, and optionally verify the server's host key + and authenticate using a password or private key. This is a shortcut + for `start_client`, `get_remote_server_key`, and + `Transport.auth_password` or `Transport.auth_publickey`. Use those + methods if you want more control. + + You can use this method immediately after creating a Transport to + negotiate encryption with a server. If it fails, an exception will be + thrown. On success, the method will return cleanly, and an encrypted + session exists. You may immediately call `open_channel` or + `open_session` to get a `.Channel` object, which is used for data + transfer. + + .. note:: + If you fail to supply a password or private key, this method may + succeed, but a subsequent `open_channel` or `open_session` call may + fail because you haven't authenticated yet. + + :param .PKey hostkey: + the host key expected from the server, or ``None`` if you don't + want to do host key verification. + :param str username: the username to authenticate as. + :param str password: + a password to use for authentication, if you want to use password + authentication; otherwise ``None``. + :param .PKey pkey: + a private key to use for authentication, if you want to use private + key authentication; otherwise ``None``. + + :raises SSHException: if the SSH2 negotiation fails, the host key + supplied by the server is incorrect, or authentication fails. + """ + if hostkey is not None: + self._preferred_keys = [hostkey.get_name()] + + self.start_client() + + # check host key if we were given one + if hostkey is not None: + key = self.get_remote_server_key() + if (key.get_name() != hostkey.get_name()) or (key.asbytes() != hostkey.asbytes()): + self._log(DEBUG, 'Bad host key from server') + self._log(DEBUG, 'Expected: %s: %s' % (hostkey.get_name(), repr(hostkey.asbytes()))) + self._log(DEBUG, 'Got : %s: %s' % (key.get_name(), repr(key.asbytes()))) + raise SSHException('Bad host key from server') + self._log(DEBUG, 'Host key verified (%s)' % hostkey.get_name()) + + if (pkey is not None) or (password is not None): + if password is not None: + self._log(DEBUG, 'Attempting password auth...') + self.auth_password(username, password) + else: + self._log(DEBUG, 'Attempting public-key auth...') + self.auth_publickey(username, pkey) + + return + + def get_exception(self): + """ + Return any exception that happened during the last server request. + This can be used to fetch more specific error information after using + calls like `start_client`. The exception (if any) is cleared after + this call. + + :return: + an exception, or ``None`` if there is no stored exception. + + .. versionadded:: 1.1 + """ + self.lock.acquire() + try: + e = self.saved_exception + self.saved_exception = None + return e + finally: + self.lock.release() + + def set_subsystem_handler(self, name, handler, *larg, **kwarg): + """ + Set the handler class for a subsystem in server mode. If a request + for this subsystem is made on an open ssh channel later, this handler + will be constructed and called -- see `.SubsystemHandler` for more + detailed documentation. + + Any extra parameters (including keyword arguments) are saved and + passed to the `.SubsystemHandler` constructor later. + + :param str name: name of the subsystem. + :param class handler: + subclass of `.SubsystemHandler` that handles this subsystem. + """ + try: + self.lock.acquire() + self.subsystem_table[name] = (handler, larg, kwarg) + finally: + self.lock.release() + + def is_authenticated(self): + """ + Return true if this session is active and authenticated. + + :return: + True if the session is still open and has been authenticated + successfully; False if authentication failed and/or the session is + closed. + """ + return self.active and (self.auth_handler is not None) and self.auth_handler.is_authenticated() + + def get_username(self): + """ + Return the username this connection is authenticated for. If the + session is not authenticated (or authentication failed), this method + returns ``None``. + + :return: username that was authenticated (a `str`), or ``None``. + """ + if not self.active or (self.auth_handler is None): + return None + return self.auth_handler.get_username() + + def get_banner(self): + """ + Return the banner supplied by the server upon connect. If no banner is + supplied, this method returns C{None}. + + @return: server supplied banner, or C{None}. + @rtype: string + """ + if not self.active or (self.auth_handler is None): + return None + return self.auth_handler.banner + + def auth_none(self, username): + """ + Try to authenticate to the server using no authentication at all. + This will almost always fail. It may be useful for determining the + list of authentication types supported by the server, by catching the + `.BadAuthenticationType` exception raised. + + :param str username: the username to authenticate as + :return: + `list` of auth types permissible for the next stage of + authentication (normally empty) + + :raises BadAuthenticationType: if "none" authentication isn't allowed + by the server for this user + :raises SSHException: if the authentication failed due to a network + error + + .. versionadded:: 1.5 + """ + if (not self.active) or (not self.initial_kex_done): + raise SSHException('No existing session') + my_event = threading.Event() + self.auth_handler = AuthHandler(self) + self.auth_handler.auth_none(username, my_event) + return self.auth_handler.wait_for_response(my_event) + + def auth_password(self, username, password, event=None, fallback=True): + """ + Authenticate to the server using a password. The username and password + are sent over an encrypted link. + + If an ``event`` is passed in, this method will return immediately, and + the event will be triggered once authentication succeeds or fails. On + success, `is_authenticated` will return ``True``. On failure, you may + use `get_exception` to get more detailed error information. + + Since 1.1, if no event is passed, this method will block until the + authentication succeeds or fails. On failure, an exception is raised. + Otherwise, the method simply returns. + + Since 1.5, if no event is passed and ``fallback`` is ``True`` (the + default), if the server doesn't support plain password authentication + but does support so-called "keyboard-interactive" mode, an attempt + will be made to authenticate using this interactive mode. If it fails, + the normal exception will be thrown as if the attempt had never been + made. This is useful for some recent Gentoo and Debian distributions, + which turn off plain password authentication in a misguided belief + that interactive authentication is "more secure". (It's not.) + + If the server requires multi-step authentication (which is very rare), + this method will return a list of auth types permissible for the next + step. Otherwise, in the normal case, an empty list is returned. + + :param str username: the username to authenticate as + :param basestring password: the password to authenticate with + :param .threading.Event event: + an event to trigger when the authentication attempt is complete + (whether it was successful or not) + :param bool fallback: + ``True`` if an attempt at an automated "interactive" password auth + should be made if the server doesn't support normal password auth + :return: + `list` of auth types permissible for the next stage of + authentication (normally empty) + + :raises BadAuthenticationType: if password authentication isn't + allowed by the server for this user (and no event was passed in) + :raises AuthenticationException: if the authentication failed (and no + event was passed in) + :raises SSHException: if there was a network error + """ + if (not self.active) or (not self.initial_kex_done): + # we should never try to send the password unless we're on a secure link + raise SSHException('No existing session') + if event is None: + my_event = threading.Event() + else: + my_event = event + self.auth_handler = AuthHandler(self) + self.auth_handler.auth_password(username, password, my_event) + if event is not None: + # caller wants to wait for event themselves + return [] + try: + return self.auth_handler.wait_for_response(my_event) + except BadAuthenticationType as e: + # if password auth isn't allowed, but keyboard-interactive *is*, try to fudge it + if not fallback or ('keyboard-interactive' not in e.allowed_types): + raise + try: + def handler(title, instructions, fields): + if len(fields) > 1: + raise SSHException('Fallback authentication failed.') + if len(fields) == 0: + # for some reason, at least on os x, a 2nd request will + # be made with zero fields requested. maybe it's just + # to try to fake out automated scripting of the exact + # type we're doing here. *shrug* :) + return [] + return [password] + return self.auth_interactive(username, handler) + except SSHException: + # attempt failed; just raise the original exception + raise e + + def auth_publickey(self, username, key, event=None): + """ + Authenticate to the server using a private key. The key is used to + sign data from the server, so it must include the private part. + + If an ``event`` is passed in, this method will return immediately, and + the event will be triggered once authentication succeeds or fails. On + success, `is_authenticated` will return ``True``. On failure, you may + use `get_exception` to get more detailed error information. + + Since 1.1, if no event is passed, this method will block until the + authentication succeeds or fails. On failure, an exception is raised. + Otherwise, the method simply returns. + + If the server requires multi-step authentication (which is very rare), + this method will return a list of auth types permissible for the next + step. Otherwise, in the normal case, an empty list is returned. + + :param str username: the username to authenticate as + :param .PKey key: the private key to authenticate with + :param .threading.Event event: + an event to trigger when the authentication attempt is complete + (whether it was successful or not) + :return: + `list` of auth types permissible for the next stage of + authentication (normally empty) + + :raises BadAuthenticationType: if public-key authentication isn't + allowed by the server for this user (and no event was passed in) + :raises AuthenticationException: if the authentication failed (and no + event was passed in) + :raises SSHException: if there was a network error + """ + if (not self.active) or (not self.initial_kex_done): + # we should never try to authenticate unless we're on a secure link + raise SSHException('No existing session') + if event is None: + my_event = threading.Event() + else: + my_event = event + self.auth_handler = AuthHandler(self) + self.auth_handler.auth_publickey(username, key, my_event) + if event is not None: + # caller wants to wait for event themselves + return [] + return self.auth_handler.wait_for_response(my_event) + + def auth_interactive(self, username, handler, submethods=''): + """ + Authenticate to the server interactively. A handler is used to answer + arbitrary questions from the server. On many servers, this is just a + dumb wrapper around PAM. + + This method will block until the authentication succeeds or fails, + peroidically calling the handler asynchronously to get answers to + authentication questions. The handler may be called more than once + if the server continues to ask questions. + + The handler is expected to be a callable that will handle calls of the + form: ``handler(title, instructions, prompt_list)``. The ``title`` is + meant to be a dialog-window title, and the ``instructions`` are user + instructions (both are strings). ``prompt_list`` will be a list of + prompts, each prompt being a tuple of ``(str, bool)``. The string is + the prompt and the boolean indicates whether the user text should be + echoed. + + A sample call would thus be: + ``handler('title', 'instructions', [('Password:', False)])``. + + The handler should return a list or tuple of answers to the server's + questions. + + If the server requires multi-step authentication (which is very rare), + this method will return a list of auth types permissible for the next + step. Otherwise, in the normal case, an empty list is returned. + + :param str username: the username to authenticate as + :param callable handler: a handler for responding to server questions + :param str submethods: a string list of desired submethods (optional) + :return: + `list` of auth types permissible for the next stage of + authentication (normally empty). + + :raises BadAuthenticationType: if public-key authentication isn't + allowed by the server for this user + :raises AuthenticationException: if the authentication failed + :raises SSHException: if there was a network error + + .. versionadded:: 1.5 + """ + if (not self.active) or (not self.initial_kex_done): + # we should never try to authenticate unless we're on a secure link + raise SSHException('No existing session') + my_event = threading.Event() + self.auth_handler = AuthHandler(self) + self.auth_handler.auth_interactive(username, handler, my_event, submethods) + return self.auth_handler.wait_for_response(my_event) + + def set_log_channel(self, name): + """ + Set the channel for this transport's logging. The default is + ``"paramiko.transport"`` but it can be set to anything you want. (See + the `.logging` module for more info.) SSH Channels will log to a + sub-channel of the one specified. + + :param str name: new channel name for logging + + .. versionadded:: 1.1 + """ + self.log_name = name + self.logger = util.get_logger(name) + self.packetizer.set_log(self.logger) + + def get_log_channel(self): + """ + Return the channel name used for this transport's logging. + + :return: channel name as a `str` + + .. versionadded:: 1.2 + """ + return self.log_name + + def set_hexdump(self, hexdump): + """ + Turn on/off logging a hex dump of protocol traffic at DEBUG level in + the logs. Normally you would want this off (which is the default), + but if you are debugging something, it may be useful. + + :param bool hexdump: + ``True`` to log protocol traffix (in hex) to the log; ``False`` + otherwise. + """ + self.packetizer.set_hexdump(hexdump) + + def get_hexdump(self): + """ + Return ``True`` if the transport is currently logging hex dumps of + protocol traffic. + + :return: ``True`` if hex dumps are being logged, else ``False``. + + .. versionadded:: 1.4 + """ + return self.packetizer.get_hexdump() + + def use_compression(self, compress=True): + """ + Turn on/off compression. This will only have an affect before starting + the transport (ie before calling `connect`, etc). By default, + compression is off since it negatively affects interactive sessions. + + :param bool compress: + ``True`` to ask the remote client/server to compress traffic; + ``False`` to refuse compression + + .. versionadded:: 1.5.2 + """ + if compress: + self._preferred_compression = ('zlib@openssh.com', 'zlib', 'none') + else: + self._preferred_compression = ('none',) + + def getpeername(self): + """ + Return the address of the remote side of this Transport, if possible. + This is effectively a wrapper around ``'getpeername'`` on the underlying + socket. If the socket-like object has no ``'getpeername'`` method, + then ``("unknown", 0)`` is returned. + + :return: + the address of the remote host, if known, as a ``(str, int)`` + tuple. + """ + gp = getattr(self.sock, 'getpeername', None) + if gp is None: + return 'unknown', 0 + return gp() + + def stop_thread(self): + self.active = False + self.packetizer.close() + while self.isAlive(): + self.join(10) + + ### internals... + + def _log(self, level, msg, *args): + if issubclass(type(msg), list): + for m in msg: + self.logger.log(level, m) + else: + self.logger.log(level, msg, *args) + + def _get_modulus_pack(self): + """used by KexGex to find primes for group exchange""" + return self._modulus_pack + + def _next_channel(self): + """you are holding the lock""" + chanid = self._channel_counter + while self._channels.get(chanid) is not None: + self._channel_counter = (self._channel_counter + 1) & 0xffffff + chanid = self._channel_counter + self._channel_counter = (self._channel_counter + 1) & 0xffffff + return chanid + + def _unlink_channel(self, chanid): + """used by a Channel to remove itself from the active channel list""" + self._channels.delete(chanid) + + def _send_message(self, data): + self.packetizer.send_message(data) + + def _send_user_message(self, data): + """ + send a message, but block if we're in key negotiation. this is used + for user-initiated requests. + """ + start = time.time() + while True: + self.clear_to_send.wait(0.1) + if not self.active: + self._log(DEBUG, 'Dropping user packet because connection is dead.') + return + self.clear_to_send_lock.acquire() + if self.clear_to_send.isSet(): + break + self.clear_to_send_lock.release() + if time.time() > start + self.clear_to_send_timeout: + raise SSHException('Key-exchange timed out waiting for key negotiation') + try: + self._send_message(data) + finally: + self.clear_to_send_lock.release() + + def _set_K_H(self, k, h): + """used by a kex object to set the K (root key) and H (exchange hash)""" + self.K = k + self.H = h + if self.session_id is None: + self.session_id = h + + def _expect_packet(self, *ptypes): + """used by a kex object to register the next packet type it expects to see""" + self._expected_packet = tuple(ptypes) + + def _verify_key(self, host_key, sig): + key = self._key_info[self.host_key_type](Message(host_key)) + if key is None: + raise SSHException('Unknown host key type') + if not key.verify_ssh_sig(self.H, Message(sig)): + raise SSHException('Signature verification (%s) failed.' % self.host_key_type) + self.host_key = key + + def _compute_key(self, id, nbytes): + """id is 'A' - 'F' for the various keys used by ssh""" + m = Message() + m.add_mpint(self.K) + m.add_bytes(self.H) + m.add_byte(b(id)) + m.add_bytes(self.session_id) + out = sofar = sha1(m.asbytes()).digest() + while len(out) < nbytes: + m = Message() + m.add_mpint(self.K) + m.add_bytes(self.H) + m.add_bytes(sofar) + digest = sha1(m.asbytes()).digest() + out += digest + sofar += digest + return out[:nbytes] + + def _get_cipher(self, name, key, iv): + if name not in self._cipher_info: + raise SSHException('Unknown client cipher ' + name) + if name in ('arcfour128', 'arcfour256'): + # arcfour cipher + cipher = self._cipher_info[name]['class'].new(key) + # as per RFC 4345, the first 1536 bytes of keystream + # generated by the cipher MUST be discarded + cipher.encrypt(" " * 1536) + return cipher + elif name.endswith("-ctr"): + # CTR modes, we need a counter + counter = Counter.new(nbits=self._cipher_info[name]['block-size'] * 8, initial_value=util.inflate_long(iv, True)) + return self._cipher_info[name]['class'].new(key, self._cipher_info[name]['mode'], iv, counter) + else: + return self._cipher_info[name]['class'].new(key, self._cipher_info[name]['mode'], iv) + + def _set_forward_agent_handler(self, handler): + if handler is None: + def default_handler(channel): + self._queue_incoming_channel(channel) + self._forward_agent_handler = default_handler + else: + self._forward_agent_handler = handler + + def _set_x11_handler(self, handler): + # only called if a channel has turned on x11 forwarding + if handler is None: + # by default, use the same mechanism as accept() + def default_handler(channel, src_addr_port): + self._queue_incoming_channel(channel) + self._x11_handler = default_handler + else: + self._x11_handler = handler + + def _queue_incoming_channel(self, channel): + self.lock.acquire() + try: + self.server_accepts.append(channel) + self.server_accept_cv.notify() + finally: + self.lock.release() + + def run(self): + # (use the exposed "run" method, because if we specify a thread target + # of a private method, threading.Thread will keep a reference to it + # indefinitely, creating a GC cycle and not letting Transport ever be + # GC'd. it's a bug in Thread.) + + # Hold reference to 'sys' so we can test sys.modules to detect + # interpreter shutdown. + self.sys = sys + + # active=True occurs before the thread is launched, to avoid a race + _active_threads.append(self) + if self.server_mode: + self._log(DEBUG, 'starting thread (server mode): %s' % hex(long(id(self)) & xffffffff)) + else: + self._log(DEBUG, 'starting thread (client mode): %s' % hex(long(id(self)) & xffffffff)) + try: + try: + self.packetizer.write_all(b(self.local_version + '\r\n')) + self._check_banner() + self._send_kex_init() + self._expect_packet(MSG_KEXINIT) + + while self.active: + if self.packetizer.need_rekey() and not self.in_kex: + self._send_kex_init() + try: + ptype, m = self.packetizer.read_message() + except NeedRekeyException: + continue + if ptype == MSG_IGNORE: + continue + elif ptype == MSG_DISCONNECT: + self._parse_disconnect(m) + self.active = False + self.packetizer.close() + break + elif ptype == MSG_DEBUG: + self._parse_debug(m) + continue + if len(self._expected_packet) > 0: + if ptype not in self._expected_packet: + raise SSHException('Expecting packet from %r, got %d' % (self._expected_packet, ptype)) + self._expected_packet = tuple() + if (ptype >= 30) and (ptype <= 39): + self.kex_engine.parse_next(ptype, m) + continue + + if ptype in self._handler_table: + self._handler_table[ptype](self, m) + elif ptype in self._channel_handler_table: + chanid = m.get_int() + chan = self._channels.get(chanid) + if chan is not None: + self._channel_handler_table[ptype](chan, m) + elif chanid in self.channels_seen: + self._log(DEBUG, 'Ignoring message for dead channel %d' % chanid) + else: + self._log(ERROR, 'Channel request for unknown channel %d' % chanid) + self.active = False + self.packetizer.close() + elif (self.auth_handler is not None) and (ptype in self.auth_handler._handler_table): + self.auth_handler._handler_table[ptype](self.auth_handler, m) + else: + self._log(WARNING, 'Oops, unhandled type %d' % ptype) + msg = Message() + msg.add_byte(cMSG_UNIMPLEMENTED) + msg.add_int(m.seqno) + self._send_message(msg) + except SSHException as e: + self._log(ERROR, 'Exception: ' + str(e)) + self._log(ERROR, util.tb_strings()) + self.saved_exception = e + except EOFError as e: + self._log(DEBUG, 'EOF in transport thread') + #self._log(DEBUG, util.tb_strings()) + self.saved_exception = e + except socket.error as e: + if type(e.args) is tuple: + if e.args: + emsg = '%s (%d)' % (e.args[1], e.args[0]) + else: # empty tuple, e.g. socket.timeout + emsg = str(e) or repr(e) + else: + emsg = e.args + self._log(ERROR, 'Socket exception: ' + emsg) + self.saved_exception = e + except Exception as e: + self._log(ERROR, 'Unknown exception: ' + str(e)) + self._log(ERROR, util.tb_strings()) + self.saved_exception = e + _active_threads.remove(self) + for chan in list(self._channels.values()): + chan._unlink() + if self.active: + self.active = False + self.packetizer.close() + if self.completion_event is not None: + self.completion_event.set() + if self.auth_handler is not None: + self.auth_handler.abort() + for event in self.channel_events.values(): + event.set() + try: + self.lock.acquire() + self.server_accept_cv.notify() + finally: + self.lock.release() + self.sock.close() + except: + # Don't raise spurious 'NoneType has no attribute X' errors when we + # wake up during interpreter shutdown. Or rather -- raise + # everything *if* sys.modules (used as a convenient sentinel) + # appears to still exist. + if self.sys.modules is not None: + raise + + ### protocol stages + + def _negotiate_keys(self, m): + # throws SSHException on anything unusual + self.clear_to_send_lock.acquire() + try: + self.clear_to_send.clear() + finally: + self.clear_to_send_lock.release() + if self.local_kex_init is None: + # remote side wants to renegotiate + self._send_kex_init() + self._parse_kex_init(m) + self.kex_engine.start_kex() + + def _check_banner(self): + # this is slow, but we only have to do it once + for i in range(100): + # give them 15 seconds for the first line, then just 2 seconds + # each additional line. (some sites have very high latency.) + if i == 0: + timeout = self.banner_timeout + else: + timeout = 2 + try: + buf = self.packetizer.readline(timeout) + except ProxyCommandFailure: + raise + except Exception as e: + raise SSHException('Error reading SSH protocol banner' + str(e)) + if buf[:4] == 'SSH-': + break + self._log(DEBUG, 'Banner: ' + buf) + if buf[:4] != 'SSH-': + raise SSHException('Indecipherable protocol version "' + buf + '"') + # save this server version string for later + self.remote_version = buf + # pull off any attached comment + comment = '' + i = buf.find(' ') + if i >= 0: + comment = buf[i+1:] + buf = buf[:i] + # parse out version string and make sure it matches + segs = buf.split('-', 2) + if len(segs) < 3: + raise SSHException('Invalid SSH banner') + version = segs[1] + client = segs[2] + if version != '1.99' and version != '2.0': + raise SSHException('Incompatible version (%s instead of 2.0)' % (version,)) + self._log(INFO, 'Connected (version %s, client %s)' % (version, client)) + + def _send_kex_init(self): + """ + announce to the other side that we'd like to negotiate keys, and what + kind of key negotiation we support. + """ + self.clear_to_send_lock.acquire() + try: + self.clear_to_send.clear() + finally: + self.clear_to_send_lock.release() + self.in_kex = True + if self.server_mode: + if (self._modulus_pack is None) and ('diffie-hellman-group-exchange-sha1' in self._preferred_kex): + # can't do group-exchange if we don't have a pack of potential primes + pkex = list(self.get_security_options().kex) + pkex.remove('diffie-hellman-group-exchange-sha1') + self.get_security_options().kex = pkex + available_server_keys = list(filter(list(self.server_key_dict.keys()).__contains__, + self._preferred_keys)) + else: + available_server_keys = self._preferred_keys + + m = Message() + m.add_byte(cMSG_KEXINIT) + m.add_bytes(os.urandom(16)) + m.add_list(self._preferred_kex) + m.add_list(available_server_keys) + m.add_list(self._preferred_ciphers) + m.add_list(self._preferred_ciphers) + m.add_list(self._preferred_macs) + m.add_list(self._preferred_macs) + m.add_list(self._preferred_compression) + m.add_list(self._preferred_compression) + m.add_string(bytes()) + m.add_string(bytes()) + m.add_boolean(False) + m.add_int(0) + # save a copy for later (needed to compute a hash) + self.local_kex_init = m.asbytes() + self._send_message(m) + + def _parse_kex_init(self, m): + cookie = m.get_bytes(16) + kex_algo_list = m.get_list() + server_key_algo_list = m.get_list() + client_encrypt_algo_list = m.get_list() + server_encrypt_algo_list = m.get_list() + client_mac_algo_list = m.get_list() + server_mac_algo_list = m.get_list() + client_compress_algo_list = m.get_list() + server_compress_algo_list = m.get_list() + client_lang_list = m.get_list() + server_lang_list = m.get_list() + kex_follows = m.get_boolean() + unused = m.get_int() + + self._log(DEBUG, 'kex algos:' + str(kex_algo_list) + ' server key:' + str(server_key_algo_list) + + ' client encrypt:' + str(client_encrypt_algo_list) + + ' server encrypt:' + str(server_encrypt_algo_list) + + ' client mac:' + str(client_mac_algo_list) + + ' server mac:' + str(server_mac_algo_list) + + ' client compress:' + str(client_compress_algo_list) + + ' server compress:' + str(server_compress_algo_list) + + ' client lang:' + str(client_lang_list) + + ' server lang:' + str(server_lang_list) + + ' kex follows?' + str(kex_follows)) + + # as a server, we pick the first item in the client's list that we support. + # as a client, we pick the first item in our list that the server supports. + if self.server_mode: + agreed_kex = list(filter(self._preferred_kex.__contains__, kex_algo_list)) + else: + agreed_kex = list(filter(kex_algo_list.__contains__, self._preferred_kex)) + if len(agreed_kex) == 0: + raise SSHException('Incompatible ssh peer (no acceptable kex algorithm)') + self.kex_engine = self._kex_info[agreed_kex[0]](self) + + if self.server_mode: + available_server_keys = list(filter(list(self.server_key_dict.keys()).__contains__, + self._preferred_keys)) + agreed_keys = list(filter(available_server_keys.__contains__, server_key_algo_list)) + else: + agreed_keys = list(filter(server_key_algo_list.__contains__, self._preferred_keys)) + if len(agreed_keys) == 0: + raise SSHException('Incompatible ssh peer (no acceptable host key)') + self.host_key_type = agreed_keys[0] + if self.server_mode and (self.get_server_key() is None): + raise SSHException('Incompatible ssh peer (can\'t match requested host key type)') + + if self.server_mode: + agreed_local_ciphers = list(filter(self._preferred_ciphers.__contains__, + server_encrypt_algo_list)) + agreed_remote_ciphers = list(filter(self._preferred_ciphers.__contains__, + client_encrypt_algo_list)) + else: + agreed_local_ciphers = list(filter(client_encrypt_algo_list.__contains__, + self._preferred_ciphers)) + agreed_remote_ciphers = list(filter(server_encrypt_algo_list.__contains__, + self._preferred_ciphers)) + if (len(agreed_local_ciphers) == 0) or (len(agreed_remote_ciphers) == 0): + raise SSHException('Incompatible ssh server (no acceptable ciphers)') + self.local_cipher = agreed_local_ciphers[0] + self.remote_cipher = agreed_remote_ciphers[0] + self._log(DEBUG, 'Ciphers agreed: local=%s, remote=%s' % (self.local_cipher, self.remote_cipher)) + + if self.server_mode: + agreed_remote_macs = list(filter(self._preferred_macs.__contains__, client_mac_algo_list)) + agreed_local_macs = list(filter(self._preferred_macs.__contains__, server_mac_algo_list)) + else: + agreed_local_macs = list(filter(client_mac_algo_list.__contains__, self._preferred_macs)) + agreed_remote_macs = list(filter(server_mac_algo_list.__contains__, self._preferred_macs)) + if (len(agreed_local_macs) == 0) or (len(agreed_remote_macs) == 0): + raise SSHException('Incompatible ssh server (no acceptable macs)') + self.local_mac = agreed_local_macs[0] + self.remote_mac = agreed_remote_macs[0] + + if self.server_mode: + agreed_remote_compression = list(filter(self._preferred_compression.__contains__, client_compress_algo_list)) + agreed_local_compression = list(filter(self._preferred_compression.__contains__, server_compress_algo_list)) + else: + agreed_local_compression = list(filter(client_compress_algo_list.__contains__, self._preferred_compression)) + agreed_remote_compression = list(filter(server_compress_algo_list.__contains__, self._preferred_compression)) + if (len(agreed_local_compression) == 0) or (len(agreed_remote_compression) == 0): + raise SSHException('Incompatible ssh server (no acceptable compression) %r %r %r' % (agreed_local_compression, agreed_remote_compression, self._preferred_compression)) + self.local_compression = agreed_local_compression[0] + self.remote_compression = agreed_remote_compression[0] + + self._log(DEBUG, 'using kex %s; server key type %s; cipher: local %s, remote %s; mac: local %s, remote %s; compression: local %s, remote %s' % + (agreed_kex[0], self.host_key_type, self.local_cipher, self.remote_cipher, self.local_mac, + self.remote_mac, self.local_compression, self.remote_compression)) + + # save for computing hash later... + # now wait! openssh has a bug (and others might too) where there are + # actually some extra bytes (one NUL byte in openssh's case) added to + # the end of the packet but not parsed. turns out we need to throw + # away those bytes because they aren't part of the hash. + self.remote_kex_init = cMSG_KEXINIT + m.get_so_far() + + def _activate_inbound(self): + """switch on newly negotiated encryption parameters for inbound traffic""" + block_size = self._cipher_info[self.remote_cipher]['block-size'] + if self.server_mode: + IV_in = self._compute_key('A', block_size) + key_in = self._compute_key('C', self._cipher_info[self.remote_cipher]['key-size']) + else: + IV_in = self._compute_key('B', block_size) + key_in = self._compute_key('D', self._cipher_info[self.remote_cipher]['key-size']) + engine = self._get_cipher(self.remote_cipher, key_in, IV_in) + mac_size = self._mac_info[self.remote_mac]['size'] + mac_engine = self._mac_info[self.remote_mac]['class'] + # initial mac keys are done in the hash's natural size (not the potentially truncated + # transmission size) + if self.server_mode: + mac_key = self._compute_key('E', mac_engine().digest_size) + else: + mac_key = self._compute_key('F', mac_engine().digest_size) + self.packetizer.set_inbound_cipher(engine, block_size, mac_engine, mac_size, mac_key) + compress_in = self._compression_info[self.remote_compression][1] + if (compress_in is not None) and ((self.remote_compression != 'zlib@openssh.com') or self.authenticated): + self._log(DEBUG, 'Switching on inbound compression ...') + self.packetizer.set_inbound_compressor(compress_in()) + + def _activate_outbound(self): + """switch on newly negotiated encryption parameters for outbound traffic""" + m = Message() + m.add_byte(cMSG_NEWKEYS) + self._send_message(m) + block_size = self._cipher_info[self.local_cipher]['block-size'] + if self.server_mode: + IV_out = self._compute_key('B', block_size) + key_out = self._compute_key('D', self._cipher_info[self.local_cipher]['key-size']) + else: + IV_out = self._compute_key('A', block_size) + key_out = self._compute_key('C', self._cipher_info[self.local_cipher]['key-size']) + engine = self._get_cipher(self.local_cipher, key_out, IV_out) + mac_size = self._mac_info[self.local_mac]['size'] + mac_engine = self._mac_info[self.local_mac]['class'] + # initial mac keys are done in the hash's natural size (not the potentially truncated + # transmission size) + if self.server_mode: + mac_key = self._compute_key('F', mac_engine().digest_size) + else: + mac_key = self._compute_key('E', mac_engine().digest_size) + sdctr = self.local_cipher.endswith('-ctr') + self.packetizer.set_outbound_cipher(engine, block_size, mac_engine, mac_size, mac_key, sdctr) + compress_out = self._compression_info[self.local_compression][0] + if (compress_out is not None) and ((self.local_compression != 'zlib@openssh.com') or self.authenticated): + self._log(DEBUG, 'Switching on outbound compression ...') + self.packetizer.set_outbound_compressor(compress_out()) + if not self.packetizer.need_rekey(): + self.in_kex = False + # we always expect to receive NEWKEYS now + self._expect_packet(MSG_NEWKEYS) + + def _auth_trigger(self): + self.authenticated = True + # delayed initiation of compression + if self.local_compression == 'zlib@openssh.com': + compress_out = self._compression_info[self.local_compression][0] + self._log(DEBUG, 'Switching on outbound compression ...') + self.packetizer.set_outbound_compressor(compress_out()) + if self.remote_compression == 'zlib@openssh.com': + compress_in = self._compression_info[self.remote_compression][1] + self._log(DEBUG, 'Switching on inbound compression ...') + self.packetizer.set_inbound_compressor(compress_in()) + + def _parse_newkeys(self, m): + self._log(DEBUG, 'Switch to new keys ...') + self._activate_inbound() + # can also free a bunch of stuff here + self.local_kex_init = self.remote_kex_init = None + self.K = None + self.kex_engine = None + if self.server_mode and (self.auth_handler is None): + # create auth handler for server mode + self.auth_handler = AuthHandler(self) + if not self.initial_kex_done: + # this was the first key exchange + self.initial_kex_done = True + # send an event? + if self.completion_event is not None: + self.completion_event.set() + # it's now okay to send data again (if this was a re-key) + if not self.packetizer.need_rekey(): + self.in_kex = False + self.clear_to_send_lock.acquire() + try: + self.clear_to_send.set() + finally: + self.clear_to_send_lock.release() + return + + def _parse_disconnect(self, m): + code = m.get_int() + desc = m.get_text() + self._log(INFO, 'Disconnect (code %d): %s' % (code, desc)) + + def _parse_global_request(self, m): + kind = m.get_text() + self._log(DEBUG, 'Received global request "%s"' % kind) + want_reply = m.get_boolean() + if not self.server_mode: + self._log(DEBUG, 'Rejecting "%s" global request from server.' % kind) + ok = False + elif kind == 'tcpip-forward': + address = m.get_text() + port = m.get_int() + ok = self.server_object.check_port_forward_request(address, port) + if ok: + ok = (ok,) + elif kind == 'cancel-tcpip-forward': + address = m.get_text() + port = m.get_int() + self.server_object.cancel_port_forward_request(address, port) + ok = True + else: + ok = self.server_object.check_global_request(kind, m) + extra = () + if type(ok) is tuple: + extra = ok + ok = True + if want_reply: + msg = Message() + if ok: + msg.add_byte(cMSG_REQUEST_SUCCESS) + msg.add(*extra) + else: + msg.add_byte(cMSG_REQUEST_FAILURE) + self._send_message(msg) + + def _parse_request_success(self, m): + self._log(DEBUG, 'Global request successful.') + self.global_response = m + if self.completion_event is not None: + self.completion_event.set() + + def _parse_request_failure(self, m): + self._log(DEBUG, 'Global request denied.') + self.global_response = None + if self.completion_event is not None: + self.completion_event.set() + + def _parse_channel_open_success(self, m): + chanid = m.get_int() + server_chanid = m.get_int() + server_window_size = m.get_int() + server_max_packet_size = m.get_int() + chan = self._channels.get(chanid) + if chan is None: + self._log(WARNING, 'Success for unrequested channel! [??]') + return + self.lock.acquire() + try: + chan._set_remote_channel(server_chanid, server_window_size, server_max_packet_size) + self._log(INFO, 'Secsh channel %d opened.' % chanid) + if chanid in self.channel_events: + self.channel_events[chanid].set() + del self.channel_events[chanid] + finally: + self.lock.release() + return + + def _parse_channel_open_failure(self, m): + chanid = m.get_int() + reason = m.get_int() + reason_str = m.get_text() + lang = m.get_text() + reason_text = CONNECTION_FAILED_CODE.get(reason, '(unknown code)') + self._log(INFO, 'Secsh channel %d open FAILED: %s: %s' % (chanid, reason_str, reason_text)) + self.lock.acquire() + try: + self.saved_exception = ChannelException(reason, reason_text) + if chanid in self.channel_events: + self._channels.delete(chanid) + if chanid in self.channel_events: + self.channel_events[chanid].set() + del self.channel_events[chanid] + finally: + self.lock.release() + return + + def _parse_channel_open(self, m): + kind = m.get_text() + chanid = m.get_int() + initial_window_size = m.get_int() + max_packet_size = m.get_int() + reject = False + if (kind == 'auth-agent@openssh.com') and (self._forward_agent_handler is not None): + self._log(DEBUG, 'Incoming forward agent connection') + self.lock.acquire() + try: + my_chanid = self._next_channel() + finally: + self.lock.release() + elif (kind == 'x11') and (self._x11_handler is not None): + origin_addr = m.get_text() + origin_port = m.get_int() + self._log(DEBUG, 'Incoming x11 connection from %s:%d' % (origin_addr, origin_port)) + self.lock.acquire() + try: + my_chanid = self._next_channel() + finally: + self.lock.release() + elif (kind == 'forwarded-tcpip') and (self._tcp_handler is not None): + server_addr = m.get_text() + server_port = m.get_int() + origin_addr = m.get_text() + origin_port = m.get_int() + self._log(DEBUG, 'Incoming tcp forwarded connection from %s:%d' % (origin_addr, origin_port)) + self.lock.acquire() + try: + my_chanid = self._next_channel() + finally: + self.lock.release() + elif not self.server_mode: + self._log(DEBUG, 'Rejecting "%s" channel request from server.' % kind) + reject = True + reason = OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED + else: + self.lock.acquire() + try: + my_chanid = self._next_channel() + finally: + self.lock.release() + if kind == 'direct-tcpip': + # handle direct-tcpip requests comming from the client + dest_addr = m.get_text() + dest_port = m.get_int() + origin_addr = m.get_text() + origin_port = m.get_int() + reason = self.server_object.check_channel_direct_tcpip_request( + my_chanid, (origin_addr, origin_port), (dest_addr, dest_port)) + else: + reason = self.server_object.check_channel_request(kind, my_chanid) + if reason != OPEN_SUCCEEDED: + self._log(DEBUG, 'Rejecting "%s" channel request from client.' % kind) + reject = True + if reject: + msg = Message() + msg.add_byte(cMSG_CHANNEL_OPEN_FAILURE) + msg.add_int(chanid) + msg.add_int(reason) + msg.add_string('') + msg.add_string('en') + self._send_message(msg) + return + + chan = Channel(my_chanid) + self.lock.acquire() + try: + self._channels.put(my_chanid, chan) + self.channels_seen[my_chanid] = True + chan._set_transport(self) + chan._set_window(self.window_size, self.max_packet_size) + chan._set_remote_channel(chanid, initial_window_size, max_packet_size) + finally: + self.lock.release() + m = Message() + m.add_byte(cMSG_CHANNEL_OPEN_SUCCESS) + m.add_int(chanid) + m.add_int(my_chanid) + m.add_int(self.window_size) + m.add_int(self.max_packet_size) + self._send_message(m) + self._log(INFO, 'Secsh channel %d (%s) opened.', my_chanid, kind) + if kind == 'auth-agent@openssh.com': + self._forward_agent_handler(chan) + elif kind == 'x11': + self._x11_handler(chan, (origin_addr, origin_port)) + elif kind == 'forwarded-tcpip': + chan.origin_addr = (origin_addr, origin_port) + self._tcp_handler(chan, (origin_addr, origin_port), (server_addr, server_port)) + else: + self._queue_incoming_channel(chan) + + def _parse_debug(self, m): + always_display = m.get_boolean() + msg = m.get_string() + lang = m.get_string() + self._log(DEBUG, 'Debug msg: ' + util.safe_string(msg)) + + def _get_subsystem_handler(self, name): + try: + self.lock.acquire() + if name not in self.subsystem_table: + return None, [], {} + return self.subsystem_table[name] + finally: + self.lock.release() + + _handler_table = { + MSG_NEWKEYS: _parse_newkeys, + MSG_GLOBAL_REQUEST: _parse_global_request, + MSG_REQUEST_SUCCESS: _parse_request_success, + MSG_REQUEST_FAILURE: _parse_request_failure, + MSG_CHANNEL_OPEN_SUCCESS: _parse_channel_open_success, + MSG_CHANNEL_OPEN_FAILURE: _parse_channel_open_failure, + MSG_CHANNEL_OPEN: _parse_channel_open, + MSG_KEXINIT: _negotiate_keys, + } + + _channel_handler_table = { + MSG_CHANNEL_SUCCESS: Channel._request_success, + MSG_CHANNEL_FAILURE: Channel._request_failed, + MSG_CHANNEL_DATA: Channel._feed, + MSG_CHANNEL_EXTENDED_DATA: Channel._feed_extended, + MSG_CHANNEL_WINDOW_ADJUST: Channel._window_adjust, + MSG_CHANNEL_REQUEST: Channel._handle_request, + MSG_CHANNEL_EOF: Channel._handle_eof, + MSG_CHANNEL_CLOSE: Channel._handle_close, + } + + +class SecurityOptions (object): + """ + Simple object containing the security preferences of an ssh transport. + These are tuples of acceptable ciphers, digests, key types, and key + exchange algorithms, listed in order of preference. + + Changing the contents and/or order of these fields affects the underlying + `.Transport` (but only if you change them before starting the session). + If you try to add an algorithm that paramiko doesn't recognize, + ``ValueError`` will be raised. If you try to assign something besides a + tuple to one of the fields, ``TypeError`` will be raised. + """ + #__slots__ = [ 'ciphers', 'digests', 'key_types', 'kex', 'compression', '_transport' ] + __slots__ = '_transport' + + def __init__(self, transport): + self._transport = transport + + def __repr__(self): + """ + Returns a string representation of this object, for debugging. + """ + return '' % repr(self._transport) + + def _get_ciphers(self): + return self._transport._preferred_ciphers + + def _get_digests(self): + return self._transport._preferred_macs + + def _get_key_types(self): + return self._transport._preferred_keys + + def _get_kex(self): + return self._transport._preferred_kex + + def _get_compression(self): + return self._transport._preferred_compression + + def _set(self, name, orig, x): + if type(x) is list: + x = tuple(x) + if type(x) is not tuple: + raise TypeError('expected tuple or list') + possible = list(getattr(self._transport, orig).keys()) + forbidden = [n for n in x if n not in possible] + if len(forbidden) > 0: + raise ValueError('unknown cipher') + setattr(self._transport, name, x) + + def _set_ciphers(self, x): + self._set('_preferred_ciphers', '_cipher_info', x) + + def _set_digests(self, x): + self._set('_preferred_macs', '_mac_info', x) + + def _set_key_types(self, x): + self._set('_preferred_keys', '_key_info', x) + + def _set_kex(self, x): + self._set('_preferred_kex', '_kex_info', x) + + def _set_compression(self, x): + self._set('_preferred_compression', '_compression_info', x) + + ciphers = property(_get_ciphers, _set_ciphers, None, + "Symmetric encryption ciphers") + digests = property(_get_digests, _set_digests, None, + "Digest (one-way hash) algorithms") + key_types = property(_get_key_types, _set_key_types, None, + "Public-key algorithms") + kex = property(_get_kex, _set_kex, None, "Key exchange algorithms") + compression = property(_get_compression, _set_compression, None, + "Compression algorithms") + + +class ChannelMap (object): + def __init__(self): + # (id -> Channel) + self._map = weakref.WeakValueDictionary() + self._lock = threading.Lock() + + def put(self, chanid, chan): + self._lock.acquire() + try: + self._map[chanid] = chan + finally: + self._lock.release() + + def get(self, chanid): + self._lock.acquire() + try: + return self._map.get(chanid, None) + finally: + self._lock.release() + + def delete(self, chanid): + self._lock.acquire() + try: + try: + del self._map[chanid] + except KeyError: + pass + finally: + self._lock.release() + + def values(self): + self._lock.acquire() + try: + return list(self._map.values()) + finally: + self._lock.release() + + def __len__(self): + self._lock.acquire() + try: + return len(self._map) + finally: + self._lock.release() diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/util.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/util.py new file mode 100644 index 0000000..f4ee3ad --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/util.py @@ -0,0 +1,322 @@ +# Copyright (C) 2003-2007 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +Useful functions used by the rest of paramiko. +""" + +from __future__ import generators + +import array +from binascii import hexlify, unhexlify +import errno +import sys +import struct +import traceback +import threading +import logging + +from paramiko.common import DEBUG, zero_byte, xffffffff, max_byte +from paramiko.py3compat import PY2, long, byte_ord, b, byte_chr +from paramiko.config import SSHConfig + + +def inflate_long(s, always_positive=False): + """turns a normalized byte string into a long-int (adapted from Crypto.Util.number)""" + out = long(0) + negative = 0 + if not always_positive and (len(s) > 0) and (byte_ord(s[0]) >= 0x80): + negative = 1 + if len(s) % 4: + filler = zero_byte + if negative: + filler = max_byte + # never convert this to ``s +=`` because this is a string, not a number + # noinspection PyAugmentAssignment + s = filler * (4 - len(s) % 4) + s + for i in range(0, len(s), 4): + out = (out << 32) + struct.unpack('>I', s[i:i+4])[0] + if negative: + out -= (long(1) << (8 * len(s))) + return out + +deflate_zero = zero_byte if PY2 else 0 +deflate_ff = max_byte if PY2 else 0xff + + +def deflate_long(n, add_sign_padding=True): + """turns a long-int into a normalized byte string (adapted from Crypto.Util.number)""" + # after much testing, this algorithm was deemed to be the fastest + s = bytes() + n = long(n) + while (n != 0) and (n != -1): + s = struct.pack('>I', n & xffffffff) + s + n >>= 32 + # strip off leading zeros, FFs + for i in enumerate(s): + if (n == 0) and (i[1] != deflate_zero): + break + if (n == -1) and (i[1] != deflate_ff): + break + else: + # degenerate case, n was either 0 or -1 + i = (0,) + if n == 0: + s = zero_byte + else: + s = max_byte + s = s[i[0]:] + if add_sign_padding: + if (n == 0) and (byte_ord(s[0]) >= 0x80): + s = zero_byte + s + if (n == -1) and (byte_ord(s[0]) < 0x80): + s = max_byte + s + return s + + +def format_binary(data, prefix=''): + x = 0 + out = [] + while len(data) > x + 16: + out.append(format_binary_line(data[x:x+16])) + x += 16 + if x < len(data): + out.append(format_binary_line(data[x:])) + return [prefix + x for x in out] + + +def format_binary_line(data): + left = ' '.join(['%02X' % byte_ord(c) for c in data]) + right = ''.join([('.%c..' % c)[(byte_ord(c)+63)//95] for c in data]) + return '%-50s %s' % (left, right) + + +def hexify(s): + return hexlify(s).upper() + + +def unhexify(s): + return unhexlify(s) + + +def safe_string(s): + out = '' + for c in s: + if (byte_ord(c) >= 32) and (byte_ord(c) <= 127): + out += c + else: + out += '%%%02X' % byte_ord(c) + return out + + +def bit_length(n): + try: + return n.bitlength() + except AttributeError: + norm = deflate_long(n, False) + hbyte = byte_ord(norm[0]) + if hbyte == 0: + return 1 + bitlen = len(norm) * 8 + while not (hbyte & 0x80): + hbyte <<= 1 + bitlen -= 1 + return bitlen + + +def tb_strings(): + return ''.join(traceback.format_exception(*sys.exc_info())).split('\n') + + +def generate_key_bytes(hash_alg, salt, key, nbytes): + """ + Given a password, passphrase, or other human-source key, scramble it + through a secure hash into some keyworthy bytes. This specific algorithm + is used for encrypting/decrypting private key files. + + :param function hash_alg: A function which creates a new hash object, such + as ``hashlib.sha256``. + :param salt: data to salt the hash with. + :type salt: byte string + :param str key: human-entered password or passphrase. + :param int nbytes: number of bytes to generate. + :return: Key data `str` + """ + keydata = bytes() + digest = bytes() + if len(salt) > 8: + salt = salt[:8] + while nbytes > 0: + hash_obj = hash_alg() + if len(digest) > 0: + hash_obj.update(digest) + hash_obj.update(b(key)) + hash_obj.update(salt) + digest = hash_obj.digest() + size = min(nbytes, len(digest)) + keydata += digest[:size] + nbytes -= size + return keydata + + +def load_host_keys(filename): + """ + Read a file of known SSH host keys, in the format used by openssh, and + return a compound dict of ``hostname -> keytype ->`` `PKey + `. The hostname may be an IP address or DNS name. The + keytype will be either ``"ssh-rsa"`` or ``"ssh-dss"``. + + This type of file unfortunately doesn't exist on Windows, but on posix, + it will usually be stored in ``os.path.expanduser("~/.ssh/known_hosts")``. + + Since 1.5.3, this is just a wrapper around `.HostKeys`. + + :param str filename: name of the file to read host keys from + :return: + nested dict of `.PKey` objects, indexed by hostname and then keytype + """ + from paramiko.hostkeys import HostKeys + return HostKeys(filename) + + +def parse_ssh_config(file_obj): + """ + Provided only as a backward-compatible wrapper around `.SSHConfig`. + """ + config = SSHConfig() + config.parse(file_obj) + return config + + +def lookup_ssh_host_config(hostname, config): + """ + Provided only as a backward-compatible wrapper around `.SSHConfig`. + """ + return config.lookup(hostname) + + +def mod_inverse(x, m): + # it's crazy how small Python can make this function. + u1, u2, u3 = 1, 0, m + v1, v2, v3 = 0, 1, x + + while v3 > 0: + q = u3 // v3 + u1, v1 = v1, u1 - v1 * q + u2, v2 = v2, u2 - v2 * q + u3, v3 = v3, u3 - v3 * q + if u2 < 0: + u2 += m + return u2 + +_g_thread_ids = {} +_g_thread_counter = 0 +_g_thread_lock = threading.Lock() + + +def get_thread_id(): + global _g_thread_ids, _g_thread_counter, _g_thread_lock + tid = id(threading.currentThread()) + try: + return _g_thread_ids[tid] + except KeyError: + _g_thread_lock.acquire() + try: + _g_thread_counter += 1 + ret = _g_thread_ids[tid] = _g_thread_counter + finally: + _g_thread_lock.release() + return ret + + +def log_to_file(filename, level=DEBUG): + """send paramiko logs to a logfile, if they're not already going somewhere""" + l = logging.getLogger("paramiko") + if len(l.handlers) > 0: + return + l.setLevel(level) + f = open(filename, 'w') + lh = logging.StreamHandler(f) + lh.setFormatter(logging.Formatter('%(levelname)-.3s [%(asctime)s.%(msecs)03d] thr=%(_threadid)-3d %(name)s: %(message)s', + '%Y%m%d-%H:%M:%S')) + l.addHandler(lh) + + +# make only one filter object, so it doesn't get applied more than once +class PFilter (object): + def filter(self, record): + record._threadid = get_thread_id() + return True +_pfilter = PFilter() + + +def get_logger(name): + l = logging.getLogger(name) + l.addFilter(_pfilter) + return l + + +def retry_on_signal(function): + """Retries function until it doesn't raise an EINTR error""" + while True: + try: + return function() + except EnvironmentError as e: + if e.errno != errno.EINTR: + raise + + +class Counter (object): + """Stateful counter for CTR mode crypto""" + def __init__(self, nbits, initial_value=long(1), overflow=long(0)): + self.blocksize = nbits / 8 + self.overflow = overflow + # start with value - 1 so we don't have to store intermediate values when counting + # could the iv be 0? + if initial_value == 0: + self.value = array.array('c', max_byte * self.blocksize) + else: + x = deflate_long(initial_value - 1, add_sign_padding=False) + self.value = array.array('c', zero_byte * (self.blocksize - len(x)) + x) + + def __call__(self): + """Increament the counter and return the new value""" + i = self.blocksize - 1 + while i > -1: + c = self.value[i] = byte_chr((byte_ord(self.value[i]) + 1) % 256) + if c != zero_byte: + return self.value.tostring() + i -= 1 + # counter reset + x = deflate_long(self.overflow, add_sign_padding=False) + self.value = array.array('c', zero_byte * (self.blocksize - len(x)) + x) + return self.value.tostring() + + def new(cls, nbits, initial_value=long(1), overflow=long(0)): + return cls(nbits, initial_value=initial_value, overflow=overflow) + new = classmethod(new) + + +def constant_time_bytes_eq(a, b): + if len(a) != len(b): + return False + res = 0 + # noinspection PyUnresolvedReferences + for i in (xrange if PY2 else range)(len(a)): + res |= byte_ord(a[i]) ^ byte_ord(b[i]) + return res == 0 diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/win_pageant.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/win_pageant.py new file mode 100644 index 0000000..20b1b0b --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/paramiko/win_pageant.py @@ -0,0 +1,134 @@ +# Copyright (C) 2005 John Arbash-Meinel +# Modified up by: Todd Whiteman +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +""" +Functions for communicating with Pageant, the basic windows ssh agent program. +""" + +import array +import ctypes.wintypes +import platform +import struct +from paramiko.util import * + +try: + import _thread as thread # Python 3.x +except ImportError: + import thread # Python 2.5-2.7 + +from . import _winapi + + +_AGENT_COPYDATA_ID = 0x804e50ba +_AGENT_MAX_MSGLEN = 8192 +# Note: The WM_COPYDATA value is pulled from win32con, as a workaround +# so we do not have to import this huge library just for this one variable. +win32con_WM_COPYDATA = 74 + + +def _get_pageant_window_object(): + return ctypes.windll.user32.FindWindowA('Pageant', 'Pageant') + + +def can_talk_to_agent(): + """ + Check to see if there is a "Pageant" agent we can talk to. + + This checks both if we have the required libraries (win32all or ctypes) + and if there is a Pageant currently running. + """ + return bool(_get_pageant_window_object()) + + +ULONG_PTR = ctypes.c_uint64 if platform.architecture()[0] == '64bit' else ctypes.c_uint32 + + +class COPYDATASTRUCT(ctypes.Structure): + """ + ctypes implementation of + http://msdn.microsoft.com/en-us/library/windows/desktop/ms649010%28v=vs.85%29.aspx + """ + _fields_ = [ + ('num_data', ULONG_PTR), + ('data_size', ctypes.wintypes.DWORD), + ('data_loc', ctypes.c_void_p), + ] + + +def _query_pageant(msg): + """ + Communication with the Pageant process is done through a shared + memory-mapped file. + """ + hwnd = _get_pageant_window_object() + if not hwnd: + # Raise a failure to connect exception, pageant isn't running anymore! + return None + + # create a name for the mmap + map_name = 'PageantRequest%08x' % thread.get_ident() + + pymap = _winapi.MemoryMap(map_name, _AGENT_MAX_MSGLEN, + _winapi.get_security_attributes_for_user(), + ) + with pymap: + pymap.write(msg) + # Create an array buffer containing the mapped filename + char_buffer = array.array("c", b(map_name) + zero_byte) + char_buffer_address, char_buffer_size = char_buffer.buffer_info() + # Create a string to use for the SendMessage function call + cds = COPYDATASTRUCT(_AGENT_COPYDATA_ID, char_buffer_size, + char_buffer_address) + + response = ctypes.windll.user32.SendMessageA(hwnd, + win32con_WM_COPYDATA, ctypes.sizeof(cds), ctypes.byref(cds)) + + if response > 0: + pymap.seek(0) + datalen = pymap.read(4) + retlen = struct.unpack('>I', datalen)[0] + return datalen + pymap.read(retlen) + return None + + +class PageantConnection(object): + """ + Mock "connection" to an agent which roughly approximates the behavior of + a unix local-domain socket (as used by Agent). Requests are sent to the + pageant daemon via special Windows magick, and responses are buffered back + for subsequent reads. + """ + + def __init__(self): + self._response = None + + def send(self, data): + self._response = _query_pageant(data) + + def recv(self, n): + if self._response is None: + return '' + ret = self._response[:n] + self._response = self._response[n:] + if self._response == '': + self._response = None + return ret + + def close(self): + pass diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip-1.5.2.dist-info/DESCRIPTION.rst b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip-1.5.2.dist-info/DESCRIPTION.rst new file mode 100644 index 0000000..55562e2 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip-1.5.2.dist-info/DESCRIPTION.rst @@ -0,0 +1,65 @@ +Project Info +============ + +* Project Page: https://github.com/pypa/pip +* Install howto: http://www.pip-installer.org/en/latest/installing.html +* Changelog: http://www.pip-installer.org/en/latest/news.html +* Bug Tracking: https://github.com/pypa/pip/issues +* Mailing list: http://groups.google.com/group/python-virtualenv +* Docs: http://www.pip-installer.org/ +* IRC: #pip on Freenode. + +Quickstart +========== + +Install a package: + +:: + + $ pip install SomePackage==1.0 + [...] + Successfully installed SomePackage + +Show what files were installed: + +:: + + $ pip show --files SomePackage + Name: SomePackage + Version: 1.0 + Location: /my/env/lib/pythonx.x/site-packages + Files: + ../somepackage/__init__.py + [...] + +List what packages are outdated: + +:: + + $ pip list --outdated + SomePackage (Current: 1.0 Latest: 2.0) + +Upgrade a package: + +:: + + $ pip install --upgrade SomePackage + [...] + Found existing installation: SomePackage 1.0 + Uninstalling SomePackage: + Successfully uninstalled SomePackage + Running setup.py install for SomePackage + Successfully installed SomePackage + +Uninstall a package: + +:: + + $ pip uninstall SomePackage + Uninstalling SomePackage: + /my/env/lib/pythonx.x/site-packages/somepackage + Proceed (y/n)? y + Successfully uninstalled SomePackage + + + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip-1.5.2.dist-info/METADATA b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip-1.5.2.dist-info/METADATA new file mode 100644 index 0000000..0445978 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip-1.5.2.dist-info/METADATA @@ -0,0 +1,92 @@ +Metadata-Version: 2.0 +Name: pip +Version: 1.5.2 +Summary: A tool for installing and managing Python packages. +Home-page: http://www.pip-installer.org +Author: The pip developers +Author-email: python-virtualenv@groups.google.com +License: MIT +Keywords: easy_install distutils setuptools egg virtualenv +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Topic :: Software Development :: Build Tools +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.1 +Classifier: Programming Language :: Python :: 3.2 +Classifier: Programming Language :: Python :: 3.3 +Provides-Extra: testing +Requires-Dist: pytest; extra == 'testing' +Requires-Dist: virtualenv (>=1.10); extra == 'testing' +Requires-Dist: scripttest (>=1.3); extra == 'testing' +Requires-Dist: mock; extra == 'testing' + +Project Info +============ + +* Project Page: https://github.com/pypa/pip +* Install howto: http://www.pip-installer.org/en/latest/installing.html +* Changelog: http://www.pip-installer.org/en/latest/news.html +* Bug Tracking: https://github.com/pypa/pip/issues +* Mailing list: http://groups.google.com/group/python-virtualenv +* Docs: http://www.pip-installer.org/ +* IRC: #pip on Freenode. + +Quickstart +========== + +Install a package: + +:: + + $ pip install SomePackage==1.0 + [...] + Successfully installed SomePackage + +Show what files were installed: + +:: + + $ pip show --files SomePackage + Name: SomePackage + Version: 1.0 + Location: /my/env/lib/pythonx.x/site-packages + Files: + ../somepackage/__init__.py + [...] + +List what packages are outdated: + +:: + + $ pip list --outdated + SomePackage (Current: 1.0 Latest: 2.0) + +Upgrade a package: + +:: + + $ pip install --upgrade SomePackage + [...] + Found existing installation: SomePackage 1.0 + Uninstalling SomePackage: + Successfully uninstalled SomePackage + Running setup.py install for SomePackage + Successfully installed SomePackage + +Uninstall a package: + +:: + + $ pip uninstall SomePackage + Uninstalling SomePackage: + /my/env/lib/pythonx.x/site-packages/somepackage + Proceed (y/n)? y + Successfully uninstalled SomePackage + + + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip-1.5.2.dist-info/RECORD b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip-1.5.2.dist-info/RECORD new file mode 100644 index 0000000..57f4d96 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip-1.5.2.dist-info/RECORD @@ -0,0 +1,434 @@ +pip/__init__.py,sha256=F1oPkByWYi_hXlvFMHoOdsrdTOZuRZXBRUbPk-JEZN0,9450 +pip/__main__.py,sha256=9JBJhprGRLUy1fEvAdufs0tsjKFAvFAY_nTde6GDkHk,116 +pip/basecommand.py,sha256=N_nE7BCcoMA7t2nRNTiJB8T__1XqI74SJI2G72VaM2E,6578 +pip/baseparser.py,sha256=lWhYrNrlZMelrxRNKFR5bgYfC9OsBgC5KdwsFtxBd_A,8075 +pip/cmdoptions.py,sha256=C0JuSfhGIgrp2hMoVDYVPekPlPiG0wIFcIIFDbrsatg,9507 +pip/download.py,sha256=1XqGT6iAx6oYqpDfykTVzWAB-GEyXCB6YPlN4s7LXHU,20583 +pip/exceptions.py,sha256=wAoboA4PdhGN7xH-ayf_dcDFPYZe9XAivAlZJbOgCN4,1086 +pip/index.py,sha256=CLPb0crVhOQ3aZpl4feUKpf1pVR6qLhBiJTa71PoIkM,40403 +pip/locations.py,sha256=YyFyCLYADKgT5x-Ctj_LeZl5bEzkbBXuR2Iv8IbVqDA,6202 +pip/log.py,sha256=1fW7cVRIRBhfqWz4JH2HhJRHzVQ4PJTRbolRj3S33f8,9455 +pip/pep425tags.py,sha256=jb5Rq395Gz_Uv8kn3L9Im1HX7EhEj8nqyYX0nXulzWo,2969 +pip/req.py,sha256=vpu3kQURuRu3WFy7eqSzkc17QhajNSuL_5P-wqIduxY,81809 +pip/runner.py,sha256=VkcZKNikprpDOSl2Y3m0FaQbdGuYsoHkxdhjtL0N3oA,431 +pip/status_codes.py,sha256=sEFHUaUJbqv8iArL3HAtcztWZmGOFX01hTesSytDEh0,116 +pip/util.py,sha256=GTnXa80tWauVlIvSQiYeNw12ly5X4hMPmDbRVQ79hwk,24172 +pip/wheel.py,sha256=VEBvbZm6bszXbLkf1l2gZsV0AuVSRh9GCJuuSCPpBQ0,18528 +pip/_vendor/__init__.py,sha256=f-xO4dF7jRP89yrL4h26_nULYgYTzgnTgUFlkDasJrc,266 +pip/_vendor/pkg_resources.py,sha256=j8k9HBJ1Ke1DFJqaEus-vehhWiWVOg81RGMVIQIpYLA,98660 +pip/_vendor/re-vendor.py,sha256=PcdZ40d0ohMsdJmA4t0AeAWbPXi1tFsvAwA5KE5FGeY,773 +pip/_vendor/six.py,sha256=KjtYDthFs13N-g4p-r-HNDlEUWtUYdmIov3F2v09ogg,12415 +pip/_vendor/_markerlib/__init__.py,sha256=2hgtRuYDOrimZF9-ENCkrP8gnJ59HZEtlk-zoTEvn1Y,564 +pip/_vendor/_markerlib/markers.py,sha256=YuFp0-osufFIoqnzG3L0Z2fDCx4Vln3VUDeXJ2DA_1I,3979 +pip/_vendor/colorama/__init__.py,sha256=3fyfgiUcARAsD3l4Zz8pBp3KiLncU-AThua1cTX6ldg,213 +pip/_vendor/colorama/ansi.py,sha256=SFUHe0pWuHrmi3APvtMWb4t4gTS_y9uH4ocnvHgjxBg,1089 +pip/_vendor/colorama/ansitowin32.py,sha256=lCEVASXHaJU8gAFnAH39ePjopsJoOtY4tdUAorTnUtY,6810 +pip/_vendor/colorama/initialise.py,sha256=nvlYpEgOfshRkRox70G82lDDHWrfwJOEoVz7OjZ-Zkk,1353 +pip/_vendor/colorama/win32.py,sha256=MyJHHf0Dc2_-qYzTafO3KPG_35UvNNFOPuN123UrjsI,4833 +pip/_vendor/colorama/winterm.py,sha256=03pEpFrkM2EfSPb-LP727AxY8JQ_1s0Sc1vnyN-cAao,4326 +pip/_vendor/distlib/__init__.py,sha256=w5CW1eatBDb6FJKjhSMvlSZvTEK8t-m6QUCvoO5Qgy8,534 +pip/_vendor/distlib/compat.py,sha256=pVILHBDu1P72lO1lEgPFpwRWGs4Cj2-eo_xOPLKjprk,38875 +pip/_vendor/distlib/database.py,sha256=reYrKl6tqHs3eOrMrdXX4W0OGlBqathuqSzCy-696MI,49138 +pip/_vendor/distlib/index.py,sha256=Z9eTnLOf0Cjrsg_JjG0IYwLJFXIfdmtHVnR5UBg_xVw,19159 +pip/_vendor/distlib/locators.py,sha256=7wJQ11VE8K3G-E_T4jgvznpcr-wA69JRys8Y7yHX8lU,46553 +pip/_vendor/distlib/manifest.py,sha256=I-JG2bVBN3Zmf4gpt9MFjgsukcIt0rMrz95jQ1kwbgE,13497 +pip/_vendor/distlib/markers.py,sha256=iRrVWwpyVwjkKJSX8NEQ92_MRMwpROcfNGKCD-Ch1QM,6282 +pip/_vendor/distlib/metadata.py,sha256=Pqrt9Wh6xKzmYK-Pioe2-9ETgGLuS4ul5jbxYpOkXpQ,36356 +pip/_vendor/distlib/resources.py,sha256=fjqggvPVmijgTXvPyUaUmwk3t3OAVihCroKRokpnnvc,10570 +pip/_vendor/distlib/scripts.py,sha256=OdVEbZoVW4oJ3qrbbDy2Ke87sWwYs9-qLnwjdg3EQ30,11979 +pip/_vendor/distlib/t32.exe,sha256=snr0x6iR5Yd7ZndiuvGMuuQGg8VklUc5538uQcOeVYQ,91136 +pip/_vendor/distlib/t64.exe,sha256=71TYxgPfviERRKhyYaXPx2pl4o4ugs5zNWuIMLlm7ss,94720 +pip/_vendor/distlib/util.py,sha256=5GbifBqPpf1vYrRgH9vh8M5UOys5rJaNEvsmfZFGR8s,49824 +pip/_vendor/distlib/version.py,sha256=cEcz_5Zh2AJjrM7ug7_TqPXS4wfPN50tKsgEYHA2mzg,21803 +pip/_vendor/distlib/w32.exe,sha256=QoyveFPxLH-db4j2YDzb-VmP7DmGT1vHwPOLGpzc3uw,87040 +pip/_vendor/distlib/w64.exe,sha256=haGpLW73-UaPHkjhKaQVPTBJCZ9Lg1rMBZdhpi7f_V8,91648 +pip/_vendor/distlib/wheel.py,sha256=mNoVSjvMGJ1Z2g2wMgj6mXQ2dQav4kuLjQyVST6RLMk,27956 +pip/_vendor/distlib/_backport/__init__.py,sha256=bqS_dTOH6uW9iGgd0uzfpPjo6vZ4xpPZ7kyfZJ2vNaw,274 +pip/_vendor/distlib/_backport/misc.py,sha256=KWecINdbFNOxSOP1fGF680CJnaC6S4fBRgEtaYTw0ig,971 +pip/_vendor/distlib/_backport/shutil.py,sha256=AUi8718iRoJ9K26mRi-rywtt8Gx7ykvrvbUbZszjfYE,25650 +pip/_vendor/distlib/_backport/sysconfig.cfg,sha256=swZKxq9RY5e9r3PXCrlvQPMsvOdiWZBTHLEbqS8LJLU,2617 +pip/_vendor/distlib/_backport/sysconfig.py,sha256=NyBr9pogW5D_y2KNC_sua6zuf8avM-K5hhZFGVNLXSE,26964 +pip/_vendor/distlib/_backport/tarfile.py,sha256=bjyTNONZb-YEXrHFLExOSuagtSOoPaONP2UUoxwkAqE,92627 +pip/_vendor/html5lib/__init__.py,sha256=b84YAwommwg18ARseUZe5M_9G82bzgDsmKeDPjddFpE,714 +pip/_vendor/html5lib/constants.py,sha256=OM0DD_2IBrVSGkJ53XQ5aQkdBR-Nh1P6IT5jNBZA3D0,86469 +pip/_vendor/html5lib/html5parser.py,sha256=z9kGWkgpOSw3Mql6jZ5iu89MyDsgyodKmuObCMpFIN0,117500 +pip/_vendor/html5lib/ihatexml.py,sha256=MT12cVXAKaW-ALUkUeN175HpUP73xK8wAIpPzQ8cgfI,16581 +pip/_vendor/html5lib/inputstream.py,sha256=_SCjEha5CCl1P-NaEUIUujoiZacSb7m_RwsJF--2DT4,30602 +pip/_vendor/html5lib/sanitizer.py,sha256=sg7g5CXF9tfvykIoSVAvA8647MgScy3ncZC7IYH-8SA,16428 +pip/_vendor/html5lib/tokenizer.py,sha256=6Uf8sDUkvNn661bcBSBYUCTfXzSs9EyCTiPcj5PAjYI,76929 +pip/_vendor/html5lib/utils.py,sha256=s98A1vQhthux1E8-FdxwCzw_LZvP1V_yJPbWvokiyhw,2413 +pip/_vendor/html5lib/filters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_vendor/html5lib/filters/_base.py,sha256=z-IU9ZAYjpsVsqmVt7kuWC63jR11hDMr6CVrvuao8W0,286 +pip/_vendor/html5lib/filters/alphabeticalattributes.py,sha256=fpRLbz6TCe5yXEkGmyMlJ80FekWsTR-sHk3Ano0U9LQ,624 +pip/_vendor/html5lib/filters/inject_meta_charset.py,sha256=xllv1I7unxhcyZTf3LTsv30wh2mAkT7wmTZx7zIhpuY,2746 +pip/_vendor/html5lib/filters/lint.py,sha256=RJT0pxEdam6smwEeTcJEYd5KHs66id1f60FOEPENhqI,4062 +pip/_vendor/html5lib/filters/optionaltags.py,sha256=4ozLwBgMRaxe7iqxefLQpDhp3irK7YHo9LgSGsvZYMw,10500 +pip/_vendor/html5lib/filters/sanitizer.py,sha256=MvGUs_v2taWPgGhjxswRSUiHfxrqMUhsNPz-eSeUYUQ,352 +pip/_vendor/html5lib/filters/whitespace.py,sha256=LbOUcC0zQ9z703KNZrArOr0kVBO7OMXjKjucDW32LU4,1142 +pip/_vendor/html5lib/serializer/__init__.py,sha256=xFXFP-inaTNlbnau5c5DGrH_O8yPm-C6HWbJxpiSqFE,490 +pip/_vendor/html5lib/serializer/htmlserializer.py,sha256=1iEa2-9OCaHBwKbdMxHQH7u7spveQaLgef9nvtmh5mY,12468 +pip/_vendor/html5lib/treebuilders/__init__.py,sha256=Xz4X6B5DA1R-5GyRa44j0sJwfl6dUNyb0NBu9-7sK3U,3405 +pip/_vendor/html5lib/treebuilders/_base.py,sha256=Xf0FZVcVwIQS6tEseJdj5wKbYucbNCnbAsnsG4lONis,13711 +pip/_vendor/html5lib/treebuilders/dom.py,sha256=9OWMbxD-gf04jsjtM-f4dtnh8gc-ZyUqIxYYe4HItFw,11328 +pip/_vendor/html5lib/treebuilders/etree.py,sha256=etbO6yQlyV46rWlj9mSyVqQOWrgoHgyJ01Tut4lWZkk,12621 +pip/_vendor/html5lib/treebuilders/etree_lxml.py,sha256=z3Bnfm2MstEEb_lbaAeicl5l-ab6MSQa5Q1ZZreK7Pc,14031 +pip/_vendor/html5lib/treewalkers/__init__.py,sha256=44g-xYZEoYxzkMu6CepBTLm4m-g9iy7Vm_IG8PWAbhY,2323 +pip/_vendor/html5lib/treewalkers/_base.py,sha256=OeT9dqryMeXwKrnIyvylqs0rM9PKjQjiWKxmjYlPbRg,6718 +pip/_vendor/html5lib/treewalkers/dom.py,sha256=mAg05wBWN2k-CGPoo0KNxa55QAlHciNccp8AezCa8j8,1457 +pip/_vendor/html5lib/treewalkers/etree.py,sha256=86nGaOMgQqsiUtglRCwcIA8QYV_41Tl7TOZy7sOttYM,4445 +pip/_vendor/html5lib/treewalkers/genshistream.py,sha256=IbBFrlgi-59-K7P1zm0d7ZFIknBN4c5E57PHJDkx39s,2278 +pip/_vendor/html5lib/treewalkers/lxmletree.py,sha256=vWfXWK3GOSrq2trQm2aPmIRWPhVuYDZ3g9Fu8hUeBQg,6215 +pip/_vendor/html5lib/treewalkers/pulldom.py,sha256=9W6i8yWtUzayV6EwX-okVacttHaqpQZwdBCc2S3XeQ4,2302 +pip/_vendor/html5lib/trie/__init__.py,sha256=mec5zyJ5wIKRM8819gIcIsYQwncg91rEmPwGH1dG3Ho,212 +pip/_vendor/html5lib/trie/_base.py,sha256=WGY8SGptFmx4O0aKLJ54zrIQOoyuvhS0ngA36vAcIcc,927 +pip/_vendor/html5lib/trie/datrie.py,sha256=EQpqSfkZRuTbE-DuhW7xMdVDxdZNZ0CfmnYfHA_3zxM,1178 +pip/_vendor/html5lib/trie/py.py,sha256=wXmQLrZRf4MyWNyg0m3h81m9InhLR7GJ002mIIZh-8o,1775 +pip/_vendor/requests/__init__.py,sha256=MZY7eU0YELAQ2jxZ0-FA6D8BBzv0CVQZFQ0qzOazKCo,1856 +pip/_vendor/requests/adapters.py,sha256=I6AMOdgE6lBRlmT6q_tStKupU_HbN8BGWCjcou0WNWw,14674 +pip/_vendor/requests/api.py,sha256=RildLmThlR3BNFI6C3bS1bvxV5G65ehCVrBGkYHejDs,4333 +pip/_vendor/requests/auth.py,sha256=DnRhJ6uJtR5nvoZCnkdwTBSkt6J0f78U0v8DOpKAS4g,6173 +pip/_vendor/requests/cacert.pem,sha256=ak7q_q8ozHdQ9ff27U-E1vCNrLisFRQSMy9zJkdpQlM,308434 +pip/_vendor/requests/certs.py,sha256=wSaqhSNoB0igp6Da-hWw0jtXICKXBbL8aS9swthlt50,544 +pip/_vendor/requests/compat.py,sha256=JGrJPV2YGatzwrexl9kSt8Z8QtmFboRZH9ywsLK_MMA,2556 +pip/_vendor/requests/cookies.py,sha256=BjMKtrI8TXQD5oQVeToYtkRBF149eI85UhWsBGwsJac,16686 +pip/_vendor/requests/exceptions.py,sha256=2VUbK6VLVL1iVmvmNmFRexQOF7ZPghlJmktbeXSlUIk,1609 +pip/_vendor/requests/hooks.py,sha256=9vNiuiRHRd5Qy6BX_0p1H3NsUzDo1M_HaFR2AFL41Tg,820 +pip/_vendor/requests/models.py,sha256=bEzgDnHhCtMfoQ3DbReLvytCsGSrioZwvUnTt_inHRI,25372 +pip/_vendor/requests/sessions.py,sha256=0gqM0o9zsLj3Or9T_1FbARwOOjHxipRoNeXwKrYDFrY,19430 +pip/_vendor/requests/status_codes.py,sha256=LYpqLv4AEKuTPby-QSvgl_gI7fcVlUDqSBcndIwX-Qg,3136 +pip/_vendor/requests/structures.py,sha256=n7kgBOUJnQXWTJpRh0Sza4kuWhuWMkn3WTZ4I25_ARk,3575 +pip/_vendor/requests/utils.py,sha256=w48YWMt5YmRDbAYYXpV0N9ul-JrTxUyRdshRUjbT_Q8,19653 +pip/_vendor/requests/packages/__init__.py,sha256=aXkbNCjM_WhryRBocE4AaA_p7-CTxL5LOutY7XzKm4s,62 +pip/_vendor/requests/packages/charade/__init__.py,sha256=RZ9HyxftSwLoctutUpJlSBwQmorUWR0hvULib7FZqPI,2286 +pip/_vendor/requests/packages/charade/__main__.py,sha256=0D8W95no2okaRWrtVkOmliW6uOiLpt1cExFP8j1jBLQ,144 +pip/_vendor/requests/packages/charade/big5freq.py,sha256=D8oTdz-GM7Jg8TsaWJDm65vM_OLHC3xub6qUJ3rOgsQ,82594 +pip/_vendor/requests/packages/charade/big5prober.py,sha256=Y00SRaY2I8vLBGEQm5eTxqtpaVCBf-vcCkoQwwbq3NM,1726 +pip/_vendor/requests/packages/charade/chardistribution.py,sha256=b38wwYRsKy_WgNidMPRsmvcMQxI-35h7PAaQ4jCbQ-E,9457 +pip/_vendor/requests/packages/charade/charsetgroupprober.py,sha256=zz5IRK6Psu5SvLrQCyB-Vl6nqQ6A9HBApDA-EfSAE9M,3897 +pip/_vendor/requests/packages/charade/charsetprober.py,sha256=Z48o2KiOj23FNqYH8FqzhH5m1qdm3rI8DcTm2Yqtklg,1902 +pip/_vendor/requests/packages/charade/codingstatemachine.py,sha256=D51CScwM5MvhpKce-YCKCcpS4-3zh-oXFIT1o9pJmF4,2379 +pip/_vendor/requests/packages/charade/compat.py,sha256=5mm6yrHwef1JEG5OxkPJlSq5lkjLVpEGh3iPgFBkpkM,1157 +pip/_vendor/requests/packages/charade/constants.py,sha256=9OMbGm5gzog4mppSefqay1cyzi-PQ32Y6yLJ3eRejM0,1374 +pip/_vendor/requests/packages/charade/cp949prober.py,sha256=vvmyKLeNj8DMZCGTvdlrLRw8XHgTOFhPgsecO9LGyXc,1826 +pip/_vendor/requests/packages/charade/escprober.py,sha256=Qt1afdsroxvIYN0lWbAel77LuUNQqXZoSlcdyKv0Du8,3273 +pip/_vendor/requests/packages/charade/escsm.py,sha256=qK7e4yP0lhXkKZM1QQLJz_MbLeafNhQ_uSQ0ixoGV6o,8081 +pip/_vendor/requests/packages/charade/eucjpprober.py,sha256=AnV_CLbv0rd7zRvdFBjKE_uMoQxGUNVtrwfLOgcivF8,3768 +pip/_vendor/requests/packages/charade/euckrfreq.py,sha256=T5saK5mImySG5ygQPtsp6o2uKulouCwYm2ElOyFkJqU,45978 +pip/_vendor/requests/packages/charade/euckrprober.py,sha256=QaDYQ7ZPR2_plHrDr61q52DK03XPJNYv5BT5U5Balwo,1717 +pip/_vendor/requests/packages/charade/euctwfreq.py,sha256=G_I0BW9i1w0ONeeUwIYqV7_U09buIHdqh-wNHVaql7I,34872 +pip/_vendor/requests/packages/charade/euctwprober.py,sha256=JKzKb-fzogPadezJVr2UrtWPok4jCaiY-quhRF28rGU,1717 +pip/_vendor/requests/packages/charade/gb2312freq.py,sha256=M2gFdo_qQ_BslStEchrPW5CrPEZEacC0uyDLw4ok-kY,36011 +pip/_vendor/requests/packages/charade/gb2312prober.py,sha256=5w2utRFlSd1uhwOPcI24WLtWbRqpgWB4udeWbhOm_Y4,1722 +pip/_vendor/requests/packages/charade/hebrewprober.py,sha256=yA0t70rXN-77dylAVsh0hGgn7Lay07YzFVAn_kHGmIQ,13642 +pip/_vendor/requests/packages/charade/jisfreq.py,sha256=ZcL4R5ekHHbP2KCYGakVMBsiKqZZZAABzhwi-uRkOps,47315 +pip/_vendor/requests/packages/charade/jpcntx.py,sha256=Yf-J76yfVSoMG0KxXYQIdxY_7spwywQXHOg8c4NlvNk,19328 +pip/_vendor/requests/packages/charade/langbulgarianmodel.py,sha256=CZ730Y31wVUCiy-j3-Kk_5dWSUF0xubMBTxyqV2-TNo,13013 +pip/_vendor/requests/packages/charade/langcyrillicmodel.py,sha256=ZZwHBjJE614EdOBbGH_JVCsq5sn3PaxifdJiwj0pJ1I,18054 +pip/_vendor/requests/packages/charade/langgreekmodel.py,sha256=NMxbf4u8ol6ErIxre4KQLi01sQ1Na_BxLaKySbiyp0k,12853 +pip/_vendor/requests/packages/charade/langhebrewmodel.py,sha256=GHYAOVeVklYsaw2LVRTO0HG5kS6mc4DEVw0gCaNs130,11519 +pip/_vendor/requests/packages/charade/langhungarianmodel.py,sha256=jz0CosvKZ46AdfBtFqU8ghXoZFlbw9DFjxOvuayGXJY,12761 +pip/_vendor/requests/packages/charade/langthaimodel.py,sha256=sL4H3aEySkGwaTZey_lJmwLWp8IcQMClQ8ow0v51ulU,11475 +pip/_vendor/requests/packages/charade/latin1prober.py,sha256=rNnBnmCWt9wQnSmroGLJn2EbJzEgGdNs_riE_bj77KU,5387 +pip/_vendor/requests/packages/charade/mbcharsetprober.py,sha256=t9JmoVQjlmTQckyHs74LSglVnaX0Qa773tcw8NT1FRM,3354 +pip/_vendor/requests/packages/charade/mbcsgroupprober.py,sha256=7uI8pj7ISywnMqYtKv6E4T6GyohD9e2nGqPltEEx41Y,2021 +pip/_vendor/requests/packages/charade/mbcssm.py,sha256=8Q2abMHRxuEy1dCa8PzMvh3ne3w-HLEZoBAeyeIsU-A,20183 +pip/_vendor/requests/packages/charade/sbcharsetprober.py,sha256=LssNp7jPt8uESgY3T6xHf2Sij3W4p4DE1W6d-tFcuuQ,4913 +pip/_vendor/requests/packages/charade/sbcsgroupprober.py,sha256=m1SoNdSjaBA6q9jv3h26pZnDvLSs6rXfLvh68ngZbew,3360 +pip/_vendor/requests/packages/charade/sjisprober.py,sha256=3KiDvYnp0zYJgMiG5HQzQIzPGfYlRW9h5l2huAMCXUQ,3825 +pip/_vendor/requests/packages/charade/universaldetector.py,sha256=r_8wd-os0T4H_IRrncIy_aanaUMJZxO2nypgOc7WLgA,6832 +pip/_vendor/requests/packages/charade/utf8prober.py,sha256=g6uljNbunvXCcF9tNubwNg_oPoCEOZC2-F1Zg0eVu8s,2728 +pip/_vendor/requests/packages/chardet/__init__.py,sha256=8-39Dg2qEuod5DNN7RMdn2ZYOO9zFU3fFfaE80iDWGc,1295 +pip/_vendor/requests/packages/chardet/big5freq.py,sha256=D8oTdz-GM7Jg8TsaWJDm65vM_OLHC3xub6qUJ3rOgsQ,82594 +pip/_vendor/requests/packages/chardet/big5prober.py,sha256=XX96C--6WKYW36mL-z7pJSAtc169Z8ZImByCP4pEN9A,1684 +pip/_vendor/requests/packages/chardet/chardetect.py,sha256=8g-dRSA97bSE6M25Tqe1roKKtl3XHSMnqi6vTzpHNV0,1141 +pip/_vendor/requests/packages/chardet/chardistribution.py,sha256=cUARQFr1oTLXeJCDQrDRkUP778AvSMzhSCnG8VLCV58,9226 +pip/_vendor/requests/packages/chardet/charsetgroupprober.py,sha256=0lKk7VE516fgMw119tNefFqLOxKfIE9WfdkpIT69OKU,3791 +pip/_vendor/requests/packages/chardet/charsetprober.py,sha256=Z48o2KiOj23FNqYH8FqzhH5m1qdm3rI8DcTm2Yqtklg,1902 +pip/_vendor/requests/packages/chardet/codingstatemachine.py,sha256=E85rYhHVMw9xDEJVgiQhp0OnLGr6i2r8_7QOWMKTH08,2318 +pip/_vendor/requests/packages/chardet/compat.py,sha256=5mm6yrHwef1JEG5OxkPJlSq5lkjLVpEGh3iPgFBkpkM,1157 +pip/_vendor/requests/packages/chardet/constants.py,sha256=-UnY8U7EP7z9fTyd09yq35BEkSFEAUAiv9ohd1DW1s4,1335 +pip/_vendor/requests/packages/chardet/cp949prober.py,sha256=FMvdLyB7fejPXRsTbca7LK1P3RUvvssmjUNyaEfz8zY,1782 +pip/_vendor/requests/packages/chardet/escprober.py,sha256=q5TcQKeVq31WxrW7Sv8yjpZkjEoaHO8S92EJZ9hodys,3187 +pip/_vendor/requests/packages/chardet/escsm.py,sha256=7iljEKN8lXTh8JFXPUSwlibMno6R6ksq4evLxbkzfro,7839 +pip/_vendor/requests/packages/chardet/eucjpprober.py,sha256=5IpfSEjAb7h3hcGMd6dkU80O900C2N6xku28rdYFKuc,3678 +pip/_vendor/requests/packages/chardet/euckrfreq.py,sha256=T5saK5mImySG5ygQPtsp6o2uKulouCwYm2ElOyFkJqU,45978 +pip/_vendor/requests/packages/chardet/euckrprober.py,sha256=Wo7dnZ5Erw_nB4H-m5alMiOxOuJUmGHlwCSaGqExDZA,1675 +pip/_vendor/requests/packages/chardet/euctwfreq.py,sha256=G_I0BW9i1w0ONeeUwIYqV7_U09buIHdqh-wNHVaql7I,34872 +pip/_vendor/requests/packages/chardet/euctwprober.py,sha256=upS2P6GuT5ujOxXYw-RJLcT7A4PTuo27KGUKU4UZpIQ,1676 +pip/_vendor/requests/packages/chardet/gb2312freq.py,sha256=M2gFdo_qQ_BslStEchrPW5CrPEZEacC0uyDLw4ok-kY,36011 +pip/_vendor/requests/packages/chardet/gb2312prober.py,sha256=VWnjoRa83Y6V6oczMaxyUr0uy48iCnC2nzk9zfEIRHc,1681 +pip/_vendor/requests/packages/chardet/hebrewprober.py,sha256=8pdoUfsVXf_L4BnJde_BewS6H2yInV5688eu0nFhLHY,13359 +pip/_vendor/requests/packages/chardet/jisfreq.py,sha256=ZcL4R5ekHHbP2KCYGakVMBsiKqZZZAABzhwi-uRkOps,47315 +pip/_vendor/requests/packages/chardet/jpcntx.py,sha256=9fJ9oS0BUarcdZNySwmzVRuT03sYdClSmFwXDj3yVNg,19104 +pip/_vendor/requests/packages/chardet/langbulgarianmodel.py,sha256=ZyPsA796MSVhYdfWhMCgKWckupAKAnKqWcE3Cl3ej6o,12784 +pip/_vendor/requests/packages/chardet/langcyrillicmodel.py,sha256=fkcd5OvogUp-GrNDWAZPgkYsSRCD2omotAEvqjlmLKE,17725 +pip/_vendor/requests/packages/chardet/langgreekmodel.py,sha256=QHMy31CH_ot67UCtmurCEKqKx2WwoaKrw2YCYYBK2Lw,12628 +pip/_vendor/requests/packages/chardet/langhebrewmodel.py,sha256=4ASl5vzKJPng4H278VHKtRYC03TpQpenlHTcsmZH1rE,11318 +pip/_vendor/requests/packages/chardet/langhungarianmodel.py,sha256=SXwuUzh49_cBeMXhshRHdrhlkz0T8_pZWV_pdqBKNFk,12536 +pip/_vendor/requests/packages/chardet/langthaimodel.py,sha256=-k7djh3dGKngAGnt3WfuoJN7acDcWcmHAPojhaUd7q4,11275 +pip/_vendor/requests/packages/chardet/latin1prober.py,sha256=g67gqZ2z89LUOlR7BZEAh4-p5a1yGWss9nWy8FCNm8Q,5241 +pip/_vendor/requests/packages/chardet/mbcharsetprober.py,sha256=9rOCjDVsmSMp6e7q2syqak22j7lrbUZhJhMee2gbVL0,3268 +pip/_vendor/requests/packages/chardet/mbcsgroupprober.py,sha256=SHRzNPLpDXfMJLA8phCHVU0WgqbgDCNxDQMolGX_7yk,1967 +pip/_vendor/requests/packages/chardet/mbcssm.py,sha256=UuiA4Ic8vEc0XpTKDneqZyiH2TwGuFVZxOxWJep3X_4,19608 +pip/_vendor/requests/packages/chardet/sbcharsetprober.py,sha256=Xq0lODqJnDgxglBiQI4BqTFiPbn63-0a5XNA5-hVu7U,4793 +pip/_vendor/requests/packages/chardet/sbcsgroupprober.py,sha256=8hLyH8RAG-aohBo7o_KciWVgRo42ZE_zEtuNG1JMRYI,3291 +pip/_vendor/requests/packages/chardet/sjisprober.py,sha256=1RjpQ2LU2gvoEB_4O839xDQVchWx2fG_C7_vXh52P5I,3734 +pip/_vendor/requests/packages/chardet/universaldetector.py,sha256=GkZdwNyNfbFWC8I1uqnzyhOUF7favWCqCOKqdQlx6gQ,6831 +pip/_vendor/requests/packages/chardet/utf8prober.py,sha256=7tdNZGrJY7jZUBD483GGMkiP0Tx8Fp-cGvWHoAsilHg,2652 +pip/_vendor/requests/packages/urllib3/__init__.py,sha256=sLIKv9dGJjDloiVXUBBjXDWWq8bM66kcvTH2SU_WZKg,1701 +pip/_vendor/requests/packages/urllib3/_collections.py,sha256=YRYW38qs6mGVg9Kw_y_etP0nh4SRGrLM6kvpIrX3Pwo,3111 +pip/_vendor/requests/packages/urllib3/connection.py,sha256=WvAYZZl7bowCZWA7t1BRgw-i1_V7N1VfifYSFfFMFsY,5659 +pip/_vendor/requests/packages/urllib3/connectionpool.py,sha256=-uqxfjbzE5YJu4oFBuU8F0xIzHfGydacECYjPWz07zI,25767 +pip/_vendor/requests/packages/urllib3/exceptions.py,sha256=3Er35Ym4xrf5xNdTPVIAANRdRVUf41uuMsNC4spPk0c,3274 +pip/_vendor/requests/packages/urllib3/fields.py,sha256=UuuI3IGfxHwOoKcbw5FS6YuY3f4yuCvQLtumSaIigr4,5976 +pip/_vendor/requests/packages/urllib3/filepost.py,sha256=tWPY33HnFM_RPpEU9PHv9D34n67w8ZRt80ZSsWIv0Kk,2512 +pip/_vendor/requests/packages/urllib3/poolmanager.py,sha256=bMYHdNaVI5O4YrJHr1T6tE2RYHEMzM2_K0cTy7uzX2M,8977 +pip/_vendor/requests/packages/urllib3/request.py,sha256=cXTcrr9d50Rt213ZXLgCf53KNWxe4LQ8lxSV1HBYa9E,5808 +pip/_vendor/requests/packages/urllib3/response.py,sha256=aaCwS91f5p2qjAjRir9B8bCTQgyP1Zq7X4YdnHTq-8w,10537 +pip/_vendor/requests/packages/urllib3/util.py,sha256=h8o_ESIDaccpLWIGKvHOgPDU_mfHEGFAjd92bAyNtnY,21407 +pip/_vendor/requests/packages/urllib3/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_vendor/requests/packages/urllib3/contrib/ntlmpool.py,sha256=VJ-GjxpYITxSj4UDKX0iqvHwaatyg2RA3PaTym5Wp6w,4741 +pip/_vendor/requests/packages/urllib3/contrib/pyopenssl.py,sha256=8BGh2Rc73IbcFvCwJZKUInaMXkLZ1xsLaZbuOSglHfE,13871 +pip/_vendor/requests/packages/urllib3/packages/__init__.py,sha256=EKCTAOjZtPR_HC50e7X8hS5j4bkFkN87XZOT-Wdpfus,74 +pip/_vendor/requests/packages/urllib3/packages/ordered_dict.py,sha256=HtHphtStJlorzQqoIat8zUH0lqLns416gfSO9y_aSAQ,8936 +pip/_vendor/requests/packages/urllib3/packages/six.py,sha256=U-rO-WBrFS8PxHeamSl6okKCjqPF18NhiZb0qPZ67XM,11628 +pip/_vendor/requests/packages/urllib3/packages/ssl_match_hostname/__init__.py,sha256=Dss0Uyb--PV_Iu7M_S10x4r-bWpudm5mIRJNiOJ3K0o,459 +pip/_vendor/requests/packages/urllib3/packages/ssl_match_hostname/_implementation.py,sha256=fK28k37hL7-D79v9iM2fHgNK9Q1Pw0M7qVRL4rkfFjQ,3778 +pip/backwardcompat/__init__.py,sha256=AcP5dr3nL-4AGxSwsFIEUcf9ki0ROUFwfc0IrIeHaJI,3756 +pip/commands/__init__.py,sha256=N_4io-oGcWF9-raDN5TYXbGlJFsx5po36HZmwgLso6I,2236 +pip/commands/bundle.py,sha256=tK8LU3Khjkrz65y3brNP71QOBkQCb9mlv9x8s1W02T4,1787 +pip/commands/completion.py,sha256=LnJgUrpGGO4x2Y8VdwhKda4kGZWMFO28P4jYzYT5Q8k,1838 +pip/commands/freeze.py,sha256=Hyx1gzMaTFwTMcP98fwNCRVvvrWenX9j1RBziLCIo0A,4664 +pip/commands/help.py,sha256=ETLg8xfv8uFwS3KvxmsCE-I56S15jUTvfkwaPAA18pE,927 +pip/commands/install.py,sha256=Ucjr4Uov9o3MbRrwuZjuLwQNPZjomRslzoLc58LXwb0,12578 +pip/commands/list.py,sha256=FHf7H35AajbCuymiG2z8xAGNSx8W5CNZKj6Hh2QGo38,6814 +pip/commands/search.py,sha256=_4Mza0qEb6P1aDA2OROYd-KuOJg0NrITOtQoiCDJF5Q,4736 +pip/commands/show.py,sha256=ipjEcTrk-hgvFysSKJ5E9PSPXZGTuE3NIXLYvXnsdNk,2767 +pip/commands/uninstall.py,sha256=MF4zSLfMxnH3E8T673ORNWz0Bsc4C6LEI5KImpAQrck,2203 +pip/commands/unzip.py,sha256=_PeTWKOd_iRxPt_7njQ8jGFpjX006vobn593tcIyeUc,185 +pip/commands/wheel.py,sha256=2W8AidwBJizUh8pENygOSw8NkIQN_VhaHkUXD8MQb90,7248 +pip/commands/zip.py,sha256=KECCb3oCHxJqDT3kUEnlf0udp31Ckoe8oyEKdS7EKNQ,14821 +pip/vcs/__init__.py,sha256=kS31hLmJ6BgKnBu8kvXKQlJEwoj1MxYE7wfRuFL-daM,8748 +pip/vcs/bazaar.py,sha256=qUIuIqDJqwZ_nP6WR52YwvYVy1lvIUmvaT-IdxDYUHo,4943 +pip/vcs/git.py,sha256=ib3TqDwJyfjBnSRFKVe_HhNdwkmfcOZfJHbqt2RUOVg,7898 +pip/vcs/mercurial.py,sha256=71ESfgxotPPPZjiH6sMTBWcj5TS8kjgJxVnWrRb3bwo,5820 +pip/vcs/subversion.py,sha256=P31K7o83JdcipIyuEVlnpSp5KZqakb4OJ1PKT-FB7C8,10640 +pip-1.5.2.dist-info/DESCRIPTION.rst,sha256=pa0zjx8IGlQx5rjOb_zZSfuVJTUmEPJOXVwBDDTC6o0,1319 +pip-1.5.2.dist-info/entry_points.txt,sha256=_bdo6l7DL9vfgDus1OpCpZPdDitK2qCZ4as_PlBr4-I,68 +pip-1.5.2.dist-info/METADATA,sha256=z5dDWKyPtyc7U7DoyXNdjWCx4zYgHtb9W0uny5heVtI,2404 +pip-1.5.2.dist-info/pydist.json,sha256=1wkxnVxjW1x2fZDcN9jDcGEjYmzN8Ql-Eak7sHYeWYE,1369 +pip-1.5.2.dist-info/RECORD,, +pip-1.5.2.dist-info/top_level.txt,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pip-1.5.2.dist-info/WHEEL,sha256=SXYYsi-y-rEGIva8sB8iKF6bAFD6YDhmqHX5hI3fc0o,110 +/Users/mstritzinger/Projects/GSoC2014/streaming-system-git/tools/flumotion-config/pusher/bin/pip,sha256=ozC9SmjEmCKgIvanWioXJ6msdd4k4JxN2zrb7VNuh2E,288 +/Users/mstritzinger/Projects/GSoC2014/streaming-system-git/tools/flumotion-config/pusher/bin/pip2,sha256=ozC9SmjEmCKgIvanWioXJ6msdd4k4JxN2zrb7VNuh2E,288 +/Users/mstritzinger/Projects/GSoC2014/streaming-system-git/tools/flumotion-config/pusher/bin/pip2.7,sha256=ozC9SmjEmCKgIvanWioXJ6msdd4k4JxN2zrb7VNuh2E,288 +pip/_vendor/requests/compat.pyc,, +pip/_vendor/requests/packages/charade/euckrprober.pyc,, +pip/_vendor/requests/certs.pyc,, +pip/_vendor/distlib/util.pyc,, +pip/__init__.pyc,, +pip/_vendor/html5lib/treewalkers/genshistream.pyc,, +pip/_vendor/requests/packages/chardet/latin1prober.pyc,, +pip/_vendor/distlib/_backport/__init__.pyc,, +pip/vcs/bazaar.pyc,, +pip/_vendor/distlib/metadata.pyc,, +pip/_vendor/html5lib/treebuilders/__init__.pyc,, +pip/_vendor/html5lib/filters/alphabeticalattributes.pyc,, +pip/_vendor/requests/packages/urllib3/request.pyc,, +pip/_vendor/requests/packages/chardet/escprober.pyc,, +pip/_vendor/requests/adapters.pyc,, +pip/_vendor/requests/packages/chardet/langbulgarianmodel.pyc,, +pip/commands/completion.pyc,, +pip/_vendor/requests/packages/charade/gb2312prober.pyc,, +pip/_vendor/html5lib/html5parser.pyc,, +pip/_vendor/html5lib/filters/lint.pyc,, +pip/pep425tags.pyc,, +pip/_vendor/requests/packages/chardet/__init__.pyc,, +pip/_vendor/html5lib/filters/whitespace.pyc,, +pip/_vendor/requests/packages/charade/big5prober.pyc,, +pip/_vendor/requests/packages/chardet/euckrprober.pyc,, +pip/_vendor/html5lib/treebuilders/etree_lxml.pyc,, +pip/_vendor/requests/packages/charade/euctwfreq.pyc,, +pip/_vendor/distlib/database.pyc,, +pip/commands/wheel.pyc,, +pip/_vendor/requests/auth.pyc,, +pip/_vendor/html5lib/treewalkers/pulldom.pyc,, +pip/_vendor/html5lib/ihatexml.pyc,, +pip/_vendor/requests/packages/charade/mbcharsetprober.pyc,, +pip/_vendor/html5lib/treewalkers/_base.pyc,, +pip/commands/list.pyc,, +pip/_vendor/distlib/scripts.pyc,, +pip/_vendor/html5lib/filters/sanitizer.pyc,, +pip/_vendor/requests/packages/charade/euctwprober.pyc,, +pip/_vendor/requests/packages/charade/__init__.pyc,, +pip/vcs/git.pyc,, +pip/cmdoptions.pyc,, +pip/_vendor/requests/packages/urllib3/poolmanager.pyc,, +pip/_vendor/html5lib/inputstream.pyc,, +pip/_vendor/html5lib/sanitizer.pyc,, +pip/_vendor/colorama/win32.pyc,, +pip/util.pyc,, +pip/_vendor/distlib/resources.pyc,, +pip/_vendor/requests/packages/chardet/hebrewprober.pyc,, +pip/_vendor/requests/packages/urllib3/packages/ssl_match_hostname/_implementation.pyc,, +pip/_vendor/requests/packages/charade/escsm.pyc,, +pip/_vendor/requests/packages/urllib3/packages/six.pyc,, +pip/_vendor/html5lib/trie/__init__.pyc,, +pip/_vendor/html5lib/__init__.pyc,, +pip/_vendor/requests/packages/chardet/charsetgroupprober.pyc,, +pip/_vendor/requests/packages/urllib3/packages/__init__.pyc,, +pip/_vendor/distlib/_backport/misc.pyc,, +pip/_vendor/requests/packages/chardet/gb2312freq.pyc,, +pip/_vendor/requests/packages/chardet/codingstatemachine.pyc,, +pip/_vendor/requests/packages/charade/langthaimodel.pyc,, +pip/runner.pyc,, +pip/_vendor/requests/packages/charade/jpcntx.pyc,, +pip/_vendor/requests/packages/urllib3/packages/ssl_match_hostname/__init__.pyc,, +pip/_vendor/distlib/_backport/sysconfig.pyc,, +pip/_vendor/requests/packages/__init__.pyc,, +pip/commands/show.pyc,, +pip/_vendor/_markerlib/__init__.pyc,, +pip/_vendor/requests/packages/chardet/jpcntx.pyc,, +pip/_vendor/colorama/initialise.pyc,, +pip/_vendor/html5lib/treebuilders/_base.pyc,, +pip/commands/zip.pyc,, +pip/commands/help.pyc,, +pip/_vendor/requests/packages/charade/sbcsgroupprober.pyc,, +pip/_vendor/requests/utils.pyc,, +pip/_vendor/colorama/__init__.pyc,, +pip/_vendor/requests/packages/charade/__main__.pyc,, +pip/_vendor/distlib/version.pyc,, +pip/commands/uninstall.pyc,, +pip/_vendor/distlib/index.pyc,, +pip/_vendor/requests/sessions.pyc,, +pip/_vendor/html5lib/treewalkers/__init__.pyc,, +pip/_vendor/requests/packages/chardet/big5prober.pyc,, +pip/_vendor/requests/packages/chardet/langthaimodel.pyc,, +pip/commands/freeze.pyc,, +pip/_vendor/distlib/_backport/tarfile.pyc,, +pip/_vendor/requests/packages/charade/langgreekmodel.pyc,, +pip/_vendor/requests/packages/charade/chardistribution.pyc,, +pip/_vendor/requests/packages/chardet/mbcsgroupprober.pyc,, +pip/_vendor/html5lib/filters/__init__.pyc,, +pip/baseparser.pyc,, +pip/_vendor/requests/packages/charade/universaldetector.pyc,, +pip/_vendor/requests/models.pyc,, +pip/_vendor/requests/packages/charade/mbcssm.pyc,, +pip/_vendor/requests/packages/charade/compat.pyc,, +pip/status_codes.pyc,, +pip/_vendor/distlib/__init__.pyc,, +pip/_vendor/requests/packages/charade/charsetprober.pyc,, +pip/commands/search.pyc,, +pip/_vendor/requests/packages/chardet/langhungarianmodel.pyc,, +pip/_vendor/requests/packages/charade/cp949prober.pyc,, +pip/_vendor/html5lib/utils.pyc,, +pip/_vendor/requests/packages/charade/mbcsgroupprober.pyc,, +pip/_vendor/html5lib/trie/datrie.pyc,, +pip/_vendor/requests/structures.pyc,, +pip/_vendor/requests/packages/urllib3/util.pyc,, +pip/_vendor/requests/packages/chardet/euckrfreq.pyc,, +pip/_vendor/requests/packages/urllib3/fields.pyc,, +pip/_vendor/requests/packages/urllib3/contrib/ntlmpool.pyc,, +pip/vcs/__init__.pyc,, +pip/_vendor/html5lib/constants.pyc,, +pip/_vendor/html5lib/trie/py.pyc,, +pip/download.pyc,, +pip/commands/unzip.pyc,, +pip/_vendor/requests/packages/charade/utf8prober.pyc,, +pip/commands/__init__.pyc,, +pip/_vendor/requests/packages/urllib3/__init__.pyc,, +pip/_vendor/requests/packages/charade/sbcharsetprober.pyc,, +pip/_vendor/distlib/compat.pyc,, +pip/_vendor/distlib/wheel.pyc,, +pip/_vendor/requests/packages/chardet/euctwprober.pyc,, +pip/_vendor/requests/packages/chardet/escsm.pyc,, +pip/_vendor/requests/status_codes.pyc,, +pip/_vendor/requests/exceptions.pyc,, +pip/_vendor/distlib/markers.pyc,, +pip/index.pyc,, +pip/_vendor/requests/packages/charade/charsetgroupprober.pyc,, +pip/_vendor/requests/packages/chardet/cp949prober.pyc,, +pip/_vendor/requests/api.pyc,, +pip/_vendor/requests/packages/urllib3/filepost.pyc,, +pip/_vendor/requests/packages/chardet/big5freq.pyc,, +pip/_vendor/html5lib/treebuilders/etree.pyc,, +pip/_vendor/html5lib/treebuilders/dom.pyc,, +pip/_vendor/requests/packages/charade/codingstatemachine.pyc,, +pip/_vendor/requests/packages/chardet/mbcssm.pyc,, +pip/_vendor/requests/packages/charade/escprober.pyc,, +pip/_vendor/distlib/_backport/shutil.pyc,, +pip/_vendor/requests/packages/chardet/sbcsgroupprober.pyc,, +pip/_vendor/requests/packages/charade/langhebrewmodel.pyc,, +pip/__main__.pyc,, +pip/backwardcompat/__init__.pyc,, +pip/_vendor/html5lib/tokenizer.pyc,, +pip/_vendor/requests/packages/charade/gb2312freq.pyc,, +pip/_vendor/requests/packages/chardet/utf8prober.pyc,, +pip/_vendor/requests/packages/chardet/langhebrewmodel.pyc,, +pip/_vendor/requests/packages/chardet/compat.pyc,, +pip/commands/install.pyc,, +pip/_vendor/six.pyc,, +pip/_vendor/requests/packages/chardet/langcyrillicmodel.pyc,, +pip/_vendor/requests/packages/chardet/constants.pyc,, +pip/_vendor/requests/packages/charade/constants.pyc,, +pip/_vendor/requests/packages/urllib3/packages/ordered_dict.pyc,, +pip/_vendor/requests/packages/chardet/eucjpprober.pyc,, +pip/_vendor/html5lib/treewalkers/etree.pyc,, +pip/_vendor/requests/packages/charade/big5freq.pyc,, +pip/_vendor/requests/packages/charade/sjisprober.pyc,, +pip/_vendor/requests/hooks.pyc,, +pip/_vendor/requests/packages/charade/latin1prober.pyc,, +pip/_vendor/requests/packages/urllib3/connectionpool.pyc,, +pip/_vendor/requests/packages/chardet/mbcharsetprober.pyc,, +pip/locations.pyc,, +pip/_vendor/requests/packages/chardet/jisfreq.pyc,, +pip/_vendor/requests/packages/charade/jisfreq.pyc,, +pip/vcs/subversion.pyc,, +pip/exceptions.pyc,, +pip/basecommand.pyc,, +pip/_vendor/distlib/locators.pyc,, +pip/_vendor/requests/packages/charade/eucjpprober.pyc,, +pip/_vendor/html5lib/filters/_base.pyc,, +pip/_vendor/re-vendor.pyc,, +pip/_vendor/requests/packages/charade/langbulgarianmodel.pyc,, +pip/_vendor/html5lib/treewalkers/dom.pyc,, +pip/_vendor/requests/packages/urllib3/contrib/__init__.pyc,, +pip/_vendor/requests/packages/chardet/euctwfreq.pyc,, +pip/_vendor/requests/packages/chardet/chardistribution.pyc,, +pip/_vendor/requests/packages/charade/hebrewprober.pyc,, +pip/_vendor/requests/packages/chardet/langgreekmodel.pyc,, +pip/_vendor/requests/packages/chardet/sbcharsetprober.pyc,, +pip/_vendor/colorama/ansitowin32.pyc,, +pip/_vendor/requests/packages/chardet/sjisprober.pyc,, +pip/log.pyc,, +pip/_vendor/requests/packages/charade/langcyrillicmodel.pyc,, +pip/_vendor/_markerlib/markers.pyc,, +pip/_vendor/requests/cookies.pyc,, +pip/_vendor/requests/packages/urllib3/_collections.pyc,, +pip/_vendor/requests/packages/charade/langhungarianmodel.pyc,, +pip/_vendor/pkg_resources.pyc,, +pip/_vendor/distlib/manifest.pyc,, +pip/_vendor/html5lib/serializer/htmlserializer.pyc,, +pip/_vendor/html5lib/serializer/__init__.pyc,, +pip/_vendor/html5lib/trie/_base.pyc,, +pip/_vendor/requests/packages/urllib3/response.pyc,, +pip/_vendor/html5lib/filters/inject_meta_charset.pyc,, +pip/_vendor/requests/packages/chardet/charsetprober.pyc,, +pip/vcs/mercurial.pyc,, +pip/_vendor/requests/packages/charade/euckrfreq.pyc,, +pip/commands/bundle.pyc,, +pip/_vendor/requests/packages/urllib3/exceptions.pyc,, +pip/_vendor/__init__.pyc,, +pip/_vendor/requests/packages/chardet/universaldetector.pyc,, +pip/_vendor/html5lib/treewalkers/lxmletree.pyc,, +pip/_vendor/colorama/ansi.pyc,, +pip/_vendor/requests/packages/chardet/gb2312prober.pyc,, +pip/_vendor/requests/packages/urllib3/contrib/pyopenssl.pyc,, +pip/wheel.pyc,, +pip/req.pyc,, +pip/_vendor/requests/__init__.pyc,, +pip/_vendor/requests/packages/chardet/chardetect.pyc,, +pip/_vendor/html5lib/filters/optionaltags.pyc,, +pip/_vendor/requests/packages/urllib3/connection.pyc,, +pip/_vendor/colorama/winterm.pyc,, diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip-1.5.2.dist-info/WHEEL b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip-1.5.2.dist-info/WHEEL new file mode 100644 index 0000000..f2ddc35 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip-1.5.2.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.22.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip-1.5.2.dist-info/entry_points.txt b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip-1.5.2.dist-info/entry_points.txt new file mode 100644 index 0000000..4416a2f --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip-1.5.2.dist-info/entry_points.txt @@ -0,0 +1,5 @@ +[console_scripts] +pip = pip:main +pip2.7 = pip:main +pip2 = pip:main + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip-1.5.2.dist-info/pydist.json b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip-1.5.2.dist-info/pydist.json new file mode 100644 index 0000000..8bdb349 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip-1.5.2.dist-info/pydist.json @@ -0,0 +1 @@ +{"license": "MIT", "exports": {"console_scripts": {"pip": "pip:main", "pip2.7": "pip:main", "pip2": "pip:main"}}, "document_names": {"description": "DESCRIPTION.rst"}, "name": "pip", "metadata_version": "2.0", "contacts": [{"role": "author", "email": "python-virtualenv@groups.google.com", "name": "The pip developers"}], "generator": "bdist_wheel (0.22.0)", "commands": {"wrap_console": {"pip": "pip:main", "pip2.7": "pip:main", "pip2": "pip:main"}}, "test_requires": [{"requires": ["pytest", "virtualenv (>=1.10)", "scripttest (>=1.3)", "mock"]}], "summary": "A tool for installing and managing Python packages.", "project_urls": {"Home": "http://www.pip-installer.org"}, "run_requires": [{"requires": ["pytest", "virtualenv (>=1.10)", "scripttest (>=1.3)", "mock"], "extra": "testing"}], "version": "1.5.2", "keywords": "easy_install distutils setuptools egg virtualenv", "classifiers": ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Topic :: Software Development :: Build Tools", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.1", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3"], "extras": ["testing"]} \ No newline at end of file diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip-1.5.2.dist-info/top_level.txt b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip-1.5.2.dist-info/top_level.txt new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip-1.5.2.dist-info/top_level.txt @@ -0,0 +1 @@ +pip diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/__init__.py new file mode 100644 index 0000000..e169fa8 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/__init__.py @@ -0,0 +1,277 @@ +#!/usr/bin/env python +import os +import optparse + +import sys +import re + +from pip.exceptions import InstallationError, CommandError, PipError +from pip.log import logger +from pip.util import get_installed_distributions, get_prog +from pip.vcs import git, mercurial, subversion, bazaar # noqa +from pip.baseparser import ConfigOptionParser, UpdatingDefaultsHelpFormatter +from pip.commands import commands, get_summaries, get_similar_commands + +# This fixes a peculiarity when importing via __import__ - as we are +# initialising the pip module, "from pip import cmdoptions" is recursive +# and appears not to work properly in that situation. +import pip.cmdoptions +cmdoptions = pip.cmdoptions + +# The version as used in the setup.py and the docs conf.py +__version__ = "1.5.2" + + +def autocomplete(): + """Command and option completion for the main option parser (and options) + and its subcommands (and options). + + Enable by sourcing one of the completion shell scripts (bash or zsh). + """ + # Don't complete if user hasn't sourced bash_completion file. + if 'PIP_AUTO_COMPLETE' not in os.environ: + return + cwords = os.environ['COMP_WORDS'].split()[1:] + cword = int(os.environ['COMP_CWORD']) + try: + current = cwords[cword - 1] + except IndexError: + current = '' + + subcommands = [cmd for cmd, summary in get_summaries()] + options = [] + # subcommand + try: + subcommand_name = [w for w in cwords if w in subcommands][0] + except IndexError: + subcommand_name = None + + parser = create_main_parser() + # subcommand options + if subcommand_name: + # special case: 'help' subcommand has no options + if subcommand_name == 'help': + sys.exit(1) + # special case: list locally installed dists for uninstall command + if subcommand_name == 'uninstall' and not current.startswith('-'): + installed = [] + lc = current.lower() + for dist in get_installed_distributions(local_only=True): + if dist.key.startswith(lc) and dist.key not in cwords[1:]: + installed.append(dist.key) + # if there are no dists installed, fall back to option completion + if installed: + for dist in installed: + print(dist) + sys.exit(1) + + subcommand = commands[subcommand_name]() + options += [(opt.get_opt_string(), opt.nargs) + for opt in subcommand.parser.option_list_all + if opt.help != optparse.SUPPRESS_HELP] + + # filter out previously specified options from available options + prev_opts = [x.split('=')[0] for x in cwords[1:cword - 1]] + options = [(x, v) for (x, v) in options if x not in prev_opts] + # filter options by current input + options = [(k, v) for k, v in options if k.startswith(current)] + for option in options: + opt_label = option[0] + # append '=' to options which require args + if option[1]: + opt_label += '=' + print(opt_label) + else: + # show main parser options only when necessary + if current.startswith('-') or current.startswith('--'): + opts = [i.option_list for i in parser.option_groups] + opts.append(parser.option_list) + opts = (o for it in opts for o in it) + + subcommands += [i.get_opt_string() for i in opts + if i.help != optparse.SUPPRESS_HELP] + + print(' '.join([x for x in subcommands if x.startswith(current)])) + sys.exit(1) + + +def create_main_parser(): + parser_kw = { + 'usage': '\n%prog [options]', + 'add_help_option': False, + 'formatter': UpdatingDefaultsHelpFormatter(), + 'name': 'global', + 'prog': get_prog(), + } + + parser = ConfigOptionParser(**parser_kw) + parser.disable_interspersed_args() + + pip_pkg_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + parser.version = 'pip %s from %s (python %s)' % ( + __version__, pip_pkg_dir, sys.version[:3]) + + # add the general options + gen_opts = cmdoptions.make_option_group(cmdoptions.general_group, parser) + parser.add_option_group(gen_opts) + + parser.main = True # so the help formatter knows + + # create command listing for description + command_summaries = get_summaries() + description = [''] + ['%-27s %s' % (i, j) for i, j in command_summaries] + parser.description = '\n'.join(description) + + return parser + + +def parseopts(args): + parser = create_main_parser() + + # Note: parser calls disable_interspersed_args(), so the result of this call + # is to split the initial args into the general options before the + # subcommand and everything else. + # For example: + # args: ['--timeout=5', 'install', '--user', 'INITools'] + # general_options: ['--timeout==5'] + # args_else: ['install', '--user', 'INITools'] + general_options, args_else = parser.parse_args(args) + + # --version + if general_options.version: + sys.stdout.write(parser.version) + sys.stdout.write(os.linesep) + sys.exit() + + # pip || pip help -> print_help() + if not args_else or (args_else[0] == 'help' and len(args_else) == 1): + parser.print_help() + sys.exit() + + # the subcommand name + cmd_name = args_else[0].lower() + + #all the args without the subcommand + cmd_args = args[:] + cmd_args.remove(args_else[0].lower()) + + if cmd_name not in commands: + guess = get_similar_commands(cmd_name) + + msg = ['unknown command "%s"' % cmd_name] + if guess: + msg.append('maybe you meant "%s"' % guess) + + raise CommandError(' - '.join(msg)) + + return cmd_name, cmd_args + + +def main(initial_args=None): + if initial_args is None: + initial_args = sys.argv[1:] + + autocomplete() + + try: + cmd_name, cmd_args = parseopts(initial_args) + except PipError: + e = sys.exc_info()[1] + sys.stderr.write("ERROR: %s" % e) + sys.stderr.write(os.linesep) + sys.exit(1) + + command = commands[cmd_name]() + return command.main(cmd_args) + + +def bootstrap(): + """ + Bootstrapping function to be called from install-pip.py script. + """ + pkgs = ['pip'] + try: + import setuptools + except ImportError: + pkgs.append('setuptools') + return main(['install', '--upgrade'] + pkgs + sys.argv[1:]) + +############################################################ +## Writing freeze files + + +class FrozenRequirement(object): + + def __init__(self, name, req, editable, comments=()): + self.name = name + self.req = req + self.editable = editable + self.comments = comments + + _rev_re = re.compile(r'-r(\d+)$') + _date_re = re.compile(r'-(20\d\d\d\d\d\d)$') + + @classmethod + def from_dist(cls, dist, dependency_links, find_tags=False): + location = os.path.normcase(os.path.abspath(dist.location)) + comments = [] + from pip.vcs import vcs, get_src_requirement + if vcs.get_backend_name(location): + editable = True + try: + req = get_src_requirement(dist, location, find_tags) + except InstallationError: + ex = sys.exc_info()[1] + logger.warn("Error when trying to get requirement for VCS system %s, falling back to uneditable format" % ex) + req = None + if req is None: + logger.warn('Could not determine repository location of %s' % location) + comments.append('## !! Could not determine repository location') + req = dist.as_requirement() + editable = False + else: + editable = False + req = dist.as_requirement() + specs = req.specs + assert len(specs) == 1 and specs[0][0] == '==' + version = specs[0][1] + ver_match = cls._rev_re.search(version) + date_match = cls._date_re.search(version) + if ver_match or date_match: + svn_backend = vcs.get_backend('svn') + if svn_backend: + svn_location = svn_backend( + ).get_location(dist, dependency_links) + if not svn_location: + logger.warn( + 'Warning: cannot find svn location for %s' % req) + comments.append('## FIXME: could not find svn URL in dependency_links for this package:') + else: + comments.append('# Installing as editable to satisfy requirement %s:' % req) + if ver_match: + rev = ver_match.group(1) + else: + rev = '{%s}' % date_match.group(1) + editable = True + req = '%s@%s#egg=%s' % (svn_location, rev, cls.egg_name(dist)) + return cls(dist.project_name, req, editable, comments) + + @staticmethod + def egg_name(dist): + name = dist.egg_name() + match = re.search(r'-py\d\.\d$', name) + if match: + name = name[:match.start()] + return name + + def __str__(self): + req = self.req + if self.editable: + req = '-e %s' % req + return '\n'.join(list(self.comments) + [str(req)]) + '\n' + + +if __name__ == '__main__': + exit = main() + if exit: + sys.exit(exit) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/__main__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/__main__.py new file mode 100644 index 0000000..5ca3746 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/__main__.py @@ -0,0 +1,7 @@ +import sys +from .runner import run + +if __name__ == '__main__': + exit = run() + if exit: + sys.exit(exit) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/__init__.py new file mode 100644 index 0000000..f233ca0 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/__init__.py @@ -0,0 +1,8 @@ +""" +pip._vendor is for vendoring dependencies of pip to prevent needing pip to +depend on something external. + +Files inside of pip._vendor should be considered immutable and should only be +updated to versions from upstream. +""" +from __future__ import absolute_import diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/_markerlib/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/_markerlib/__init__.py new file mode 100644 index 0000000..197781a --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/_markerlib/__init__.py @@ -0,0 +1,16 @@ +try: + import ast + from pip._vendor._markerlib.markers import default_environment, compile, interpret +except ImportError: + if 'ast' in globals(): + raise + def default_environment(): + return {} + def compile(marker): + def marker_fn(environment=None, override=None): + # 'empty markers are True' heuristic won't install extra deps. + return not marker.strip() + marker_fn.__doc__ = marker + return marker_fn + def interpret(marker, environment=None, override=None): + return compile(marker)() diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/_markerlib/markers.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/_markerlib/markers.py new file mode 100644 index 0000000..fa83706 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/_markerlib/markers.py @@ -0,0 +1,119 @@ +# -*- coding: utf-8 -*- +"""Interpret PEP 345 environment markers. + +EXPR [in|==|!=|not in] EXPR [or|and] ... + +where EXPR belongs to any of those: + + python_version = '%s.%s' % (sys.version_info[0], sys.version_info[1]) + python_full_version = sys.version.split()[0] + os.name = os.name + sys.platform = sys.platform + platform.version = platform.version() + platform.machine = platform.machine() + platform.python_implementation = platform.python_implementation() + a free string, like '2.6', or 'win32' +""" + +__all__ = ['default_environment', 'compile', 'interpret'] + +import ast +import os +import platform +import sys +import weakref + +_builtin_compile = compile + +try: + from platform import python_implementation +except ImportError: + if os.name == "java": + # Jython 2.5 has ast module, but not platform.python_implementation() function. + def python_implementation(): + return "Jython" + else: + raise + + +# restricted set of variables +_VARS = {'sys.platform': sys.platform, + 'python_version': '%s.%s' % sys.version_info[:2], + # FIXME parsing sys.platform is not reliable, but there is no other + # way to get e.g. 2.7.2+, and the PEP is defined with sys.version + 'python_full_version': sys.version.split(' ', 1)[0], + 'os.name': os.name, + 'platform.version': platform.version(), + 'platform.machine': platform.machine(), + 'platform.python_implementation': python_implementation(), + 'extra': None # wheel extension + } + +for var in list(_VARS.keys()): + if '.' in var: + _VARS[var.replace('.', '_')] = _VARS[var] + +def default_environment(): + """Return copy of default PEP 385 globals dictionary.""" + return dict(_VARS) + +class ASTWhitelist(ast.NodeTransformer): + def __init__(self, statement): + self.statement = statement # for error messages + + ALLOWED = (ast.Compare, ast.BoolOp, ast.Attribute, ast.Name, ast.Load, ast.Str) + # Bool operations + ALLOWED += (ast.And, ast.Or) + # Comparison operations + ALLOWED += (ast.Eq, ast.Gt, ast.GtE, ast.In, ast.Is, ast.IsNot, ast.Lt, ast.LtE, ast.NotEq, ast.NotIn) + + def visit(self, node): + """Ensure statement only contains allowed nodes.""" + if not isinstance(node, self.ALLOWED): + raise SyntaxError('Not allowed in environment markers.\n%s\n%s' % + (self.statement, + (' ' * node.col_offset) + '^')) + return ast.NodeTransformer.visit(self, node) + + def visit_Attribute(self, node): + """Flatten one level of attribute access.""" + new_node = ast.Name("%s.%s" % (node.value.id, node.attr), node.ctx) + return ast.copy_location(new_node, node) + +def parse_marker(marker): + tree = ast.parse(marker, mode='eval') + new_tree = ASTWhitelist(marker).generic_visit(tree) + return new_tree + +def compile_marker(parsed_marker): + return _builtin_compile(parsed_marker, '', 'eval', + dont_inherit=True) + +_cache = weakref.WeakValueDictionary() + +def compile(marker): + """Return compiled marker as a function accepting an environment dict.""" + try: + return _cache[marker] + except KeyError: + pass + if not marker.strip(): + def marker_fn(environment=None, override=None): + """""" + return True + else: + compiled_marker = compile_marker(parse_marker(marker)) + def marker_fn(environment=None, override=None): + """override updates environment""" + if override is None: + override = {} + if environment is None: + environment = default_environment() + environment.update(override) + return eval(compiled_marker, environment) + marker_fn.__doc__ = marker + _cache[marker] = marker_fn + return _cache[marker] + +def interpret(marker, environment=None): + return compile(marker)(environment) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/colorama/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/colorama/__init__.py new file mode 100644 index 0000000..2d127fa --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/colorama/__init__.py @@ -0,0 +1,7 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from .initialise import init, deinit, reinit +from .ansi import Fore, Back, Style +from .ansitowin32 import AnsiToWin32 + +VERSION = '0.2.7' + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/colorama/ansi.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/colorama/ansi.py new file mode 100644 index 0000000..fef4057 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/colorama/ansi.py @@ -0,0 +1,50 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +''' +This module generates ANSI character codes to printing colors to terminals. +See: http://en.wikipedia.org/wiki/ANSI_escape_code +''' + +CSI = '\033[' + +def code_to_chars(code): + return CSI + str(code) + 'm' + +class AnsiCodes(object): + def __init__(self, codes): + for name in dir(codes): + if not name.startswith('_'): + value = getattr(codes, name) + setattr(self, name, code_to_chars(value)) + +class AnsiFore: + BLACK = 30 + RED = 31 + GREEN = 32 + YELLOW = 33 + BLUE = 34 + MAGENTA = 35 + CYAN = 36 + WHITE = 37 + RESET = 39 + +class AnsiBack: + BLACK = 40 + RED = 41 + GREEN = 42 + YELLOW = 43 + BLUE = 44 + MAGENTA = 45 + CYAN = 46 + WHITE = 47 + RESET = 49 + +class AnsiStyle: + BRIGHT = 1 + DIM = 2 + NORMAL = 22 + RESET_ALL = 0 + +Fore = AnsiCodes( AnsiFore ) +Back = AnsiCodes( AnsiBack ) +Style = AnsiCodes( AnsiStyle ) + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/colorama/ansitowin32.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/colorama/ansitowin32.py new file mode 100644 index 0000000..d290601 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/colorama/ansitowin32.py @@ -0,0 +1,189 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import re +import sys + +from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style +from .winterm import WinTerm, WinColor, WinStyle +from .win32 import windll + + +if windll is not None: + winterm = WinTerm() + + +def is_a_tty(stream): + return hasattr(stream, 'isatty') and stream.isatty() + + +class StreamWrapper(object): + ''' + Wraps a stream (such as stdout), acting as a transparent proxy for all + attribute access apart from method 'write()', which is delegated to our + Converter instance. + ''' + def __init__(self, wrapped, converter): + # double-underscore everything to prevent clashes with names of + # attributes on the wrapped stream object. + self.__wrapped = wrapped + self.__convertor = converter + + def __getattr__(self, name): + return getattr(self.__wrapped, name) + + def write(self, text): + self.__convertor.write(text) + + +class AnsiToWin32(object): + ''' + Implements a 'write()' method which, on Windows, will strip ANSI character + sequences from the text, and if outputting to a tty, will convert them into + win32 function calls. + ''' + ANSI_RE = re.compile('\033\[((?:\d|;)*)([a-zA-Z])') + + def __init__(self, wrapped, convert=None, strip=None, autoreset=False): + # The wrapped stream (normally sys.stdout or sys.stderr) + self.wrapped = wrapped + + # should we reset colors to defaults after every .write() + self.autoreset = autoreset + + # create the proxy wrapping our output stream + self.stream = StreamWrapper(wrapped, self) + + on_windows = sys.platform.startswith('win') + + # should we strip ANSI sequences from our output? + if strip is None: + strip = on_windows + self.strip = strip + + # should we should convert ANSI sequences into win32 calls? + if convert is None: + convert = on_windows and is_a_tty(wrapped) + self.convert = convert + + # dict of ansi codes to win32 functions and parameters + self.win32_calls = self.get_win32_calls() + + # are we wrapping stderr? + self.on_stderr = self.wrapped is sys.stderr + + + def should_wrap(self): + ''' + True if this class is actually needed. If false, then the output + stream will not be affected, nor will win32 calls be issued, so + wrapping stdout is not actually required. This will generally be + False on non-Windows platforms, unless optional functionality like + autoreset has been requested using kwargs to init() + ''' + return self.convert or self.strip or self.autoreset + + + def get_win32_calls(self): + if self.convert and winterm: + return { + AnsiStyle.RESET_ALL: (winterm.reset_all, ), + AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT), + AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL), + AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL), + AnsiFore.BLACK: (winterm.fore, WinColor.BLACK), + AnsiFore.RED: (winterm.fore, WinColor.RED), + AnsiFore.GREEN: (winterm.fore, WinColor.GREEN), + AnsiFore.YELLOW: (winterm.fore, WinColor.YELLOW), + AnsiFore.BLUE: (winterm.fore, WinColor.BLUE), + AnsiFore.MAGENTA: (winterm.fore, WinColor.MAGENTA), + AnsiFore.CYAN: (winterm.fore, WinColor.CYAN), + AnsiFore.WHITE: (winterm.fore, WinColor.GREY), + AnsiFore.RESET: (winterm.fore, ), + AnsiBack.BLACK: (winterm.back, WinColor.BLACK), + AnsiBack.RED: (winterm.back, WinColor.RED), + AnsiBack.GREEN: (winterm.back, WinColor.GREEN), + AnsiBack.YELLOW: (winterm.back, WinColor.YELLOW), + AnsiBack.BLUE: (winterm.back, WinColor.BLUE), + AnsiBack.MAGENTA: (winterm.back, WinColor.MAGENTA), + AnsiBack.CYAN: (winterm.back, WinColor.CYAN), + AnsiBack.WHITE: (winterm.back, WinColor.GREY), + AnsiBack.RESET: (winterm.back, ), + } + + + def write(self, text): + if self.strip or self.convert: + self.write_and_convert(text) + else: + self.wrapped.write(text) + self.wrapped.flush() + if self.autoreset: + self.reset_all() + + + def reset_all(self): + if self.convert: + self.call_win32('m', (0,)) + elif is_a_tty(self.wrapped): + self.wrapped.write(Style.RESET_ALL) + + + def write_and_convert(self, text): + ''' + Write the given text to our wrapped stream, stripping any ANSI + sequences from the text, and optionally converting them into win32 + calls. + ''' + cursor = 0 + for match in self.ANSI_RE.finditer(text): + start, end = match.span() + self.write_plain_text(text, cursor, start) + self.convert_ansi(*match.groups()) + cursor = end + self.write_plain_text(text, cursor, len(text)) + + + def write_plain_text(self, text, start, end): + if start < end: + self.wrapped.write(text[start:end]) + self.wrapped.flush() + + + def convert_ansi(self, paramstring, command): + if self.convert: + params = self.extract_params(paramstring) + self.call_win32(command, params) + + + def extract_params(self, paramstring): + def split(paramstring): + for p in paramstring.split(';'): + if p != '': + yield int(p) + return tuple(split(paramstring)) + + + def call_win32(self, command, params): + if params == []: + params = [0] + if command == 'm': + for param in params: + if param in self.win32_calls: + func_args = self.win32_calls[param] + func = func_args[0] + args = func_args[1:] + kwargs = dict(on_stderr=self.on_stderr) + func(*args, **kwargs) + elif command in ('H', 'f'): # set cursor position + func = winterm.set_cursor_position + func(params, on_stderr=self.on_stderr) + elif command in ('J'): + func = winterm.erase_data + func(params, on_stderr=self.on_stderr) + elif command == 'A': + if params == () or params == None: + num_rows = 1 + else: + num_rows = params[0] + func = winterm.cursor_up + func(num_rows, on_stderr=self.on_stderr) + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/colorama/initialise.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/colorama/initialise.py new file mode 100644 index 0000000..7c8e776 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/colorama/initialise.py @@ -0,0 +1,56 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import atexit +import sys + +from .ansitowin32 import AnsiToWin32 + + +orig_stdout = sys.stdout +orig_stderr = sys.stderr + +wrapped_stdout = sys.stdout +wrapped_stderr = sys.stderr + +atexit_done = False + + +def reset_all(): + AnsiToWin32(orig_stdout).reset_all() + + +def init(autoreset=False, convert=None, strip=None, wrap=True): + + if not wrap and any([autoreset, convert, strip]): + raise ValueError('wrap=False conflicts with any other arg=True') + + global wrapped_stdout, wrapped_stderr + sys.stdout = wrapped_stdout = \ + wrap_stream(orig_stdout, convert, strip, autoreset, wrap) + sys.stderr = wrapped_stderr = \ + wrap_stream(orig_stderr, convert, strip, autoreset, wrap) + + global atexit_done + if not atexit_done: + atexit.register(reset_all) + atexit_done = True + + +def deinit(): + sys.stdout = orig_stdout + sys.stderr = orig_stderr + + +def reinit(): + sys.stdout = wrapped_stdout + sys.stderr = wrapped_stdout + + +def wrap_stream(stream, convert, strip, autoreset, wrap): + if wrap: + wrapper = AnsiToWin32(stream, + convert=convert, strip=strip, autoreset=autoreset) + if wrapper.should_wrap(): + stream = wrapper.stream + return stream + + diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/colorama/win32.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/colorama/win32.py new file mode 100644 index 0000000..f4024f9 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/colorama/win32.py @@ -0,0 +1,134 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. + +# from winbase.h +STDOUT = -11 +STDERR = -12 + +try: + from ctypes import windll + from ctypes import wintypes +except ImportError: + windll = None + SetConsoleTextAttribute = lambda *_: None +else: + from ctypes import ( + byref, Structure, c_char, c_short, c_uint32, c_ushort, POINTER + ) + + class CONSOLE_SCREEN_BUFFER_INFO(Structure): + """struct in wincon.h.""" + _fields_ = [ + ("dwSize", wintypes._COORD), + ("dwCursorPosition", wintypes._COORD), + ("wAttributes", wintypes.WORD), + ("srWindow", wintypes.SMALL_RECT), + ("dwMaximumWindowSize", wintypes._COORD), + ] + def __str__(self): + return '(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)' % ( + self.dwSize.Y, self.dwSize.X + , self.dwCursorPosition.Y, self.dwCursorPosition.X + , self.wAttributes + , self.srWindow.Top, self.srWindow.Left, self.srWindow.Bottom, self.srWindow.Right + , self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X + ) + + _GetStdHandle = windll.kernel32.GetStdHandle + _GetStdHandle.argtypes = [ + wintypes.DWORD, + ] + _GetStdHandle.restype = wintypes.HANDLE + + _GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo + _GetConsoleScreenBufferInfo.argtypes = [ + wintypes.HANDLE, + POINTER(CONSOLE_SCREEN_BUFFER_INFO), + ] + _GetConsoleScreenBufferInfo.restype = wintypes.BOOL + + _SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute + _SetConsoleTextAttribute.argtypes = [ + wintypes.HANDLE, + wintypes.WORD, + ] + _SetConsoleTextAttribute.restype = wintypes.BOOL + + _SetConsoleCursorPosition = windll.kernel32.SetConsoleCursorPosition + _SetConsoleCursorPosition.argtypes = [ + wintypes.HANDLE, + wintypes._COORD, + ] + _SetConsoleCursorPosition.restype = wintypes.BOOL + + _FillConsoleOutputCharacterA = windll.kernel32.FillConsoleOutputCharacterA + _FillConsoleOutputCharacterA.argtypes = [ + wintypes.HANDLE, + c_char, + wintypes.DWORD, + wintypes._COORD, + POINTER(wintypes.DWORD), + ] + _FillConsoleOutputCharacterA.restype = wintypes.BOOL + + _FillConsoleOutputAttribute = windll.kernel32.FillConsoleOutputAttribute + _FillConsoleOutputAttribute.argtypes = [ + wintypes.HANDLE, + wintypes.WORD, + wintypes.DWORD, + wintypes._COORD, + POINTER(wintypes.DWORD), + ] + _FillConsoleOutputAttribute.restype = wintypes.BOOL + + handles = { + STDOUT: _GetStdHandle(STDOUT), + STDERR: _GetStdHandle(STDERR), + } + + def GetConsoleScreenBufferInfo(stream_id=STDOUT): + handle = handles[stream_id] + csbi = CONSOLE_SCREEN_BUFFER_INFO() + success = _GetConsoleScreenBufferInfo( + handle, byref(csbi)) + return csbi + + def SetConsoleTextAttribute(stream_id, attrs): + handle = handles[stream_id] + return _SetConsoleTextAttribute(handle, attrs) + + def SetConsoleCursorPosition(stream_id, position): + position = wintypes._COORD(*position) + # If the position is out of range, do nothing. + if position.Y <= 0 or position.X <= 0: + return + # Adjust for Windows' SetConsoleCursorPosition: + # 1. being 0-based, while ANSI is 1-based. + # 2. expecting (x,y), while ANSI uses (y,x). + adjusted_position = wintypes._COORD(position.Y - 1, position.X - 1) + # Adjust for viewport's scroll position + sr = GetConsoleScreenBufferInfo(STDOUT).srWindow + adjusted_position.Y += sr.Top + adjusted_position.X += sr.Left + # Resume normal processing + handle = handles[stream_id] + return _SetConsoleCursorPosition(handle, adjusted_position) + + def FillConsoleOutputCharacter(stream_id, char, length, start): + handle = handles[stream_id] + char = c_char(char) + length = wintypes.DWORD(length) + num_written = wintypes.DWORD(0) + # Note that this is hard-coded for ANSI (vs wide) bytes. + success = _FillConsoleOutputCharacterA( + handle, char, length, start, byref(num_written)) + return num_written.value + + def FillConsoleOutputAttribute(stream_id, attr, length, start): + ''' FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten )''' + handle = handles[stream_id] + attribute = wintypes.WORD(attr) + length = wintypes.DWORD(length) + num_written = wintypes.DWORD(0) + # Note that this is hard-coded for ANSI (vs wide) bytes. + return _FillConsoleOutputAttribute( + handle, attribute, length, start, byref(num_written)) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/colorama/winterm.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/colorama/winterm.py new file mode 100644 index 0000000..4a711fd --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/colorama/winterm.py @@ -0,0 +1,120 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from . import win32 + + +# from wincon.h +class WinColor(object): + BLACK = 0 + BLUE = 1 + GREEN = 2 + CYAN = 3 + RED = 4 + MAGENTA = 5 + YELLOW = 6 + GREY = 7 + +# from wincon.h +class WinStyle(object): + NORMAL = 0x00 # dim text, dim background + BRIGHT = 0x08 # bright text, dim background + + +class WinTerm(object): + + def __init__(self): + self._default = win32.GetConsoleScreenBufferInfo(win32.STDOUT).wAttributes + self.set_attrs(self._default) + self._default_fore = self._fore + self._default_back = self._back + self._default_style = self._style + + def get_attrs(self): + return self._fore + self._back * 16 + self._style + + def set_attrs(self, value): + self._fore = value & 7 + self._back = (value >> 4) & 7 + self._style = value & WinStyle.BRIGHT + + def reset_all(self, on_stderr=None): + self.set_attrs(self._default) + self.set_console(attrs=self._default) + + def fore(self, fore=None, on_stderr=False): + if fore is None: + fore = self._default_fore + self._fore = fore + self.set_console(on_stderr=on_stderr) + + def back(self, back=None, on_stderr=False): + if back is None: + back = self._default_back + self._back = back + self.set_console(on_stderr=on_stderr) + + def style(self, style=None, on_stderr=False): + if style is None: + style = self._default_style + self._style = style + self.set_console(on_stderr=on_stderr) + + def set_console(self, attrs=None, on_stderr=False): + if attrs is None: + attrs = self.get_attrs() + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + win32.SetConsoleTextAttribute(handle, attrs) + + def get_position(self, handle): + position = win32.GetConsoleScreenBufferInfo(handle).dwCursorPosition + # Because Windows coordinates are 0-based, + # and win32.SetConsoleCursorPosition expects 1-based. + position.X += 1 + position.Y += 1 + return position + + def set_cursor_position(self, position=None, on_stderr=False): + if position is None: + #I'm not currently tracking the position, so there is no default. + #position = self.get_position() + return + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + win32.SetConsoleCursorPosition(handle, position) + + def cursor_up(self, num_rows=0, on_stderr=False): + if num_rows == 0: + return + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + position = self.get_position(handle) + adjusted_position = (position.Y - num_rows, position.X) + self.set_cursor_position(adjusted_position, on_stderr) + + def erase_data(self, mode=0, on_stderr=False): + # 0 (or None) should clear from the cursor to the end of the screen. + # 1 should clear from the cursor to the beginning of the screen. + # 2 should clear the entire screen. (And maybe move cursor to (1,1)?) + # + # At the moment, I only support mode 2. From looking at the API, it + # should be possible to calculate a different number of bytes to clear, + # and to do so relative to the cursor position. + if mode[0] not in (2,): + return + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + # here's where we'll home the cursor + coord_screen = win32.COORD(0,0) + csbi = win32.GetConsoleScreenBufferInfo(handle) + # get the number of character cells in the current buffer + dw_con_size = csbi.dwSize.X * csbi.dwSize.Y + # fill the entire screen with blanks + win32.FillConsoleOutputCharacter(handle, ' ', dw_con_size, coord_screen) + # now set the buffer's attributes accordingly + win32.FillConsoleOutputAttribute(handle, self.get_attrs(), dw_con_size, coord_screen ); + # put the cursor at (0, 0) + win32.SetConsoleCursorPosition(handle, (coord_screen.X, coord_screen.Y)) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/__init__.py new file mode 100644 index 0000000..56a56ba --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2013 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +import logging + +__version__ = '0.1.7' + +class DistlibException(Exception): + pass + +try: + from logging import NullHandler +except ImportError: # pragma: no cover + class NullHandler(logging.Handler): + def handle(self, record): pass + def emit(self, record): pass + +logger = logging.getLogger(__name__) +logger.addHandler(NullHandler()) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/__init__.py new file mode 100644 index 0000000..f7dbf4c --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/__init__.py @@ -0,0 +1,6 @@ +"""Modules copied from Python 3 standard libraries, for internal use only. + +Individual classes and functions are found in d2._backport.misc. Intended +usage is to always import things missing from 3.1 from that module: the +built-in/stdlib objects will be used if found. +""" diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/misc.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/misc.py new file mode 100644 index 0000000..cfb318d --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/misc.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +"""Backports for individual classes and functions.""" + +import os +import sys + +__all__ = ['cache_from_source', 'callable', 'fsencode'] + + +try: + from imp import cache_from_source +except ImportError: + def cache_from_source(py_file, debug=__debug__): + ext = debug and 'c' or 'o' + return py_file + ext + + +try: + callable = callable +except NameError: + from collections import Callable + + def callable(obj): + return isinstance(obj, Callable) + + +try: + fsencode = os.fsencode +except AttributeError: + def fsencode(filename): + if isinstance(filename, bytes): + return filename + elif isinstance(filename, str): + return filename.encode(sys.getfilesystemencoding()) + else: + raise TypeError("expect bytes or str, not %s" % + type(filename).__name__) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/shutil.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/shutil.py new file mode 100644 index 0000000..9e2e234 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/shutil.py @@ -0,0 +1,761 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +"""Utility functions for copying and archiving files and directory trees. + +XXX The functions here don't copy the resource fork or other metadata on Mac. + +""" + +import os +import sys +import stat +from os.path import abspath +import fnmatch +import collections +import errno +from . import tarfile + +try: + import bz2 + _BZ2_SUPPORTED = True +except ImportError: + _BZ2_SUPPORTED = False + +try: + from pwd import getpwnam +except ImportError: + getpwnam = None + +try: + from grp import getgrnam +except ImportError: + getgrnam = None + +__all__ = ["copyfileobj", "copyfile", "copymode", "copystat", "copy", "copy2", + "copytree", "move", "rmtree", "Error", "SpecialFileError", + "ExecError", "make_archive", "get_archive_formats", + "register_archive_format", "unregister_archive_format", + "get_unpack_formats", "register_unpack_format", + "unregister_unpack_format", "unpack_archive", "ignore_patterns"] + +class Error(EnvironmentError): + pass + +class SpecialFileError(EnvironmentError): + """Raised when trying to do a kind of operation (e.g. copying) which is + not supported on a special file (e.g. a named pipe)""" + +class ExecError(EnvironmentError): + """Raised when a command could not be executed""" + +class ReadError(EnvironmentError): + """Raised when an archive cannot be read""" + +class RegistryError(Exception): + """Raised when a registery operation with the archiving + and unpacking registeries fails""" + + +try: + WindowsError +except NameError: + WindowsError = None + +def copyfileobj(fsrc, fdst, length=16*1024): + """copy data from file-like object fsrc to file-like object fdst""" + while 1: + buf = fsrc.read(length) + if not buf: + break + fdst.write(buf) + +def _samefile(src, dst): + # Macintosh, Unix. + if hasattr(os.path, 'samefile'): + try: + return os.path.samefile(src, dst) + except OSError: + return False + + # All other platforms: check for same pathname. + return (os.path.normcase(os.path.abspath(src)) == + os.path.normcase(os.path.abspath(dst))) + +def copyfile(src, dst): + """Copy data from src to dst""" + if _samefile(src, dst): + raise Error("`%s` and `%s` are the same file" % (src, dst)) + + for fn in [src, dst]: + try: + st = os.stat(fn) + except OSError: + # File most likely does not exist + pass + else: + # XXX What about other special files? (sockets, devices...) + if stat.S_ISFIFO(st.st_mode): + raise SpecialFileError("`%s` is a named pipe" % fn) + + with open(src, 'rb') as fsrc: + with open(dst, 'wb') as fdst: + copyfileobj(fsrc, fdst) + +def copymode(src, dst): + """Copy mode bits from src to dst""" + if hasattr(os, 'chmod'): + st = os.stat(src) + mode = stat.S_IMODE(st.st_mode) + os.chmod(dst, mode) + +def copystat(src, dst): + """Copy all stat info (mode bits, atime, mtime, flags) from src to dst""" + st = os.stat(src) + mode = stat.S_IMODE(st.st_mode) + if hasattr(os, 'utime'): + os.utime(dst, (st.st_atime, st.st_mtime)) + if hasattr(os, 'chmod'): + os.chmod(dst, mode) + if hasattr(os, 'chflags') and hasattr(st, 'st_flags'): + try: + os.chflags(dst, st.st_flags) + except OSError as why: + if (not hasattr(errno, 'EOPNOTSUPP') or + why.errno != errno.EOPNOTSUPP): + raise + +def copy(src, dst): + """Copy data and mode bits ("cp src dst"). + + The destination may be a directory. + + """ + if os.path.isdir(dst): + dst = os.path.join(dst, os.path.basename(src)) + copyfile(src, dst) + copymode(src, dst) + +def copy2(src, dst): + """Copy data and all stat info ("cp -p src dst"). + + The destination may be a directory. + + """ + if os.path.isdir(dst): + dst = os.path.join(dst, os.path.basename(src)) + copyfile(src, dst) + copystat(src, dst) + +def ignore_patterns(*patterns): + """Function that can be used as copytree() ignore parameter. + + Patterns is a sequence of glob-style patterns + that are used to exclude files""" + def _ignore_patterns(path, names): + ignored_names = [] + for pattern in patterns: + ignored_names.extend(fnmatch.filter(names, pattern)) + return set(ignored_names) + return _ignore_patterns + +def copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2, + ignore_dangling_symlinks=False): + """Recursively copy a directory tree. + + The destination directory must not already exist. + If exception(s) occur, an Error is raised with a list of reasons. + + If the optional symlinks flag is true, symbolic links in the + source tree result in symbolic links in the destination tree; if + it is false, the contents of the files pointed to by symbolic + links are copied. If the file pointed by the symlink doesn't + exist, an exception will be added in the list of errors raised in + an Error exception at the end of the copy process. + + You can set the optional ignore_dangling_symlinks flag to true if you + want to silence this exception. Notice that this has no effect on + platforms that don't support os.symlink. + + The optional ignore argument is a callable. If given, it + is called with the `src` parameter, which is the directory + being visited by copytree(), and `names` which is the list of + `src` contents, as returned by os.listdir(): + + callable(src, names) -> ignored_names + + Since copytree() is called recursively, the callable will be + called once for each directory that is copied. It returns a + list of names relative to the `src` directory that should + not be copied. + + The optional copy_function argument is a callable that will be used + to copy each file. It will be called with the source path and the + destination path as arguments. By default, copy2() is used, but any + function that supports the same signature (like copy()) can be used. + + """ + names = os.listdir(src) + if ignore is not None: + ignored_names = ignore(src, names) + else: + ignored_names = set() + + os.makedirs(dst) + errors = [] + for name in names: + if name in ignored_names: + continue + srcname = os.path.join(src, name) + dstname = os.path.join(dst, name) + try: + if os.path.islink(srcname): + linkto = os.readlink(srcname) + if symlinks: + os.symlink(linkto, dstname) + else: + # ignore dangling symlink if the flag is on + if not os.path.exists(linkto) and ignore_dangling_symlinks: + continue + # otherwise let the copy occurs. copy2 will raise an error + copy_function(srcname, dstname) + elif os.path.isdir(srcname): + copytree(srcname, dstname, symlinks, ignore, copy_function) + else: + # Will raise a SpecialFileError for unsupported file types + copy_function(srcname, dstname) + # catch the Error from the recursive copytree so that we can + # continue with other files + except Error as err: + errors.extend(err.args[0]) + except EnvironmentError as why: + errors.append((srcname, dstname, str(why))) + try: + copystat(src, dst) + except OSError as why: + if WindowsError is not None and isinstance(why, WindowsError): + # Copying file access times may fail on Windows + pass + else: + errors.extend((src, dst, str(why))) + if errors: + raise Error(errors) + +def rmtree(path, ignore_errors=False, onerror=None): + """Recursively delete a directory tree. + + If ignore_errors is set, errors are ignored; otherwise, if onerror + is set, it is called to handle the error with arguments (func, + path, exc_info) where func is os.listdir, os.remove, or os.rmdir; + path is the argument to that function that caused it to fail; and + exc_info is a tuple returned by sys.exc_info(). If ignore_errors + is false and onerror is None, an exception is raised. + + """ + if ignore_errors: + def onerror(*args): + pass + elif onerror is None: + def onerror(*args): + raise + try: + if os.path.islink(path): + # symlinks to directories are forbidden, see bug #1669 + raise OSError("Cannot call rmtree on a symbolic link") + except OSError: + onerror(os.path.islink, path, sys.exc_info()) + # can't continue even if onerror hook returns + return + names = [] + try: + names = os.listdir(path) + except os.error: + onerror(os.listdir, path, sys.exc_info()) + for name in names: + fullname = os.path.join(path, name) + try: + mode = os.lstat(fullname).st_mode + except os.error: + mode = 0 + if stat.S_ISDIR(mode): + rmtree(fullname, ignore_errors, onerror) + else: + try: + os.remove(fullname) + except os.error: + onerror(os.remove, fullname, sys.exc_info()) + try: + os.rmdir(path) + except os.error: + onerror(os.rmdir, path, sys.exc_info()) + + +def _basename(path): + # A basename() variant which first strips the trailing slash, if present. + # Thus we always get the last component of the path, even for directories. + return os.path.basename(path.rstrip(os.path.sep)) + +def move(src, dst): + """Recursively move a file or directory to another location. This is + similar to the Unix "mv" command. + + If the destination is a directory or a symlink to a directory, the source + is moved inside the directory. The destination path must not already + exist. + + If the destination already exists but is not a directory, it may be + overwritten depending on os.rename() semantics. + + If the destination is on our current filesystem, then rename() is used. + Otherwise, src is copied to the destination and then removed. + A lot more could be done here... A look at a mv.c shows a lot of + the issues this implementation glosses over. + + """ + real_dst = dst + if os.path.isdir(dst): + if _samefile(src, dst): + # We might be on a case insensitive filesystem, + # perform the rename anyway. + os.rename(src, dst) + return + + real_dst = os.path.join(dst, _basename(src)) + if os.path.exists(real_dst): + raise Error("Destination path '%s' already exists" % real_dst) + try: + os.rename(src, real_dst) + except OSError: + if os.path.isdir(src): + if _destinsrc(src, dst): + raise Error("Cannot move a directory '%s' into itself '%s'." % (src, dst)) + copytree(src, real_dst, symlinks=True) + rmtree(src) + else: + copy2(src, real_dst) + os.unlink(src) + +def _destinsrc(src, dst): + src = abspath(src) + dst = abspath(dst) + if not src.endswith(os.path.sep): + src += os.path.sep + if not dst.endswith(os.path.sep): + dst += os.path.sep + return dst.startswith(src) + +def _get_gid(name): + """Returns a gid, given a group name.""" + if getgrnam is None or name is None: + return None + try: + result = getgrnam(name) + except KeyError: + result = None + if result is not None: + return result[2] + return None + +def _get_uid(name): + """Returns an uid, given a user name.""" + if getpwnam is None or name is None: + return None + try: + result = getpwnam(name) + except KeyError: + result = None + if result is not None: + return result[2] + return None + +def _make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0, + owner=None, group=None, logger=None): + """Create a (possibly compressed) tar file from all the files under + 'base_dir'. + + 'compress' must be "gzip" (the default), "bzip2", or None. + + 'owner' and 'group' can be used to define an owner and a group for the + archive that is being built. If not provided, the current owner and group + will be used. + + The output tar file will be named 'base_name' + ".tar", possibly plus + the appropriate compression extension (".gz", or ".bz2"). + + Returns the output filename. + """ + tar_compression = {'gzip': 'gz', None: ''} + compress_ext = {'gzip': '.gz'} + + if _BZ2_SUPPORTED: + tar_compression['bzip2'] = 'bz2' + compress_ext['bzip2'] = '.bz2' + + # flags for compression program, each element of list will be an argument + if compress is not None and compress not in compress_ext: + raise ValueError("bad value for 'compress', or compression format not " + "supported : {0}".format(compress)) + + archive_name = base_name + '.tar' + compress_ext.get(compress, '') + archive_dir = os.path.dirname(archive_name) + + if not os.path.exists(archive_dir): + if logger is not None: + logger.info("creating %s", archive_dir) + if not dry_run: + os.makedirs(archive_dir) + + # creating the tarball + if logger is not None: + logger.info('Creating tar archive') + + uid = _get_uid(owner) + gid = _get_gid(group) + + def _set_uid_gid(tarinfo): + if gid is not None: + tarinfo.gid = gid + tarinfo.gname = group + if uid is not None: + tarinfo.uid = uid + tarinfo.uname = owner + return tarinfo + + if not dry_run: + tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress]) + try: + tar.add(base_dir, filter=_set_uid_gid) + finally: + tar.close() + + return archive_name + +def _call_external_zip(base_dir, zip_filename, verbose=False, dry_run=False): + # XXX see if we want to keep an external call here + if verbose: + zipoptions = "-r" + else: + zipoptions = "-rq" + from distutils.errors import DistutilsExecError + from distutils.spawn import spawn + try: + spawn(["zip", zipoptions, zip_filename, base_dir], dry_run=dry_run) + except DistutilsExecError: + # XXX really should distinguish between "couldn't find + # external 'zip' command" and "zip failed". + raise ExecError("unable to create zip file '%s': " + "could neither import the 'zipfile' module nor " + "find a standalone zip utility") % zip_filename + +def _make_zipfile(base_name, base_dir, verbose=0, dry_run=0, logger=None): + """Create a zip file from all the files under 'base_dir'. + + The output zip file will be named 'base_name' + ".zip". Uses either the + "zipfile" Python module (if available) or the InfoZIP "zip" utility + (if installed and found on the default search path). If neither tool is + available, raises ExecError. Returns the name of the output zip + file. + """ + zip_filename = base_name + ".zip" + archive_dir = os.path.dirname(base_name) + + if not os.path.exists(archive_dir): + if logger is not None: + logger.info("creating %s", archive_dir) + if not dry_run: + os.makedirs(archive_dir) + + # If zipfile module is not available, try spawning an external 'zip' + # command. + try: + import zipfile + except ImportError: + zipfile = None + + if zipfile is None: + _call_external_zip(base_dir, zip_filename, verbose, dry_run) + else: + if logger is not None: + logger.info("creating '%s' and adding '%s' to it", + zip_filename, base_dir) + + if not dry_run: + zip = zipfile.ZipFile(zip_filename, "w", + compression=zipfile.ZIP_DEFLATED) + + for dirpath, dirnames, filenames in os.walk(base_dir): + for name in filenames: + path = os.path.normpath(os.path.join(dirpath, name)) + if os.path.isfile(path): + zip.write(path, path) + if logger is not None: + logger.info("adding '%s'", path) + zip.close() + + return zip_filename + +_ARCHIVE_FORMATS = { + 'gztar': (_make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"), + 'bztar': (_make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"), + 'tar': (_make_tarball, [('compress', None)], "uncompressed tar file"), + 'zip': (_make_zipfile, [], "ZIP file"), + } + +if _BZ2_SUPPORTED: + _ARCHIVE_FORMATS['bztar'] = (_make_tarball, [('compress', 'bzip2')], + "bzip2'ed tar-file") + +def get_archive_formats(): + """Returns a list of supported formats for archiving and unarchiving. + + Each element of the returned sequence is a tuple (name, description) + """ + formats = [(name, registry[2]) for name, registry in + _ARCHIVE_FORMATS.items()] + formats.sort() + return formats + +def register_archive_format(name, function, extra_args=None, description=''): + """Registers an archive format. + + name is the name of the format. function is the callable that will be + used to create archives. If provided, extra_args is a sequence of + (name, value) tuples that will be passed as arguments to the callable. + description can be provided to describe the format, and will be returned + by the get_archive_formats() function. + """ + if extra_args is None: + extra_args = [] + if not isinstance(function, collections.Callable): + raise TypeError('The %s object is not callable' % function) + if not isinstance(extra_args, (tuple, list)): + raise TypeError('extra_args needs to be a sequence') + for element in extra_args: + if not isinstance(element, (tuple, list)) or len(element) !=2: + raise TypeError('extra_args elements are : (arg_name, value)') + + _ARCHIVE_FORMATS[name] = (function, extra_args, description) + +def unregister_archive_format(name): + del _ARCHIVE_FORMATS[name] + +def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, + dry_run=0, owner=None, group=None, logger=None): + """Create an archive file (eg. zip or tar). + + 'base_name' is the name of the file to create, minus any format-specific + extension; 'format' is the archive format: one of "zip", "tar", "bztar" + or "gztar". + + 'root_dir' is a directory that will be the root directory of the + archive; ie. we typically chdir into 'root_dir' before creating the + archive. 'base_dir' is the directory where we start archiving from; + ie. 'base_dir' will be the common prefix of all files and + directories in the archive. 'root_dir' and 'base_dir' both default + to the current directory. Returns the name of the archive file. + + 'owner' and 'group' are used when creating a tar archive. By default, + uses the current owner and group. + """ + save_cwd = os.getcwd() + if root_dir is not None: + if logger is not None: + logger.debug("changing into '%s'", root_dir) + base_name = os.path.abspath(base_name) + if not dry_run: + os.chdir(root_dir) + + if base_dir is None: + base_dir = os.curdir + + kwargs = {'dry_run': dry_run, 'logger': logger} + + try: + format_info = _ARCHIVE_FORMATS[format] + except KeyError: + raise ValueError("unknown archive format '%s'" % format) + + func = format_info[0] + for arg, val in format_info[1]: + kwargs[arg] = val + + if format != 'zip': + kwargs['owner'] = owner + kwargs['group'] = group + + try: + filename = func(base_name, base_dir, **kwargs) + finally: + if root_dir is not None: + if logger is not None: + logger.debug("changing back to '%s'", save_cwd) + os.chdir(save_cwd) + + return filename + + +def get_unpack_formats(): + """Returns a list of supported formats for unpacking. + + Each element of the returned sequence is a tuple + (name, extensions, description) + """ + formats = [(name, info[0], info[3]) for name, info in + _UNPACK_FORMATS.items()] + formats.sort() + return formats + +def _check_unpack_options(extensions, function, extra_args): + """Checks what gets registered as an unpacker.""" + # first make sure no other unpacker is registered for this extension + existing_extensions = {} + for name, info in _UNPACK_FORMATS.items(): + for ext in info[0]: + existing_extensions[ext] = name + + for extension in extensions: + if extension in existing_extensions: + msg = '%s is already registered for "%s"' + raise RegistryError(msg % (extension, + existing_extensions[extension])) + + if not isinstance(function, collections.Callable): + raise TypeError('The registered function must be a callable') + + +def register_unpack_format(name, extensions, function, extra_args=None, + description=''): + """Registers an unpack format. + + `name` is the name of the format. `extensions` is a list of extensions + corresponding to the format. + + `function` is the callable that will be + used to unpack archives. The callable will receive archives to unpack. + If it's unable to handle an archive, it needs to raise a ReadError + exception. + + If provided, `extra_args` is a sequence of + (name, value) tuples that will be passed as arguments to the callable. + description can be provided to describe the format, and will be returned + by the get_unpack_formats() function. + """ + if extra_args is None: + extra_args = [] + _check_unpack_options(extensions, function, extra_args) + _UNPACK_FORMATS[name] = extensions, function, extra_args, description + +def unregister_unpack_format(name): + """Removes the pack format from the registery.""" + del _UNPACK_FORMATS[name] + +def _ensure_directory(path): + """Ensure that the parent directory of `path` exists""" + dirname = os.path.dirname(path) + if not os.path.isdir(dirname): + os.makedirs(dirname) + +def _unpack_zipfile(filename, extract_dir): + """Unpack zip `filename` to `extract_dir` + """ + try: + import zipfile + except ImportError: + raise ReadError('zlib not supported, cannot unpack this archive.') + + if not zipfile.is_zipfile(filename): + raise ReadError("%s is not a zip file" % filename) + + zip = zipfile.ZipFile(filename) + try: + for info in zip.infolist(): + name = info.filename + + # don't extract absolute paths or ones with .. in them + if name.startswith('/') or '..' in name: + continue + + target = os.path.join(extract_dir, *name.split('/')) + if not target: + continue + + _ensure_directory(target) + if not name.endswith('/'): + # file + data = zip.read(info.filename) + f = open(target, 'wb') + try: + f.write(data) + finally: + f.close() + del data + finally: + zip.close() + +def _unpack_tarfile(filename, extract_dir): + """Unpack tar/tar.gz/tar.bz2 `filename` to `extract_dir` + """ + try: + tarobj = tarfile.open(filename) + except tarfile.TarError: + raise ReadError( + "%s is not a compressed or uncompressed tar file" % filename) + try: + tarobj.extractall(extract_dir) + finally: + tarobj.close() + +_UNPACK_FORMATS = { + 'gztar': (['.tar.gz', '.tgz'], _unpack_tarfile, [], "gzip'ed tar-file"), + 'tar': (['.tar'], _unpack_tarfile, [], "uncompressed tar file"), + 'zip': (['.zip'], _unpack_zipfile, [], "ZIP file") + } + +if _BZ2_SUPPORTED: + _UNPACK_FORMATS['bztar'] = (['.bz2'], _unpack_tarfile, [], + "bzip2'ed tar-file") + +def _find_unpack_format(filename): + for name, info in _UNPACK_FORMATS.items(): + for extension in info[0]: + if filename.endswith(extension): + return name + return None + +def unpack_archive(filename, extract_dir=None, format=None): + """Unpack an archive. + + `filename` is the name of the archive. + + `extract_dir` is the name of the target directory, where the archive + is unpacked. If not provided, the current working directory is used. + + `format` is the archive format: one of "zip", "tar", or "gztar". Or any + other registered format. If not provided, unpack_archive will use the + filename extension and see if an unpacker was registered for that + extension. + + In case none is found, a ValueError is raised. + """ + if extract_dir is None: + extract_dir = os.getcwd() + + if format is not None: + try: + format_info = _UNPACK_FORMATS[format] + except KeyError: + raise ValueError("Unknown unpack format '{0}'".format(format)) + + func = format_info[1] + func(filename, extract_dir, **dict(format_info[2])) + else: + # we need to look at the registered unpackers supported extensions + format = _find_unpack_format(filename) + if format is None: + raise ReadError("Unknown archive format '{0}'".format(filename)) + + func = _UNPACK_FORMATS[format][1] + kwargs = dict(_UNPACK_FORMATS[format][2]) + func(filename, extract_dir, **kwargs) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg new file mode 100644 index 0000000..1746bd0 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg @@ -0,0 +1,84 @@ +[posix_prefix] +# Configuration directories. Some of these come straight out of the +# configure script. They are for implementing the other variables, not to +# be used directly in [resource_locations]. +confdir = /etc +datadir = /usr/share +libdir = /usr/lib +statedir = /var +# User resource directory +local = ~/.local/{distribution.name} + +stdlib = {base}/lib/python{py_version_short} +platstdlib = {platbase}/lib/python{py_version_short} +purelib = {base}/lib/python{py_version_short}/site-packages +platlib = {platbase}/lib/python{py_version_short}/site-packages +include = {base}/include/python{py_version_short}{abiflags} +platinclude = {platbase}/include/python{py_version_short}{abiflags} +data = {base} + +[posix_home] +stdlib = {base}/lib/python +platstdlib = {base}/lib/python +purelib = {base}/lib/python +platlib = {base}/lib/python +include = {base}/include/python +platinclude = {base}/include/python +scripts = {base}/bin +data = {base} + +[nt] +stdlib = {base}/Lib +platstdlib = {base}/Lib +purelib = {base}/Lib/site-packages +platlib = {base}/Lib/site-packages +include = {base}/Include +platinclude = {base}/Include +scripts = {base}/Scripts +data = {base} + +[os2] +stdlib = {base}/Lib +platstdlib = {base}/Lib +purelib = {base}/Lib/site-packages +platlib = {base}/Lib/site-packages +include = {base}/Include +platinclude = {base}/Include +scripts = {base}/Scripts +data = {base} + +[os2_home] +stdlib = {userbase}/lib/python{py_version_short} +platstdlib = {userbase}/lib/python{py_version_short} +purelib = {userbase}/lib/python{py_version_short}/site-packages +platlib = {userbase}/lib/python{py_version_short}/site-packages +include = {userbase}/include/python{py_version_short} +scripts = {userbase}/bin +data = {userbase} + +[nt_user] +stdlib = {userbase}/Python{py_version_nodot} +platstdlib = {userbase}/Python{py_version_nodot} +purelib = {userbase}/Python{py_version_nodot}/site-packages +platlib = {userbase}/Python{py_version_nodot}/site-packages +include = {userbase}/Python{py_version_nodot}/Include +scripts = {userbase}/Scripts +data = {userbase} + +[posix_user] +stdlib = {userbase}/lib/python{py_version_short} +platstdlib = {userbase}/lib/python{py_version_short} +purelib = {userbase}/lib/python{py_version_short}/site-packages +platlib = {userbase}/lib/python{py_version_short}/site-packages +include = {userbase}/include/python{py_version_short} +scripts = {userbase}/bin +data = {userbase} + +[osx_framework_user] +stdlib = {userbase}/lib/python +platstdlib = {userbase}/lib/python +purelib = {userbase}/lib/python/site-packages +platlib = {userbase}/lib/python/site-packages +include = {userbase}/include +scripts = {userbase}/bin +data = {userbase} diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/sysconfig.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/sysconfig.py new file mode 100644 index 0000000..a5b55fe --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/sysconfig.py @@ -0,0 +1,788 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +"""Access to Python's configuration information.""" + +import codecs +import os +import re +import sys +from os.path import pardir, realpath +try: + import configparser +except ImportError: + import ConfigParser as configparser + + +__all__ = [ + 'get_config_h_filename', + 'get_config_var', + 'get_config_vars', + 'get_makefile_filename', + 'get_path', + 'get_path_names', + 'get_paths', + 'get_platform', + 'get_python_version', + 'get_scheme_names', + 'parse_config_h', +] + + +def _safe_realpath(path): + try: + return realpath(path) + except OSError: + return path + + +if sys.executable: + _PROJECT_BASE = os.path.dirname(_safe_realpath(sys.executable)) +else: + # sys.executable can be empty if argv[0] has been changed and Python is + # unable to retrieve the real program name + _PROJECT_BASE = _safe_realpath(os.getcwd()) + +if os.name == "nt" and "pcbuild" in _PROJECT_BASE[-8:].lower(): + _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir)) +# PC/VS7.1 +if os.name == "nt" and "\\pc\\v" in _PROJECT_BASE[-10:].lower(): + _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir, pardir)) +# PC/AMD64 +if os.name == "nt" and "\\pcbuild\\amd64" in _PROJECT_BASE[-14:].lower(): + _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir, pardir)) + + +def is_python_build(): + for fn in ("Setup.dist", "Setup.local"): + if os.path.isfile(os.path.join(_PROJECT_BASE, "Modules", fn)): + return True + return False + +_PYTHON_BUILD = is_python_build() + +_cfg_read = False + +def _ensure_cfg_read(): + global _cfg_read + if not _cfg_read: + from distlib.resources import finder + backport_package = __name__.rsplit('.', 1)[0] + _finder = finder(backport_package) + _cfgfile = _finder.find('sysconfig.cfg') + assert _cfgfile, 'sysconfig.cfg exists' + with _cfgfile.as_stream() as s: + _SCHEMES.readfp(s) + if _PYTHON_BUILD: + for scheme in ('posix_prefix', 'posix_home'): + _SCHEMES.set(scheme, 'include', '{srcdir}/Include') + _SCHEMES.set(scheme, 'platinclude', '{projectbase}/.') + + _cfg_read = True + + +_SCHEMES = configparser.RawConfigParser() +_VAR_REPL = re.compile(r'\{([^{]*?)\}') + +def _expand_globals(config): + _ensure_cfg_read() + if config.has_section('globals'): + globals = config.items('globals') + else: + globals = tuple() + + sections = config.sections() + for section in sections: + if section == 'globals': + continue + for option, value in globals: + if config.has_option(section, option): + continue + config.set(section, option, value) + config.remove_section('globals') + + # now expanding local variables defined in the cfg file + # + for section in config.sections(): + variables = dict(config.items(section)) + + def _replacer(matchobj): + name = matchobj.group(1) + if name in variables: + return variables[name] + return matchobj.group(0) + + for option, value in config.items(section): + config.set(section, option, _VAR_REPL.sub(_replacer, value)) + +#_expand_globals(_SCHEMES) + + # FIXME don't rely on sys.version here, its format is an implementation detail + # of CPython, use sys.version_info or sys.hexversion +_PY_VERSION = sys.version.split()[0] +_PY_VERSION_SHORT = sys.version[:3] +_PY_VERSION_SHORT_NO_DOT = _PY_VERSION[0] + _PY_VERSION[2] +_PREFIX = os.path.normpath(sys.prefix) +_EXEC_PREFIX = os.path.normpath(sys.exec_prefix) +_CONFIG_VARS = None +_USER_BASE = None + + +def _subst_vars(path, local_vars): + """In the string `path`, replace tokens like {some.thing} with the + corresponding value from the map `local_vars`. + + If there is no corresponding value, leave the token unchanged. + """ + def _replacer(matchobj): + name = matchobj.group(1) + if name in local_vars: + return local_vars[name] + elif name in os.environ: + return os.environ[name] + return matchobj.group(0) + return _VAR_REPL.sub(_replacer, path) + + +def _extend_dict(target_dict, other_dict): + target_keys = target_dict.keys() + for key, value in other_dict.items(): + if key in target_keys: + continue + target_dict[key] = value + + +def _expand_vars(scheme, vars): + res = {} + if vars is None: + vars = {} + _extend_dict(vars, get_config_vars()) + + for key, value in _SCHEMES.items(scheme): + if os.name in ('posix', 'nt'): + value = os.path.expanduser(value) + res[key] = os.path.normpath(_subst_vars(value, vars)) + return res + + +def format_value(value, vars): + def _replacer(matchobj): + name = matchobj.group(1) + if name in vars: + return vars[name] + return matchobj.group(0) + return _VAR_REPL.sub(_replacer, value) + + +def _get_default_scheme(): + if os.name == 'posix': + # the default scheme for posix is posix_prefix + return 'posix_prefix' + return os.name + + +def _getuserbase(): + env_base = os.environ.get("PYTHONUSERBASE", None) + + def joinuser(*args): + return os.path.expanduser(os.path.join(*args)) + + # what about 'os2emx', 'riscos' ? + if os.name == "nt": + base = os.environ.get("APPDATA") or "~" + if env_base: + return env_base + else: + return joinuser(base, "Python") + + if sys.platform == "darwin": + framework = get_config_var("PYTHONFRAMEWORK") + if framework: + if env_base: + return env_base + else: + return joinuser("~", "Library", framework, "%d.%d" % + sys.version_info[:2]) + + if env_base: + return env_base + else: + return joinuser("~", ".local") + + +def _parse_makefile(filename, vars=None): + """Parse a Makefile-style file. + + A dictionary containing name/value pairs is returned. If an + optional dictionary is passed in as the second argument, it is + used instead of a new dictionary. + """ + # Regexes needed for parsing Makefile (and similar syntaxes, + # like old-style Setup files). + _variable_rx = re.compile("([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)") + _findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)") + _findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}") + + if vars is None: + vars = {} + done = {} + notdone = {} + + with codecs.open(filename, encoding='utf-8', errors="surrogateescape") as f: + lines = f.readlines() + + for line in lines: + if line.startswith('#') or line.strip() == '': + continue + m = _variable_rx.match(line) + if m: + n, v = m.group(1, 2) + v = v.strip() + # `$$' is a literal `$' in make + tmpv = v.replace('$$', '') + + if "$" in tmpv: + notdone[n] = v + else: + try: + v = int(v) + except ValueError: + # insert literal `$' + done[n] = v.replace('$$', '$') + else: + done[n] = v + + # do variable interpolation here + variables = list(notdone.keys()) + + # Variables with a 'PY_' prefix in the makefile. These need to + # be made available without that prefix through sysconfig. + # Special care is needed to ensure that variable expansion works, even + # if the expansion uses the name without a prefix. + renamed_variables = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS') + + while len(variables) > 0: + for name in tuple(variables): + value = notdone[name] + m = _findvar1_rx.search(value) or _findvar2_rx.search(value) + if m is not None: + n = m.group(1) + found = True + if n in done: + item = str(done[n]) + elif n in notdone: + # get it on a subsequent round + found = False + elif n in os.environ: + # do it like make: fall back to environment + item = os.environ[n] + + elif n in renamed_variables: + if (name.startswith('PY_') and + name[3:] in renamed_variables): + item = "" + + elif 'PY_' + n in notdone: + found = False + + else: + item = str(done['PY_' + n]) + + else: + done[n] = item = "" + + if found: + after = value[m.end():] + value = value[:m.start()] + item + after + if "$" in after: + notdone[name] = value + else: + try: + value = int(value) + except ValueError: + done[name] = value.strip() + else: + done[name] = value + variables.remove(name) + + if (name.startswith('PY_') and + name[3:] in renamed_variables): + + name = name[3:] + if name not in done: + done[name] = value + + else: + # bogus variable reference (e.g. "prefix=$/opt/python"); + # just drop it since we can't deal + done[name] = value + variables.remove(name) + + # strip spurious spaces + for k, v in done.items(): + if isinstance(v, str): + done[k] = v.strip() + + # save the results in the global dictionary + vars.update(done) + return vars + + +def get_makefile_filename(): + """Return the path of the Makefile.""" + if _PYTHON_BUILD: + return os.path.join(_PROJECT_BASE, "Makefile") + if hasattr(sys, 'abiflags'): + config_dir_name = 'config-%s%s' % (_PY_VERSION_SHORT, sys.abiflags) + else: + config_dir_name = 'config' + return os.path.join(get_path('stdlib'), config_dir_name, 'Makefile') + + +def _init_posix(vars): + """Initialize the module as appropriate for POSIX systems.""" + # load the installed Makefile: + makefile = get_makefile_filename() + try: + _parse_makefile(makefile, vars) + except IOError as e: + msg = "invalid Python installation: unable to open %s" % makefile + if hasattr(e, "strerror"): + msg = msg + " (%s)" % e.strerror + raise IOError(msg) + # load the installed pyconfig.h: + config_h = get_config_h_filename() + try: + with open(config_h) as f: + parse_config_h(f, vars) + except IOError as e: + msg = "invalid Python installation: unable to open %s" % config_h + if hasattr(e, "strerror"): + msg = msg + " (%s)" % e.strerror + raise IOError(msg) + # On AIX, there are wrong paths to the linker scripts in the Makefile + # -- these paths are relative to the Python source, but when installed + # the scripts are in another directory. + if _PYTHON_BUILD: + vars['LDSHARED'] = vars['BLDSHARED'] + + +def _init_non_posix(vars): + """Initialize the module as appropriate for NT""" + # set basic install directories + vars['LIBDEST'] = get_path('stdlib') + vars['BINLIBDEST'] = get_path('platstdlib') + vars['INCLUDEPY'] = get_path('include') + vars['SO'] = '.pyd' + vars['EXE'] = '.exe' + vars['VERSION'] = _PY_VERSION_SHORT_NO_DOT + vars['BINDIR'] = os.path.dirname(_safe_realpath(sys.executable)) + +# +# public APIs +# + + +def parse_config_h(fp, vars=None): + """Parse a config.h-style file. + + A dictionary containing name/value pairs is returned. If an + optional dictionary is passed in as the second argument, it is + used instead of a new dictionary. + """ + if vars is None: + vars = {} + define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n") + undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n") + + while True: + line = fp.readline() + if not line: + break + m = define_rx.match(line) + if m: + n, v = m.group(1, 2) + try: + v = int(v) + except ValueError: + pass + vars[n] = v + else: + m = undef_rx.match(line) + if m: + vars[m.group(1)] = 0 + return vars + + +def get_config_h_filename(): + """Return the path of pyconfig.h.""" + if _PYTHON_BUILD: + if os.name == "nt": + inc_dir = os.path.join(_PROJECT_BASE, "PC") + else: + inc_dir = _PROJECT_BASE + else: + inc_dir = get_path('platinclude') + return os.path.join(inc_dir, 'pyconfig.h') + + +def get_scheme_names(): + """Return a tuple containing the schemes names.""" + return tuple(sorted(_SCHEMES.sections())) + + +def get_path_names(): + """Return a tuple containing the paths names.""" + # xxx see if we want a static list + return _SCHEMES.options('posix_prefix') + + +def get_paths(scheme=_get_default_scheme(), vars=None, expand=True): + """Return a mapping containing an install scheme. + + ``scheme`` is the install scheme name. If not provided, it will + return the default scheme for the current platform. + """ + _ensure_cfg_read() + if expand: + return _expand_vars(scheme, vars) + else: + return dict(_SCHEMES.items(scheme)) + + +def get_path(name, scheme=_get_default_scheme(), vars=None, expand=True): + """Return a path corresponding to the scheme. + + ``scheme`` is the install scheme name. + """ + return get_paths(scheme, vars, expand)[name] + + +def get_config_vars(*args): + """With no arguments, return a dictionary of all configuration + variables relevant for the current platform. + + On Unix, this means every variable defined in Python's installed Makefile; + On Windows and Mac OS it's a much smaller set. + + With arguments, return a list of values that result from looking up + each argument in the configuration variable dictionary. + """ + global _CONFIG_VARS + if _CONFIG_VARS is None: + _CONFIG_VARS = {} + # Normalized versions of prefix and exec_prefix are handy to have; + # in fact, these are the standard versions used most places in the + # distutils2 module. + _CONFIG_VARS['prefix'] = _PREFIX + _CONFIG_VARS['exec_prefix'] = _EXEC_PREFIX + _CONFIG_VARS['py_version'] = _PY_VERSION + _CONFIG_VARS['py_version_short'] = _PY_VERSION_SHORT + _CONFIG_VARS['py_version_nodot'] = _PY_VERSION[0] + _PY_VERSION[2] + _CONFIG_VARS['base'] = _PREFIX + _CONFIG_VARS['platbase'] = _EXEC_PREFIX + _CONFIG_VARS['projectbase'] = _PROJECT_BASE + try: + _CONFIG_VARS['abiflags'] = sys.abiflags + except AttributeError: + # sys.abiflags may not be defined on all platforms. + _CONFIG_VARS['abiflags'] = '' + + if os.name in ('nt', 'os2'): + _init_non_posix(_CONFIG_VARS) + if os.name == 'posix': + _init_posix(_CONFIG_VARS) + # Setting 'userbase' is done below the call to the + # init function to enable using 'get_config_var' in + # the init-function. + if sys.version >= '2.6': + _CONFIG_VARS['userbase'] = _getuserbase() + + if 'srcdir' not in _CONFIG_VARS: + _CONFIG_VARS['srcdir'] = _PROJECT_BASE + else: + _CONFIG_VARS['srcdir'] = _safe_realpath(_CONFIG_VARS['srcdir']) + + # Convert srcdir into an absolute path if it appears necessary. + # Normally it is relative to the build directory. However, during + # testing, for example, we might be running a non-installed python + # from a different directory. + if _PYTHON_BUILD and os.name == "posix": + base = _PROJECT_BASE + try: + cwd = os.getcwd() + except OSError: + cwd = None + if (not os.path.isabs(_CONFIG_VARS['srcdir']) and + base != cwd): + # srcdir is relative and we are not in the same directory + # as the executable. Assume executable is in the build + # directory and make srcdir absolute. + srcdir = os.path.join(base, _CONFIG_VARS['srcdir']) + _CONFIG_VARS['srcdir'] = os.path.normpath(srcdir) + + if sys.platform == 'darwin': + kernel_version = os.uname()[2] # Kernel version (8.4.3) + major_version = int(kernel_version.split('.')[0]) + + if major_version < 8: + # On Mac OS X before 10.4, check if -arch and -isysroot + # are in CFLAGS or LDFLAGS and remove them if they are. + # This is needed when building extensions on a 10.3 system + # using a universal build of python. + for key in ('LDFLAGS', 'BASECFLAGS', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + flags = _CONFIG_VARS[key] + flags = re.sub('-arch\s+\w+\s', ' ', flags) + flags = re.sub('-isysroot [^ \t]*', ' ', flags) + _CONFIG_VARS[key] = flags + else: + # Allow the user to override the architecture flags using + # an environment variable. + # NOTE: This name was introduced by Apple in OSX 10.5 and + # is used by several scripting languages distributed with + # that OS release. + if 'ARCHFLAGS' in os.environ: + arch = os.environ['ARCHFLAGS'] + for key in ('LDFLAGS', 'BASECFLAGS', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + + flags = _CONFIG_VARS[key] + flags = re.sub('-arch\s+\w+\s', ' ', flags) + flags = flags + ' ' + arch + _CONFIG_VARS[key] = flags + + # If we're on OSX 10.5 or later and the user tries to + # compiles an extension using an SDK that is not present + # on the current machine it is better to not use an SDK + # than to fail. + # + # The major usecase for this is users using a Python.org + # binary installer on OSX 10.6: that installer uses + # the 10.4u SDK, but that SDK is not installed by default + # when you install Xcode. + # + CFLAGS = _CONFIG_VARS.get('CFLAGS', '') + m = re.search('-isysroot\s+(\S+)', CFLAGS) + if m is not None: + sdk = m.group(1) + if not os.path.exists(sdk): + for key in ('LDFLAGS', 'BASECFLAGS', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + + flags = _CONFIG_VARS[key] + flags = re.sub('-isysroot\s+\S+(\s|$)', ' ', flags) + _CONFIG_VARS[key] = flags + + if args: + vals = [] + for name in args: + vals.append(_CONFIG_VARS.get(name)) + return vals + else: + return _CONFIG_VARS + + +def get_config_var(name): + """Return the value of a single variable using the dictionary returned by + 'get_config_vars()'. + + Equivalent to get_config_vars().get(name) + """ + return get_config_vars().get(name) + + +def get_platform(): + """Return a string that identifies the current platform. + + This is used mainly to distinguish platform-specific build directories and + platform-specific built distributions. Typically includes the OS name + and version and the architecture (as supplied by 'os.uname()'), + although the exact information included depends on the OS; eg. for IRIX + the architecture isn't particularly important (IRIX only runs on SGI + hardware), but for Linux the kernel version isn't particularly + important. + + Examples of returned values: + linux-i586 + linux-alpha (?) + solaris-2.6-sun4u + irix-5.3 + irix64-6.2 + + Windows will return one of: + win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) + win-ia64 (64bit Windows on Itanium) + win32 (all others - specifically, sys.platform is returned) + + For other non-POSIX platforms, currently just returns 'sys.platform'. + """ + if os.name == 'nt': + # sniff sys.version for architecture. + prefix = " bit (" + i = sys.version.find(prefix) + if i == -1: + return sys.platform + j = sys.version.find(")", i) + look = sys.version[i+len(prefix):j].lower() + if look == 'amd64': + return 'win-amd64' + if look == 'itanium': + return 'win-ia64' + return sys.platform + + if os.name != "posix" or not hasattr(os, 'uname'): + # XXX what about the architecture? NT is Intel or Alpha, + # Mac OS is M68k or PPC, etc. + return sys.platform + + # Try to distinguish various flavours of Unix + osname, host, release, version, machine = os.uname() + + # Convert the OS name to lowercase, remove '/' characters + # (to accommodate BSD/OS), and translate spaces (for "Power Macintosh") + osname = osname.lower().replace('/', '') + machine = machine.replace(' ', '_') + machine = machine.replace('/', '-') + + if osname[:5] == "linux": + # At least on Linux/Intel, 'machine' is the processor -- + # i386, etc. + # XXX what about Alpha, SPARC, etc? + return "%s-%s" % (osname, machine) + elif osname[:5] == "sunos": + if release[0] >= "5": # SunOS 5 == Solaris 2 + osname = "solaris" + release = "%d.%s" % (int(release[0]) - 3, release[2:]) + # fall through to standard osname-release-machine representation + elif osname[:4] == "irix": # could be "irix64"! + return "%s-%s" % (osname, release) + elif osname[:3] == "aix": + return "%s-%s.%s" % (osname, version, release) + elif osname[:6] == "cygwin": + osname = "cygwin" + rel_re = re.compile(r'[\d.]+') + m = rel_re.match(release) + if m: + release = m.group() + elif osname[:6] == "darwin": + # + # For our purposes, we'll assume that the system version from + # distutils' perspective is what MACOSX_DEPLOYMENT_TARGET is set + # to. This makes the compatibility story a bit more sane because the + # machine is going to compile and link as if it were + # MACOSX_DEPLOYMENT_TARGET. + cfgvars = get_config_vars() + macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET') + + if True: + # Always calculate the release of the running machine, + # needed to determine if we can build fat binaries or not. + + macrelease = macver + # Get the system version. Reading this plist is a documented + # way to get the system version (see the documentation for + # the Gestalt Manager) + try: + f = open('/System/Library/CoreServices/SystemVersion.plist') + except IOError: + # We're on a plain darwin box, fall back to the default + # behaviour. + pass + else: + try: + m = re.search(r'ProductUserVisibleVersion\s*' + r'(.*?)', f.read()) + finally: + f.close() + if m is not None: + macrelease = '.'.join(m.group(1).split('.')[:2]) + # else: fall back to the default behaviour + + if not macver: + macver = macrelease + + if macver: + release = macver + osname = "macosx" + + if ((macrelease + '.') >= '10.4.' and + '-arch' in get_config_vars().get('CFLAGS', '').strip()): + # The universal build will build fat binaries, but not on + # systems before 10.4 + # + # Try to detect 4-way universal builds, those have machine-type + # 'universal' instead of 'fat'. + + machine = 'fat' + cflags = get_config_vars().get('CFLAGS') + + archs = re.findall('-arch\s+(\S+)', cflags) + archs = tuple(sorted(set(archs))) + + if len(archs) == 1: + machine = archs[0] + elif archs == ('i386', 'ppc'): + machine = 'fat' + elif archs == ('i386', 'x86_64'): + machine = 'intel' + elif archs == ('i386', 'ppc', 'x86_64'): + machine = 'fat3' + elif archs == ('ppc64', 'x86_64'): + machine = 'fat64' + elif archs == ('i386', 'ppc', 'ppc64', 'x86_64'): + machine = 'universal' + else: + raise ValueError( + "Don't know machine value for archs=%r" % (archs,)) + + elif machine == 'i386': + # On OSX the machine type returned by uname is always the + # 32-bit variant, even if the executable architecture is + # the 64-bit variant + if sys.maxsize >= 2**32: + machine = 'x86_64' + + elif machine in ('PowerPC', 'Power_Macintosh'): + # Pick a sane name for the PPC architecture. + # See 'i386' case + if sys.maxsize >= 2**32: + machine = 'ppc64' + else: + machine = 'ppc' + + return "%s-%s-%s" % (osname, release, machine) + + +def get_python_version(): + return _PY_VERSION_SHORT + + +def _print_dict(title, data): + for index, (key, value) in enumerate(sorted(data.items())): + if index == 0: + print('%s: ' % (title)) + print('\t%s = "%s"' % (key, value)) + + +def _main(): + """Display all information sysconfig detains.""" + print('Platform: "%s"' % get_platform()) + print('Python version: "%s"' % get_python_version()) + print('Current installation scheme: "%s"' % _get_default_scheme()) + print() + _print_dict('Paths', get_paths()) + print() + _print_dict('Variables', get_config_vars()) + + +if __name__ == '__main__': + _main() diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/tarfile.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/tarfile.py new file mode 100644 index 0000000..0580fb7 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/tarfile.py @@ -0,0 +1,2607 @@ +#------------------------------------------------------------------- +# tarfile.py +#------------------------------------------------------------------- +# Copyright (C) 2002 Lars Gustaebel +# All rights reserved. +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, +# copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following +# conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +from __future__ import print_function + +"""Read from and write to tar format archives. +""" + +__version__ = "$Revision$" + +version = "0.9.0" +__author__ = "Lars Gust\u00e4bel (lars@gustaebel.de)" +__date__ = "$Date: 2011-02-25 17:42:01 +0200 (Fri, 25 Feb 2011) $" +__cvsid__ = "$Id: tarfile.py 88586 2011-02-25 15:42:01Z marc-andre.lemburg $" +__credits__ = "Gustavo Niemeyer, Niels Gust\u00e4bel, Richard Townsend." + +#--------- +# Imports +#--------- +import sys +import os +import stat +import errno +import time +import struct +import copy +import re + +try: + import grp, pwd +except ImportError: + grp = pwd = None + +# os.symlink on Windows prior to 6.0 raises NotImplementedError +symlink_exception = (AttributeError, NotImplementedError) +try: + # WindowsError (1314) will be raised if the caller does not hold the + # SeCreateSymbolicLinkPrivilege privilege + symlink_exception += (WindowsError,) +except NameError: + pass + +# from tarfile import * +__all__ = ["TarFile", "TarInfo", "is_tarfile", "TarError"] + +if sys.version_info[0] < 3: + import __builtin__ as builtins +else: + import builtins + +_open = builtins.open # Since 'open' is TarFile.open + +#--------------------------------------------------------- +# tar constants +#--------------------------------------------------------- +NUL = b"\0" # the null character +BLOCKSIZE = 512 # length of processing blocks +RECORDSIZE = BLOCKSIZE * 20 # length of records +GNU_MAGIC = b"ustar \0" # magic gnu tar string +POSIX_MAGIC = b"ustar\x0000" # magic posix tar string + +LENGTH_NAME = 100 # maximum length of a filename +LENGTH_LINK = 100 # maximum length of a linkname +LENGTH_PREFIX = 155 # maximum length of the prefix field + +REGTYPE = b"0" # regular file +AREGTYPE = b"\0" # regular file +LNKTYPE = b"1" # link (inside tarfile) +SYMTYPE = b"2" # symbolic link +CHRTYPE = b"3" # character special device +BLKTYPE = b"4" # block special device +DIRTYPE = b"5" # directory +FIFOTYPE = b"6" # fifo special device +CONTTYPE = b"7" # contiguous file + +GNUTYPE_LONGNAME = b"L" # GNU tar longname +GNUTYPE_LONGLINK = b"K" # GNU tar longlink +GNUTYPE_SPARSE = b"S" # GNU tar sparse file + +XHDTYPE = b"x" # POSIX.1-2001 extended header +XGLTYPE = b"g" # POSIX.1-2001 global header +SOLARIS_XHDTYPE = b"X" # Solaris extended header + +USTAR_FORMAT = 0 # POSIX.1-1988 (ustar) format +GNU_FORMAT = 1 # GNU tar format +PAX_FORMAT = 2 # POSIX.1-2001 (pax) format +DEFAULT_FORMAT = GNU_FORMAT + +#--------------------------------------------------------- +# tarfile constants +#--------------------------------------------------------- +# File types that tarfile supports: +SUPPORTED_TYPES = (REGTYPE, AREGTYPE, LNKTYPE, + SYMTYPE, DIRTYPE, FIFOTYPE, + CONTTYPE, CHRTYPE, BLKTYPE, + GNUTYPE_LONGNAME, GNUTYPE_LONGLINK, + GNUTYPE_SPARSE) + +# File types that will be treated as a regular file. +REGULAR_TYPES = (REGTYPE, AREGTYPE, + CONTTYPE, GNUTYPE_SPARSE) + +# File types that are part of the GNU tar format. +GNU_TYPES = (GNUTYPE_LONGNAME, GNUTYPE_LONGLINK, + GNUTYPE_SPARSE) + +# Fields from a pax header that override a TarInfo attribute. +PAX_FIELDS = ("path", "linkpath", "size", "mtime", + "uid", "gid", "uname", "gname") + +# Fields from a pax header that are affected by hdrcharset. +PAX_NAME_FIELDS = set(("path", "linkpath", "uname", "gname")) + +# Fields in a pax header that are numbers, all other fields +# are treated as strings. +PAX_NUMBER_FIELDS = { + "atime": float, + "ctime": float, + "mtime": float, + "uid": int, + "gid": int, + "size": int +} + +#--------------------------------------------------------- +# Bits used in the mode field, values in octal. +#--------------------------------------------------------- +S_IFLNK = 0o120000 # symbolic link +S_IFREG = 0o100000 # regular file +S_IFBLK = 0o060000 # block device +S_IFDIR = 0o040000 # directory +S_IFCHR = 0o020000 # character device +S_IFIFO = 0o010000 # fifo + +TSUID = 0o4000 # set UID on execution +TSGID = 0o2000 # set GID on execution +TSVTX = 0o1000 # reserved + +TUREAD = 0o400 # read by owner +TUWRITE = 0o200 # write by owner +TUEXEC = 0o100 # execute/search by owner +TGREAD = 0o040 # read by group +TGWRITE = 0o020 # write by group +TGEXEC = 0o010 # execute/search by group +TOREAD = 0o004 # read by other +TOWRITE = 0o002 # write by other +TOEXEC = 0o001 # execute/search by other + +#--------------------------------------------------------- +# initialization +#--------------------------------------------------------- +if os.name in ("nt", "ce"): + ENCODING = "utf-8" +else: + ENCODING = sys.getfilesystemencoding() + +#--------------------------------------------------------- +# Some useful functions +#--------------------------------------------------------- + +def stn(s, length, encoding, errors): + """Convert a string to a null-terminated bytes object. + """ + s = s.encode(encoding, errors) + return s[:length] + (length - len(s)) * NUL + +def nts(s, encoding, errors): + """Convert a null-terminated bytes object to a string. + """ + p = s.find(b"\0") + if p != -1: + s = s[:p] + return s.decode(encoding, errors) + +def nti(s): + """Convert a number field to a python number. + """ + # There are two possible encodings for a number field, see + # itn() below. + if s[0] != chr(0o200): + try: + n = int(nts(s, "ascii", "strict") or "0", 8) + except ValueError: + raise InvalidHeaderError("invalid header") + else: + n = 0 + for i in range(len(s) - 1): + n <<= 8 + n += ord(s[i + 1]) + return n + +def itn(n, digits=8, format=DEFAULT_FORMAT): + """Convert a python number to a number field. + """ + # POSIX 1003.1-1988 requires numbers to be encoded as a string of + # octal digits followed by a null-byte, this allows values up to + # (8**(digits-1))-1. GNU tar allows storing numbers greater than + # that if necessary. A leading 0o200 byte indicates this particular + # encoding, the following digits-1 bytes are a big-endian + # representation. This allows values up to (256**(digits-1))-1. + if 0 <= n < 8 ** (digits - 1): + s = ("%0*o" % (digits - 1, n)).encode("ascii") + NUL + else: + if format != GNU_FORMAT or n >= 256 ** (digits - 1): + raise ValueError("overflow in number field") + + if n < 0: + # XXX We mimic GNU tar's behaviour with negative numbers, + # this could raise OverflowError. + n = struct.unpack("L", struct.pack("l", n))[0] + + s = bytearray() + for i in range(digits - 1): + s.insert(0, n & 0o377) + n >>= 8 + s.insert(0, 0o200) + return s + +def calc_chksums(buf): + """Calculate the checksum for a member's header by summing up all + characters except for the chksum field which is treated as if + it was filled with spaces. According to the GNU tar sources, + some tars (Sun and NeXT) calculate chksum with signed char, + which will be different if there are chars in the buffer with + the high bit set. So we calculate two checksums, unsigned and + signed. + """ + unsigned_chksum = 256 + sum(struct.unpack("148B", buf[:148]) + struct.unpack("356B", buf[156:512])) + signed_chksum = 256 + sum(struct.unpack("148b", buf[:148]) + struct.unpack("356b", buf[156:512])) + return unsigned_chksum, signed_chksum + +def copyfileobj(src, dst, length=None): + """Copy length bytes from fileobj src to fileobj dst. + If length is None, copy the entire content. + """ + if length == 0: + return + if length is None: + while True: + buf = src.read(16*1024) + if not buf: + break + dst.write(buf) + return + + BUFSIZE = 16 * 1024 + blocks, remainder = divmod(length, BUFSIZE) + for b in range(blocks): + buf = src.read(BUFSIZE) + if len(buf) < BUFSIZE: + raise IOError("end of file reached") + dst.write(buf) + + if remainder != 0: + buf = src.read(remainder) + if len(buf) < remainder: + raise IOError("end of file reached") + dst.write(buf) + return + +filemode_table = ( + ((S_IFLNK, "l"), + (S_IFREG, "-"), + (S_IFBLK, "b"), + (S_IFDIR, "d"), + (S_IFCHR, "c"), + (S_IFIFO, "p")), + + ((TUREAD, "r"),), + ((TUWRITE, "w"),), + ((TUEXEC|TSUID, "s"), + (TSUID, "S"), + (TUEXEC, "x")), + + ((TGREAD, "r"),), + ((TGWRITE, "w"),), + ((TGEXEC|TSGID, "s"), + (TSGID, "S"), + (TGEXEC, "x")), + + ((TOREAD, "r"),), + ((TOWRITE, "w"),), + ((TOEXEC|TSVTX, "t"), + (TSVTX, "T"), + (TOEXEC, "x")) +) + +def filemode(mode): + """Convert a file's mode to a string of the form + -rwxrwxrwx. + Used by TarFile.list() + """ + perm = [] + for table in filemode_table: + for bit, char in table: + if mode & bit == bit: + perm.append(char) + break + else: + perm.append("-") + return "".join(perm) + +class TarError(Exception): + """Base exception.""" + pass +class ExtractError(TarError): + """General exception for extract errors.""" + pass +class ReadError(TarError): + """Exception for unreadble tar archives.""" + pass +class CompressionError(TarError): + """Exception for unavailable compression methods.""" + pass +class StreamError(TarError): + """Exception for unsupported operations on stream-like TarFiles.""" + pass +class HeaderError(TarError): + """Base exception for header errors.""" + pass +class EmptyHeaderError(HeaderError): + """Exception for empty headers.""" + pass +class TruncatedHeaderError(HeaderError): + """Exception for truncated headers.""" + pass +class EOFHeaderError(HeaderError): + """Exception for end of file headers.""" + pass +class InvalidHeaderError(HeaderError): + """Exception for invalid headers.""" + pass +class SubsequentHeaderError(HeaderError): + """Exception for missing and invalid extended headers.""" + pass + +#--------------------------- +# internal stream interface +#--------------------------- +class _LowLevelFile(object): + """Low-level file object. Supports reading and writing. + It is used instead of a regular file object for streaming + access. + """ + + def __init__(self, name, mode): + mode = { + "r": os.O_RDONLY, + "w": os.O_WRONLY | os.O_CREAT | os.O_TRUNC, + }[mode] + if hasattr(os, "O_BINARY"): + mode |= os.O_BINARY + self.fd = os.open(name, mode, 0o666) + + def close(self): + os.close(self.fd) + + def read(self, size): + return os.read(self.fd, size) + + def write(self, s): + os.write(self.fd, s) + +class _Stream(object): + """Class that serves as an adapter between TarFile and + a stream-like object. The stream-like object only + needs to have a read() or write() method and is accessed + blockwise. Use of gzip or bzip2 compression is possible. + A stream-like object could be for example: sys.stdin, + sys.stdout, a socket, a tape device etc. + + _Stream is intended to be used only internally. + """ + + def __init__(self, name, mode, comptype, fileobj, bufsize): + """Construct a _Stream object. + """ + self._extfileobj = True + if fileobj is None: + fileobj = _LowLevelFile(name, mode) + self._extfileobj = False + + if comptype == '*': + # Enable transparent compression detection for the + # stream interface + fileobj = _StreamProxy(fileobj) + comptype = fileobj.getcomptype() + + self.name = name or "" + self.mode = mode + self.comptype = comptype + self.fileobj = fileobj + self.bufsize = bufsize + self.buf = b"" + self.pos = 0 + self.closed = False + + try: + if comptype == "gz": + try: + import zlib + except ImportError: + raise CompressionError("zlib module is not available") + self.zlib = zlib + self.crc = zlib.crc32(b"") + if mode == "r": + self._init_read_gz() + else: + self._init_write_gz() + + if comptype == "bz2": + try: + import bz2 + except ImportError: + raise CompressionError("bz2 module is not available") + if mode == "r": + self.dbuf = b"" + self.cmp = bz2.BZ2Decompressor() + else: + self.cmp = bz2.BZ2Compressor() + except: + if not self._extfileobj: + self.fileobj.close() + self.closed = True + raise + + def __del__(self): + if hasattr(self, "closed") and not self.closed: + self.close() + + def _init_write_gz(self): + """Initialize for writing with gzip compression. + """ + self.cmp = self.zlib.compressobj(9, self.zlib.DEFLATED, + -self.zlib.MAX_WBITS, + self.zlib.DEF_MEM_LEVEL, + 0) + timestamp = struct.pack(" self.bufsize: + self.fileobj.write(self.buf[:self.bufsize]) + self.buf = self.buf[self.bufsize:] + + def close(self): + """Close the _Stream object. No operation should be + done on it afterwards. + """ + if self.closed: + return + + if self.mode == "w" and self.comptype != "tar": + self.buf += self.cmp.flush() + + if self.mode == "w" and self.buf: + self.fileobj.write(self.buf) + self.buf = b"" + if self.comptype == "gz": + # The native zlib crc is an unsigned 32-bit integer, but + # the Python wrapper implicitly casts that to a signed C + # long. So, on a 32-bit box self.crc may "look negative", + # while the same crc on a 64-bit box may "look positive". + # To avoid irksome warnings from the `struct` module, force + # it to look positive on all boxes. + self.fileobj.write(struct.pack("= 0: + blocks, remainder = divmod(pos - self.pos, self.bufsize) + for i in range(blocks): + self.read(self.bufsize) + self.read(remainder) + else: + raise StreamError("seeking backwards is not allowed") + return self.pos + + def read(self, size=None): + """Return the next size number of bytes from the stream. + If size is not defined, return all bytes of the stream + up to EOF. + """ + if size is None: + t = [] + while True: + buf = self._read(self.bufsize) + if not buf: + break + t.append(buf) + buf = "".join(t) + else: + buf = self._read(size) + self.pos += len(buf) + return buf + + def _read(self, size): + """Return size bytes from the stream. + """ + if self.comptype == "tar": + return self.__read(size) + + c = len(self.dbuf) + while c < size: + buf = self.__read(self.bufsize) + if not buf: + break + try: + buf = self.cmp.decompress(buf) + except IOError: + raise ReadError("invalid compressed data") + self.dbuf += buf + c += len(buf) + buf = self.dbuf[:size] + self.dbuf = self.dbuf[size:] + return buf + + def __read(self, size): + """Return size bytes from stream. If internal buffer is empty, + read another block from the stream. + """ + c = len(self.buf) + while c < size: + buf = self.fileobj.read(self.bufsize) + if not buf: + break + self.buf += buf + c += len(buf) + buf = self.buf[:size] + self.buf = self.buf[size:] + return buf +# class _Stream + +class _StreamProxy(object): + """Small proxy class that enables transparent compression + detection for the Stream interface (mode 'r|*'). + """ + + def __init__(self, fileobj): + self.fileobj = fileobj + self.buf = self.fileobj.read(BLOCKSIZE) + + def read(self, size): + self.read = self.fileobj.read + return self.buf + + def getcomptype(self): + if self.buf.startswith(b"\037\213\010"): + return "gz" + if self.buf.startswith(b"BZh91"): + return "bz2" + return "tar" + + def close(self): + self.fileobj.close() +# class StreamProxy + +class _BZ2Proxy(object): + """Small proxy class that enables external file object + support for "r:bz2" and "w:bz2" modes. This is actually + a workaround for a limitation in bz2 module's BZ2File + class which (unlike gzip.GzipFile) has no support for + a file object argument. + """ + + blocksize = 16 * 1024 + + def __init__(self, fileobj, mode): + self.fileobj = fileobj + self.mode = mode + self.name = getattr(self.fileobj, "name", None) + self.init() + + def init(self): + import bz2 + self.pos = 0 + if self.mode == "r": + self.bz2obj = bz2.BZ2Decompressor() + self.fileobj.seek(0) + self.buf = b"" + else: + self.bz2obj = bz2.BZ2Compressor() + + def read(self, size): + x = len(self.buf) + while x < size: + raw = self.fileobj.read(self.blocksize) + if not raw: + break + data = self.bz2obj.decompress(raw) + self.buf += data + x += len(data) + + buf = self.buf[:size] + self.buf = self.buf[size:] + self.pos += len(buf) + return buf + + def seek(self, pos): + if pos < self.pos: + self.init() + self.read(pos - self.pos) + + def tell(self): + return self.pos + + def write(self, data): + self.pos += len(data) + raw = self.bz2obj.compress(data) + self.fileobj.write(raw) + + def close(self): + if self.mode == "w": + raw = self.bz2obj.flush() + self.fileobj.write(raw) +# class _BZ2Proxy + +#------------------------ +# Extraction file object +#------------------------ +class _FileInFile(object): + """A thin wrapper around an existing file object that + provides a part of its data as an individual file + object. + """ + + def __init__(self, fileobj, offset, size, blockinfo=None): + self.fileobj = fileobj + self.offset = offset + self.size = size + self.position = 0 + + if blockinfo is None: + blockinfo = [(0, size)] + + # Construct a map with data and zero blocks. + self.map_index = 0 + self.map = [] + lastpos = 0 + realpos = self.offset + for offset, size in blockinfo: + if offset > lastpos: + self.map.append((False, lastpos, offset, None)) + self.map.append((True, offset, offset + size, realpos)) + realpos += size + lastpos = offset + size + if lastpos < self.size: + self.map.append((False, lastpos, self.size, None)) + + def seekable(self): + if not hasattr(self.fileobj, "seekable"): + # XXX gzip.GzipFile and bz2.BZ2File + return True + return self.fileobj.seekable() + + def tell(self): + """Return the current file position. + """ + return self.position + + def seek(self, position): + """Seek to a position in the file. + """ + self.position = position + + def read(self, size=None): + """Read data from the file. + """ + if size is None: + size = self.size - self.position + else: + size = min(size, self.size - self.position) + + buf = b"" + while size > 0: + while True: + data, start, stop, offset = self.map[self.map_index] + if start <= self.position < stop: + break + else: + self.map_index += 1 + if self.map_index == len(self.map): + self.map_index = 0 + length = min(size, stop - self.position) + if data: + self.fileobj.seek(offset + (self.position - start)) + buf += self.fileobj.read(length) + else: + buf += NUL * length + size -= length + self.position += length + return buf +#class _FileInFile + + +class ExFileObject(object): + """File-like object for reading an archive member. + Is returned by TarFile.extractfile(). + """ + blocksize = 1024 + + def __init__(self, tarfile, tarinfo): + self.fileobj = _FileInFile(tarfile.fileobj, + tarinfo.offset_data, + tarinfo.size, + tarinfo.sparse) + self.name = tarinfo.name + self.mode = "r" + self.closed = False + self.size = tarinfo.size + + self.position = 0 + self.buffer = b"" + + def readable(self): + return True + + def writable(self): + return False + + def seekable(self): + return self.fileobj.seekable() + + def read(self, size=None): + """Read at most size bytes from the file. If size is not + present or None, read all data until EOF is reached. + """ + if self.closed: + raise ValueError("I/O operation on closed file") + + buf = b"" + if self.buffer: + if size is None: + buf = self.buffer + self.buffer = b"" + else: + buf = self.buffer[:size] + self.buffer = self.buffer[size:] + + if size is None: + buf += self.fileobj.read() + else: + buf += self.fileobj.read(size - len(buf)) + + self.position += len(buf) + return buf + + # XXX TextIOWrapper uses the read1() method. + read1 = read + + def readline(self, size=-1): + """Read one entire line from the file. If size is present + and non-negative, return a string with at most that + size, which may be an incomplete line. + """ + if self.closed: + raise ValueError("I/O operation on closed file") + + pos = self.buffer.find(b"\n") + 1 + if pos == 0: + # no newline found. + while True: + buf = self.fileobj.read(self.blocksize) + self.buffer += buf + if not buf or b"\n" in buf: + pos = self.buffer.find(b"\n") + 1 + if pos == 0: + # no newline found. + pos = len(self.buffer) + break + + if size != -1: + pos = min(size, pos) + + buf = self.buffer[:pos] + self.buffer = self.buffer[pos:] + self.position += len(buf) + return buf + + def readlines(self): + """Return a list with all remaining lines. + """ + result = [] + while True: + line = self.readline() + if not line: break + result.append(line) + return result + + def tell(self): + """Return the current file position. + """ + if self.closed: + raise ValueError("I/O operation on closed file") + + return self.position + + def seek(self, pos, whence=os.SEEK_SET): + """Seek to a position in the file. + """ + if self.closed: + raise ValueError("I/O operation on closed file") + + if whence == os.SEEK_SET: + self.position = min(max(pos, 0), self.size) + elif whence == os.SEEK_CUR: + if pos < 0: + self.position = max(self.position + pos, 0) + else: + self.position = min(self.position + pos, self.size) + elif whence == os.SEEK_END: + self.position = max(min(self.size + pos, self.size), 0) + else: + raise ValueError("Invalid argument") + + self.buffer = b"" + self.fileobj.seek(self.position) + + def close(self): + """Close the file object. + """ + self.closed = True + + def __iter__(self): + """Get an iterator over the file's lines. + """ + while True: + line = self.readline() + if not line: + break + yield line +#class ExFileObject + +#------------------ +# Exported Classes +#------------------ +class TarInfo(object): + """Informational class which holds the details about an + archive member given by a tar header block. + TarInfo objects are returned by TarFile.getmember(), + TarFile.getmembers() and TarFile.gettarinfo() and are + usually created internally. + """ + + __slots__ = ("name", "mode", "uid", "gid", "size", "mtime", + "chksum", "type", "linkname", "uname", "gname", + "devmajor", "devminor", + "offset", "offset_data", "pax_headers", "sparse", + "tarfile", "_sparse_structs", "_link_target") + + def __init__(self, name=""): + """Construct a TarInfo object. name is the optional name + of the member. + """ + self.name = name # member name + self.mode = 0o644 # file permissions + self.uid = 0 # user id + self.gid = 0 # group id + self.size = 0 # file size + self.mtime = 0 # modification time + self.chksum = 0 # header checksum + self.type = REGTYPE # member type + self.linkname = "" # link name + self.uname = "" # user name + self.gname = "" # group name + self.devmajor = 0 # device major number + self.devminor = 0 # device minor number + + self.offset = 0 # the tar header starts here + self.offset_data = 0 # the file's data starts here + + self.sparse = None # sparse member information + self.pax_headers = {} # pax header information + + # In pax headers the "name" and "linkname" field are called + # "path" and "linkpath". + def _getpath(self): + return self.name + def _setpath(self, name): + self.name = name + path = property(_getpath, _setpath) + + def _getlinkpath(self): + return self.linkname + def _setlinkpath(self, linkname): + self.linkname = linkname + linkpath = property(_getlinkpath, _setlinkpath) + + def __repr__(self): + return "<%s %r at %#x>" % (self.__class__.__name__,self.name,id(self)) + + def get_info(self): + """Return the TarInfo's attributes as a dictionary. + """ + info = { + "name": self.name, + "mode": self.mode & 0o7777, + "uid": self.uid, + "gid": self.gid, + "size": self.size, + "mtime": self.mtime, + "chksum": self.chksum, + "type": self.type, + "linkname": self.linkname, + "uname": self.uname, + "gname": self.gname, + "devmajor": self.devmajor, + "devminor": self.devminor + } + + if info["type"] == DIRTYPE and not info["name"].endswith("/"): + info["name"] += "/" + + return info + + def tobuf(self, format=DEFAULT_FORMAT, encoding=ENCODING, errors="surrogateescape"): + """Return a tar header as a string of 512 byte blocks. + """ + info = self.get_info() + + if format == USTAR_FORMAT: + return self.create_ustar_header(info, encoding, errors) + elif format == GNU_FORMAT: + return self.create_gnu_header(info, encoding, errors) + elif format == PAX_FORMAT: + return self.create_pax_header(info, encoding) + else: + raise ValueError("invalid format") + + def create_ustar_header(self, info, encoding, errors): + """Return the object as a ustar header block. + """ + info["magic"] = POSIX_MAGIC + + if len(info["linkname"]) > LENGTH_LINK: + raise ValueError("linkname is too long") + + if len(info["name"]) > LENGTH_NAME: + info["prefix"], info["name"] = self._posix_split_name(info["name"]) + + return self._create_header(info, USTAR_FORMAT, encoding, errors) + + def create_gnu_header(self, info, encoding, errors): + """Return the object as a GNU header block sequence. + """ + info["magic"] = GNU_MAGIC + + buf = b"" + if len(info["linkname"]) > LENGTH_LINK: + buf += self._create_gnu_long_header(info["linkname"], GNUTYPE_LONGLINK, encoding, errors) + + if len(info["name"]) > LENGTH_NAME: + buf += self._create_gnu_long_header(info["name"], GNUTYPE_LONGNAME, encoding, errors) + + return buf + self._create_header(info, GNU_FORMAT, encoding, errors) + + def create_pax_header(self, info, encoding): + """Return the object as a ustar header block. If it cannot be + represented this way, prepend a pax extended header sequence + with supplement information. + """ + info["magic"] = POSIX_MAGIC + pax_headers = self.pax_headers.copy() + + # Test string fields for values that exceed the field length or cannot + # be represented in ASCII encoding. + for name, hname, length in ( + ("name", "path", LENGTH_NAME), ("linkname", "linkpath", LENGTH_LINK), + ("uname", "uname", 32), ("gname", "gname", 32)): + + if hname in pax_headers: + # The pax header has priority. + continue + + # Try to encode the string as ASCII. + try: + info[name].encode("ascii", "strict") + except UnicodeEncodeError: + pax_headers[hname] = info[name] + continue + + if len(info[name]) > length: + pax_headers[hname] = info[name] + + # Test number fields for values that exceed the field limit or values + # that like to be stored as float. + for name, digits in (("uid", 8), ("gid", 8), ("size", 12), ("mtime", 12)): + if name in pax_headers: + # The pax header has priority. Avoid overflow. + info[name] = 0 + continue + + val = info[name] + if not 0 <= val < 8 ** (digits - 1) or isinstance(val, float): + pax_headers[name] = str(val) + info[name] = 0 + + # Create a pax extended header if necessary. + if pax_headers: + buf = self._create_pax_generic_header(pax_headers, XHDTYPE, encoding) + else: + buf = b"" + + return buf + self._create_header(info, USTAR_FORMAT, "ascii", "replace") + + @classmethod + def create_pax_global_header(cls, pax_headers): + """Return the object as a pax global header block sequence. + """ + return cls._create_pax_generic_header(pax_headers, XGLTYPE, "utf8") + + def _posix_split_name(self, name): + """Split a name longer than 100 chars into a prefix + and a name part. + """ + prefix = name[:LENGTH_PREFIX + 1] + while prefix and prefix[-1] != "/": + prefix = prefix[:-1] + + name = name[len(prefix):] + prefix = prefix[:-1] + + if not prefix or len(name) > LENGTH_NAME: + raise ValueError("name is too long") + return prefix, name + + @staticmethod + def _create_header(info, format, encoding, errors): + """Return a header block. info is a dictionary with file + information, format must be one of the *_FORMAT constants. + """ + parts = [ + stn(info.get("name", ""), 100, encoding, errors), + itn(info.get("mode", 0) & 0o7777, 8, format), + itn(info.get("uid", 0), 8, format), + itn(info.get("gid", 0), 8, format), + itn(info.get("size", 0), 12, format), + itn(info.get("mtime", 0), 12, format), + b" ", # checksum field + info.get("type", REGTYPE), + stn(info.get("linkname", ""), 100, encoding, errors), + info.get("magic", POSIX_MAGIC), + stn(info.get("uname", ""), 32, encoding, errors), + stn(info.get("gname", ""), 32, encoding, errors), + itn(info.get("devmajor", 0), 8, format), + itn(info.get("devminor", 0), 8, format), + stn(info.get("prefix", ""), 155, encoding, errors) + ] + + buf = struct.pack("%ds" % BLOCKSIZE, b"".join(parts)) + chksum = calc_chksums(buf[-BLOCKSIZE:])[0] + buf = buf[:-364] + ("%06o\0" % chksum).encode("ascii") + buf[-357:] + return buf + + @staticmethod + def _create_payload(payload): + """Return the string payload filled with zero bytes + up to the next 512 byte border. + """ + blocks, remainder = divmod(len(payload), BLOCKSIZE) + if remainder > 0: + payload += (BLOCKSIZE - remainder) * NUL + return payload + + @classmethod + def _create_gnu_long_header(cls, name, type, encoding, errors): + """Return a GNUTYPE_LONGNAME or GNUTYPE_LONGLINK sequence + for name. + """ + name = name.encode(encoding, errors) + NUL + + info = {} + info["name"] = "././@LongLink" + info["type"] = type + info["size"] = len(name) + info["magic"] = GNU_MAGIC + + # create extended header + name blocks. + return cls._create_header(info, USTAR_FORMAT, encoding, errors) + \ + cls._create_payload(name) + + @classmethod + def _create_pax_generic_header(cls, pax_headers, type, encoding): + """Return a POSIX.1-2008 extended or global header sequence + that contains a list of keyword, value pairs. The values + must be strings. + """ + # Check if one of the fields contains surrogate characters and thereby + # forces hdrcharset=BINARY, see _proc_pax() for more information. + binary = False + for keyword, value in pax_headers.items(): + try: + value.encode("utf8", "strict") + except UnicodeEncodeError: + binary = True + break + + records = b"" + if binary: + # Put the hdrcharset field at the beginning of the header. + records += b"21 hdrcharset=BINARY\n" + + for keyword, value in pax_headers.items(): + keyword = keyword.encode("utf8") + if binary: + # Try to restore the original byte representation of `value'. + # Needless to say, that the encoding must match the string. + value = value.encode(encoding, "surrogateescape") + else: + value = value.encode("utf8") + + l = len(keyword) + len(value) + 3 # ' ' + '=' + '\n' + n = p = 0 + while True: + n = l + len(str(p)) + if n == p: + break + p = n + records += bytes(str(p), "ascii") + b" " + keyword + b"=" + value + b"\n" + + # We use a hardcoded "././@PaxHeader" name like star does + # instead of the one that POSIX recommends. + info = {} + info["name"] = "././@PaxHeader" + info["type"] = type + info["size"] = len(records) + info["magic"] = POSIX_MAGIC + + # Create pax header + record blocks. + return cls._create_header(info, USTAR_FORMAT, "ascii", "replace") + \ + cls._create_payload(records) + + @classmethod + def frombuf(cls, buf, encoding, errors): + """Construct a TarInfo object from a 512 byte bytes object. + """ + if len(buf) == 0: + raise EmptyHeaderError("empty header") + if len(buf) != BLOCKSIZE: + raise TruncatedHeaderError("truncated header") + if buf.count(NUL) == BLOCKSIZE: + raise EOFHeaderError("end of file header") + + chksum = nti(buf[148:156]) + if chksum not in calc_chksums(buf): + raise InvalidHeaderError("bad checksum") + + obj = cls() + obj.name = nts(buf[0:100], encoding, errors) + obj.mode = nti(buf[100:108]) + obj.uid = nti(buf[108:116]) + obj.gid = nti(buf[116:124]) + obj.size = nti(buf[124:136]) + obj.mtime = nti(buf[136:148]) + obj.chksum = chksum + obj.type = buf[156:157] + obj.linkname = nts(buf[157:257], encoding, errors) + obj.uname = nts(buf[265:297], encoding, errors) + obj.gname = nts(buf[297:329], encoding, errors) + obj.devmajor = nti(buf[329:337]) + obj.devminor = nti(buf[337:345]) + prefix = nts(buf[345:500], encoding, errors) + + # Old V7 tar format represents a directory as a regular + # file with a trailing slash. + if obj.type == AREGTYPE and obj.name.endswith("/"): + obj.type = DIRTYPE + + # The old GNU sparse format occupies some of the unused + # space in the buffer for up to 4 sparse structures. + # Save the them for later processing in _proc_sparse(). + if obj.type == GNUTYPE_SPARSE: + pos = 386 + structs = [] + for i in range(4): + try: + offset = nti(buf[pos:pos + 12]) + numbytes = nti(buf[pos + 12:pos + 24]) + except ValueError: + break + structs.append((offset, numbytes)) + pos += 24 + isextended = bool(buf[482]) + origsize = nti(buf[483:495]) + obj._sparse_structs = (structs, isextended, origsize) + + # Remove redundant slashes from directories. + if obj.isdir(): + obj.name = obj.name.rstrip("/") + + # Reconstruct a ustar longname. + if prefix and obj.type not in GNU_TYPES: + obj.name = prefix + "/" + obj.name + return obj + + @classmethod + def fromtarfile(cls, tarfile): + """Return the next TarInfo object from TarFile object + tarfile. + """ + buf = tarfile.fileobj.read(BLOCKSIZE) + obj = cls.frombuf(buf, tarfile.encoding, tarfile.errors) + obj.offset = tarfile.fileobj.tell() - BLOCKSIZE + return obj._proc_member(tarfile) + + #-------------------------------------------------------------------------- + # The following are methods that are called depending on the type of a + # member. The entry point is _proc_member() which can be overridden in a + # subclass to add custom _proc_*() methods. A _proc_*() method MUST + # implement the following + # operations: + # 1. Set self.offset_data to the position where the data blocks begin, + # if there is data that follows. + # 2. Set tarfile.offset to the position where the next member's header will + # begin. + # 3. Return self or another valid TarInfo object. + def _proc_member(self, tarfile): + """Choose the right processing method depending on + the type and call it. + """ + if self.type in (GNUTYPE_LONGNAME, GNUTYPE_LONGLINK): + return self._proc_gnulong(tarfile) + elif self.type == GNUTYPE_SPARSE: + return self._proc_sparse(tarfile) + elif self.type in (XHDTYPE, XGLTYPE, SOLARIS_XHDTYPE): + return self._proc_pax(tarfile) + else: + return self._proc_builtin(tarfile) + + def _proc_builtin(self, tarfile): + """Process a builtin type or an unknown type which + will be treated as a regular file. + """ + self.offset_data = tarfile.fileobj.tell() + offset = self.offset_data + if self.isreg() or self.type not in SUPPORTED_TYPES: + # Skip the following data blocks. + offset += self._block(self.size) + tarfile.offset = offset + + # Patch the TarInfo object with saved global + # header information. + self._apply_pax_info(tarfile.pax_headers, tarfile.encoding, tarfile.errors) + + return self + + def _proc_gnulong(self, tarfile): + """Process the blocks that hold a GNU longname + or longlink member. + """ + buf = tarfile.fileobj.read(self._block(self.size)) + + # Fetch the next header and process it. + try: + next = self.fromtarfile(tarfile) + except HeaderError: + raise SubsequentHeaderError("missing or bad subsequent header") + + # Patch the TarInfo object from the next header with + # the longname information. + next.offset = self.offset + if self.type == GNUTYPE_LONGNAME: + next.name = nts(buf, tarfile.encoding, tarfile.errors) + elif self.type == GNUTYPE_LONGLINK: + next.linkname = nts(buf, tarfile.encoding, tarfile.errors) + + return next + + def _proc_sparse(self, tarfile): + """Process a GNU sparse header plus extra headers. + """ + # We already collected some sparse structures in frombuf(). + structs, isextended, origsize = self._sparse_structs + del self._sparse_structs + + # Collect sparse structures from extended header blocks. + while isextended: + buf = tarfile.fileobj.read(BLOCKSIZE) + pos = 0 + for i in range(21): + try: + offset = nti(buf[pos:pos + 12]) + numbytes = nti(buf[pos + 12:pos + 24]) + except ValueError: + break + if offset and numbytes: + structs.append((offset, numbytes)) + pos += 24 + isextended = bool(buf[504]) + self.sparse = structs + + self.offset_data = tarfile.fileobj.tell() + tarfile.offset = self.offset_data + self._block(self.size) + self.size = origsize + return self + + def _proc_pax(self, tarfile): + """Process an extended or global header as described in + POSIX.1-2008. + """ + # Read the header information. + buf = tarfile.fileobj.read(self._block(self.size)) + + # A pax header stores supplemental information for either + # the following file (extended) or all following files + # (global). + if self.type == XGLTYPE: + pax_headers = tarfile.pax_headers + else: + pax_headers = tarfile.pax_headers.copy() + + # Check if the pax header contains a hdrcharset field. This tells us + # the encoding of the path, linkpath, uname and gname fields. Normally, + # these fields are UTF-8 encoded but since POSIX.1-2008 tar + # implementations are allowed to store them as raw binary strings if + # the translation to UTF-8 fails. + match = re.search(br"\d+ hdrcharset=([^\n]+)\n", buf) + if match is not None: + pax_headers["hdrcharset"] = match.group(1).decode("utf8") + + # For the time being, we don't care about anything other than "BINARY". + # The only other value that is currently allowed by the standard is + # "ISO-IR 10646 2000 UTF-8" in other words UTF-8. + hdrcharset = pax_headers.get("hdrcharset") + if hdrcharset == "BINARY": + encoding = tarfile.encoding + else: + encoding = "utf8" + + # Parse pax header information. A record looks like that: + # "%d %s=%s\n" % (length, keyword, value). length is the size + # of the complete record including the length field itself and + # the newline. keyword and value are both UTF-8 encoded strings. + regex = re.compile(br"(\d+) ([^=]+)=") + pos = 0 + while True: + match = regex.match(buf, pos) + if not match: + break + + length, keyword = match.groups() + length = int(length) + value = buf[match.end(2) + 1:match.start(1) + length - 1] + + # Normally, we could just use "utf8" as the encoding and "strict" + # as the error handler, but we better not take the risk. For + # example, GNU tar <= 1.23 is known to store filenames it cannot + # translate to UTF-8 as raw strings (unfortunately without a + # hdrcharset=BINARY header). + # We first try the strict standard encoding, and if that fails we + # fall back on the user's encoding and error handler. + keyword = self._decode_pax_field(keyword, "utf8", "utf8", + tarfile.errors) + if keyword in PAX_NAME_FIELDS: + value = self._decode_pax_field(value, encoding, tarfile.encoding, + tarfile.errors) + else: + value = self._decode_pax_field(value, "utf8", "utf8", + tarfile.errors) + + pax_headers[keyword] = value + pos += length + + # Fetch the next header. + try: + next = self.fromtarfile(tarfile) + except HeaderError: + raise SubsequentHeaderError("missing or bad subsequent header") + + # Process GNU sparse information. + if "GNU.sparse.map" in pax_headers: + # GNU extended sparse format version 0.1. + self._proc_gnusparse_01(next, pax_headers) + + elif "GNU.sparse.size" in pax_headers: + # GNU extended sparse format version 0.0. + self._proc_gnusparse_00(next, pax_headers, buf) + + elif pax_headers.get("GNU.sparse.major") == "1" and pax_headers.get("GNU.sparse.minor") == "0": + # GNU extended sparse format version 1.0. + self._proc_gnusparse_10(next, pax_headers, tarfile) + + if self.type in (XHDTYPE, SOLARIS_XHDTYPE): + # Patch the TarInfo object with the extended header info. + next._apply_pax_info(pax_headers, tarfile.encoding, tarfile.errors) + next.offset = self.offset + + if "size" in pax_headers: + # If the extended header replaces the size field, + # we need to recalculate the offset where the next + # header starts. + offset = next.offset_data + if next.isreg() or next.type not in SUPPORTED_TYPES: + offset += next._block(next.size) + tarfile.offset = offset + + return next + + def _proc_gnusparse_00(self, next, pax_headers, buf): + """Process a GNU tar extended sparse header, version 0.0. + """ + offsets = [] + for match in re.finditer(br"\d+ GNU.sparse.offset=(\d+)\n", buf): + offsets.append(int(match.group(1))) + numbytes = [] + for match in re.finditer(br"\d+ GNU.sparse.numbytes=(\d+)\n", buf): + numbytes.append(int(match.group(1))) + next.sparse = list(zip(offsets, numbytes)) + + def _proc_gnusparse_01(self, next, pax_headers): + """Process a GNU tar extended sparse header, version 0.1. + """ + sparse = [int(x) for x in pax_headers["GNU.sparse.map"].split(",")] + next.sparse = list(zip(sparse[::2], sparse[1::2])) + + def _proc_gnusparse_10(self, next, pax_headers, tarfile): + """Process a GNU tar extended sparse header, version 1.0. + """ + fields = None + sparse = [] + buf = tarfile.fileobj.read(BLOCKSIZE) + fields, buf = buf.split(b"\n", 1) + fields = int(fields) + while len(sparse) < fields * 2: + if b"\n" not in buf: + buf += tarfile.fileobj.read(BLOCKSIZE) + number, buf = buf.split(b"\n", 1) + sparse.append(int(number)) + next.offset_data = tarfile.fileobj.tell() + next.sparse = list(zip(sparse[::2], sparse[1::2])) + + def _apply_pax_info(self, pax_headers, encoding, errors): + """Replace fields with supplemental information from a previous + pax extended or global header. + """ + for keyword, value in pax_headers.items(): + if keyword == "GNU.sparse.name": + setattr(self, "path", value) + elif keyword == "GNU.sparse.size": + setattr(self, "size", int(value)) + elif keyword == "GNU.sparse.realsize": + setattr(self, "size", int(value)) + elif keyword in PAX_FIELDS: + if keyword in PAX_NUMBER_FIELDS: + try: + value = PAX_NUMBER_FIELDS[keyword](value) + except ValueError: + value = 0 + if keyword == "path": + value = value.rstrip("/") + setattr(self, keyword, value) + + self.pax_headers = pax_headers.copy() + + def _decode_pax_field(self, value, encoding, fallback_encoding, fallback_errors): + """Decode a single field from a pax record. + """ + try: + return value.decode(encoding, "strict") + except UnicodeDecodeError: + return value.decode(fallback_encoding, fallback_errors) + + def _block(self, count): + """Round up a byte count by BLOCKSIZE and return it, + e.g. _block(834) => 1024. + """ + blocks, remainder = divmod(count, BLOCKSIZE) + if remainder: + blocks += 1 + return blocks * BLOCKSIZE + + def isreg(self): + return self.type in REGULAR_TYPES + def isfile(self): + return self.isreg() + def isdir(self): + return self.type == DIRTYPE + def issym(self): + return self.type == SYMTYPE + def islnk(self): + return self.type == LNKTYPE + def ischr(self): + return self.type == CHRTYPE + def isblk(self): + return self.type == BLKTYPE + def isfifo(self): + return self.type == FIFOTYPE + def issparse(self): + return self.sparse is not None + def isdev(self): + return self.type in (CHRTYPE, BLKTYPE, FIFOTYPE) +# class TarInfo + +class TarFile(object): + """The TarFile Class provides an interface to tar archives. + """ + + debug = 0 # May be set from 0 (no msgs) to 3 (all msgs) + + dereference = False # If true, add content of linked file to the + # tar file, else the link. + + ignore_zeros = False # If true, skips empty or invalid blocks and + # continues processing. + + errorlevel = 1 # If 0, fatal errors only appear in debug + # messages (if debug >= 0). If > 0, errors + # are passed to the caller as exceptions. + + format = DEFAULT_FORMAT # The format to use when creating an archive. + + encoding = ENCODING # Encoding for 8-bit character strings. + + errors = None # Error handler for unicode conversion. + + tarinfo = TarInfo # The default TarInfo class to use. + + fileobject = ExFileObject # The default ExFileObject class to use. + + def __init__(self, name=None, mode="r", fileobj=None, format=None, + tarinfo=None, dereference=None, ignore_zeros=None, encoding=None, + errors="surrogateescape", pax_headers=None, debug=None, errorlevel=None): + """Open an (uncompressed) tar archive `name'. `mode' is either 'r' to + read from an existing archive, 'a' to append data to an existing + file or 'w' to create a new file overwriting an existing one. `mode' + defaults to 'r'. + If `fileobj' is given, it is used for reading or writing data. If it + can be determined, `mode' is overridden by `fileobj's mode. + `fileobj' is not closed, when TarFile is closed. + """ + if len(mode) > 1 or mode not in "raw": + raise ValueError("mode must be 'r', 'a' or 'w'") + self.mode = mode + self._mode = {"r": "rb", "a": "r+b", "w": "wb"}[mode] + + if not fileobj: + if self.mode == "a" and not os.path.exists(name): + # Create nonexistent files in append mode. + self.mode = "w" + self._mode = "wb" + fileobj = bltn_open(name, self._mode) + self._extfileobj = False + else: + if name is None and hasattr(fileobj, "name"): + name = fileobj.name + if hasattr(fileobj, "mode"): + self._mode = fileobj.mode + self._extfileobj = True + self.name = os.path.abspath(name) if name else None + self.fileobj = fileobj + + # Init attributes. + if format is not None: + self.format = format + if tarinfo is not None: + self.tarinfo = tarinfo + if dereference is not None: + self.dereference = dereference + if ignore_zeros is not None: + self.ignore_zeros = ignore_zeros + if encoding is not None: + self.encoding = encoding + self.errors = errors + + if pax_headers is not None and self.format == PAX_FORMAT: + self.pax_headers = pax_headers + else: + self.pax_headers = {} + + if debug is not None: + self.debug = debug + if errorlevel is not None: + self.errorlevel = errorlevel + + # Init datastructures. + self.closed = False + self.members = [] # list of members as TarInfo objects + self._loaded = False # flag if all members have been read + self.offset = self.fileobj.tell() + # current position in the archive file + self.inodes = {} # dictionary caching the inodes of + # archive members already added + + try: + if self.mode == "r": + self.firstmember = None + self.firstmember = self.next() + + if self.mode == "a": + # Move to the end of the archive, + # before the first empty block. + while True: + self.fileobj.seek(self.offset) + try: + tarinfo = self.tarinfo.fromtarfile(self) + self.members.append(tarinfo) + except EOFHeaderError: + self.fileobj.seek(self.offset) + break + except HeaderError as e: + raise ReadError(str(e)) + + if self.mode in "aw": + self._loaded = True + + if self.pax_headers: + buf = self.tarinfo.create_pax_global_header(self.pax_headers.copy()) + self.fileobj.write(buf) + self.offset += len(buf) + except: + if not self._extfileobj: + self.fileobj.close() + self.closed = True + raise + + #-------------------------------------------------------------------------- + # Below are the classmethods which act as alternate constructors to the + # TarFile class. The open() method is the only one that is needed for + # public use; it is the "super"-constructor and is able to select an + # adequate "sub"-constructor for a particular compression using the mapping + # from OPEN_METH. + # + # This concept allows one to subclass TarFile without losing the comfort of + # the super-constructor. A sub-constructor is registered and made available + # by adding it to the mapping in OPEN_METH. + + @classmethod + def open(cls, name=None, mode="r", fileobj=None, bufsize=RECORDSIZE, **kwargs): + """Open a tar archive for reading, writing or appending. Return + an appropriate TarFile class. + + mode: + 'r' or 'r:*' open for reading with transparent compression + 'r:' open for reading exclusively uncompressed + 'r:gz' open for reading with gzip compression + 'r:bz2' open for reading with bzip2 compression + 'a' or 'a:' open for appending, creating the file if necessary + 'w' or 'w:' open for writing without compression + 'w:gz' open for writing with gzip compression + 'w:bz2' open for writing with bzip2 compression + + 'r|*' open a stream of tar blocks with transparent compression + 'r|' open an uncompressed stream of tar blocks for reading + 'r|gz' open a gzip compressed stream of tar blocks + 'r|bz2' open a bzip2 compressed stream of tar blocks + 'w|' open an uncompressed stream for writing + 'w|gz' open a gzip compressed stream for writing + 'w|bz2' open a bzip2 compressed stream for writing + """ + + if not name and not fileobj: + raise ValueError("nothing to open") + + if mode in ("r", "r:*"): + # Find out which *open() is appropriate for opening the file. + for comptype in cls.OPEN_METH: + func = getattr(cls, cls.OPEN_METH[comptype]) + if fileobj is not None: + saved_pos = fileobj.tell() + try: + return func(name, "r", fileobj, **kwargs) + except (ReadError, CompressionError) as e: + if fileobj is not None: + fileobj.seek(saved_pos) + continue + raise ReadError("file could not be opened successfully") + + elif ":" in mode: + filemode, comptype = mode.split(":", 1) + filemode = filemode or "r" + comptype = comptype or "tar" + + # Select the *open() function according to + # given compression. + if comptype in cls.OPEN_METH: + func = getattr(cls, cls.OPEN_METH[comptype]) + else: + raise CompressionError("unknown compression type %r" % comptype) + return func(name, filemode, fileobj, **kwargs) + + elif "|" in mode: + filemode, comptype = mode.split("|", 1) + filemode = filemode or "r" + comptype = comptype or "tar" + + if filemode not in "rw": + raise ValueError("mode must be 'r' or 'w'") + + stream = _Stream(name, filemode, comptype, fileobj, bufsize) + try: + t = cls(name, filemode, stream, **kwargs) + except: + stream.close() + raise + t._extfileobj = False + return t + + elif mode in "aw": + return cls.taropen(name, mode, fileobj, **kwargs) + + raise ValueError("undiscernible mode") + + @classmethod + def taropen(cls, name, mode="r", fileobj=None, **kwargs): + """Open uncompressed tar archive name for reading or writing. + """ + if len(mode) > 1 or mode not in "raw": + raise ValueError("mode must be 'r', 'a' or 'w'") + return cls(name, mode, fileobj, **kwargs) + + @classmethod + def gzopen(cls, name, mode="r", fileobj=None, compresslevel=9, **kwargs): + """Open gzip compressed tar archive name for reading or writing. + Appending is not allowed. + """ + if len(mode) > 1 or mode not in "rw": + raise ValueError("mode must be 'r' or 'w'") + + try: + import gzip + gzip.GzipFile + except (ImportError, AttributeError): + raise CompressionError("gzip module is not available") + + extfileobj = fileobj is not None + try: + fileobj = gzip.GzipFile(name, mode + "b", compresslevel, fileobj) + t = cls.taropen(name, mode, fileobj, **kwargs) + except IOError: + if not extfileobj and fileobj is not None: + fileobj.close() + if fileobj is None: + raise + raise ReadError("not a gzip file") + except: + if not extfileobj and fileobj is not None: + fileobj.close() + raise + t._extfileobj = extfileobj + return t + + @classmethod + def bz2open(cls, name, mode="r", fileobj=None, compresslevel=9, **kwargs): + """Open bzip2 compressed tar archive name for reading or writing. + Appending is not allowed. + """ + if len(mode) > 1 or mode not in "rw": + raise ValueError("mode must be 'r' or 'w'.") + + try: + import bz2 + except ImportError: + raise CompressionError("bz2 module is not available") + + if fileobj is not None: + fileobj = _BZ2Proxy(fileobj, mode) + else: + fileobj = bz2.BZ2File(name, mode, compresslevel=compresslevel) + + try: + t = cls.taropen(name, mode, fileobj, **kwargs) + except (IOError, EOFError): + fileobj.close() + raise ReadError("not a bzip2 file") + t._extfileobj = False + return t + + # All *open() methods are registered here. + OPEN_METH = { + "tar": "taropen", # uncompressed tar + "gz": "gzopen", # gzip compressed tar + "bz2": "bz2open" # bzip2 compressed tar + } + + #-------------------------------------------------------------------------- + # The public methods which TarFile provides: + + def close(self): + """Close the TarFile. In write-mode, two finishing zero blocks are + appended to the archive. + """ + if self.closed: + return + + if self.mode in "aw": + self.fileobj.write(NUL * (BLOCKSIZE * 2)) + self.offset += (BLOCKSIZE * 2) + # fill up the end with zero-blocks + # (like option -b20 for tar does) + blocks, remainder = divmod(self.offset, RECORDSIZE) + if remainder > 0: + self.fileobj.write(NUL * (RECORDSIZE - remainder)) + + if not self._extfileobj: + self.fileobj.close() + self.closed = True + + def getmember(self, name): + """Return a TarInfo object for member `name'. If `name' can not be + found in the archive, KeyError is raised. If a member occurs more + than once in the archive, its last occurrence is assumed to be the + most up-to-date version. + """ + tarinfo = self._getmember(name) + if tarinfo is None: + raise KeyError("filename %r not found" % name) + return tarinfo + + def getmembers(self): + """Return the members of the archive as a list of TarInfo objects. The + list has the same order as the members in the archive. + """ + self._check() + if not self._loaded: # if we want to obtain a list of + self._load() # all members, we first have to + # scan the whole archive. + return self.members + + def getnames(self): + """Return the members of the archive as a list of their names. It has + the same order as the list returned by getmembers(). + """ + return [tarinfo.name for tarinfo in self.getmembers()] + + def gettarinfo(self, name=None, arcname=None, fileobj=None): + """Create a TarInfo object for either the file `name' or the file + object `fileobj' (using os.fstat on its file descriptor). You can + modify some of the TarInfo's attributes before you add it using + addfile(). If given, `arcname' specifies an alternative name for the + file in the archive. + """ + self._check("aw") + + # When fileobj is given, replace name by + # fileobj's real name. + if fileobj is not None: + name = fileobj.name + + # Building the name of the member in the archive. + # Backward slashes are converted to forward slashes, + # Absolute paths are turned to relative paths. + if arcname is None: + arcname = name + drv, arcname = os.path.splitdrive(arcname) + arcname = arcname.replace(os.sep, "/") + arcname = arcname.lstrip("/") + + # Now, fill the TarInfo object with + # information specific for the file. + tarinfo = self.tarinfo() + tarinfo.tarfile = self + + # Use os.stat or os.lstat, depending on platform + # and if symlinks shall be resolved. + if fileobj is None: + if hasattr(os, "lstat") and not self.dereference: + statres = os.lstat(name) + else: + statres = os.stat(name) + else: + statres = os.fstat(fileobj.fileno()) + linkname = "" + + stmd = statres.st_mode + if stat.S_ISREG(stmd): + inode = (statres.st_ino, statres.st_dev) + if not self.dereference and statres.st_nlink > 1 and \ + inode in self.inodes and arcname != self.inodes[inode]: + # Is it a hardlink to an already + # archived file? + type = LNKTYPE + linkname = self.inodes[inode] + else: + # The inode is added only if its valid. + # For win32 it is always 0. + type = REGTYPE + if inode[0]: + self.inodes[inode] = arcname + elif stat.S_ISDIR(stmd): + type = DIRTYPE + elif stat.S_ISFIFO(stmd): + type = FIFOTYPE + elif stat.S_ISLNK(stmd): + type = SYMTYPE + linkname = os.readlink(name) + elif stat.S_ISCHR(stmd): + type = CHRTYPE + elif stat.S_ISBLK(stmd): + type = BLKTYPE + else: + return None + + # Fill the TarInfo object with all + # information we can get. + tarinfo.name = arcname + tarinfo.mode = stmd + tarinfo.uid = statres.st_uid + tarinfo.gid = statres.st_gid + if type == REGTYPE: + tarinfo.size = statres.st_size + else: + tarinfo.size = 0 + tarinfo.mtime = statres.st_mtime + tarinfo.type = type + tarinfo.linkname = linkname + if pwd: + try: + tarinfo.uname = pwd.getpwuid(tarinfo.uid)[0] + except KeyError: + pass + if grp: + try: + tarinfo.gname = grp.getgrgid(tarinfo.gid)[0] + except KeyError: + pass + + if type in (CHRTYPE, BLKTYPE): + if hasattr(os, "major") and hasattr(os, "minor"): + tarinfo.devmajor = os.major(statres.st_rdev) + tarinfo.devminor = os.minor(statres.st_rdev) + return tarinfo + + def list(self, verbose=True): + """Print a table of contents to sys.stdout. If `verbose' is False, only + the names of the members are printed. If it is True, an `ls -l'-like + output is produced. + """ + self._check() + + for tarinfo in self: + if verbose: + print(filemode(tarinfo.mode), end=' ') + print("%s/%s" % (tarinfo.uname or tarinfo.uid, + tarinfo.gname or tarinfo.gid), end=' ') + if tarinfo.ischr() or tarinfo.isblk(): + print("%10s" % ("%d,%d" \ + % (tarinfo.devmajor, tarinfo.devminor)), end=' ') + else: + print("%10d" % tarinfo.size, end=' ') + print("%d-%02d-%02d %02d:%02d:%02d" \ + % time.localtime(tarinfo.mtime)[:6], end=' ') + + print(tarinfo.name + ("/" if tarinfo.isdir() else ""), end=' ') + + if verbose: + if tarinfo.issym(): + print("->", tarinfo.linkname, end=' ') + if tarinfo.islnk(): + print("link to", tarinfo.linkname, end=' ') + print() + + def add(self, name, arcname=None, recursive=True, exclude=None, filter=None): + """Add the file `name' to the archive. `name' may be any type of file + (directory, fifo, symbolic link, etc.). If given, `arcname' + specifies an alternative name for the file in the archive. + Directories are added recursively by default. This can be avoided by + setting `recursive' to False. `exclude' is a function that should + return True for each filename to be excluded. `filter' is a function + that expects a TarInfo object argument and returns the changed + TarInfo object, if it returns None the TarInfo object will be + excluded from the archive. + """ + self._check("aw") + + if arcname is None: + arcname = name + + # Exclude pathnames. + if exclude is not None: + import warnings + warnings.warn("use the filter argument instead", + DeprecationWarning, 2) + if exclude(name): + self._dbg(2, "tarfile: Excluded %r" % name) + return + + # Skip if somebody tries to archive the archive... + if self.name is not None and os.path.abspath(name) == self.name: + self._dbg(2, "tarfile: Skipped %r" % name) + return + + self._dbg(1, name) + + # Create a TarInfo object from the file. + tarinfo = self.gettarinfo(name, arcname) + + if tarinfo is None: + self._dbg(1, "tarfile: Unsupported type %r" % name) + return + + # Change or exclude the TarInfo object. + if filter is not None: + tarinfo = filter(tarinfo) + if tarinfo is None: + self._dbg(2, "tarfile: Excluded %r" % name) + return + + # Append the tar header and data to the archive. + if tarinfo.isreg(): + f = bltn_open(name, "rb") + self.addfile(tarinfo, f) + f.close() + + elif tarinfo.isdir(): + self.addfile(tarinfo) + if recursive: + for f in os.listdir(name): + self.add(os.path.join(name, f), os.path.join(arcname, f), + recursive, exclude, filter=filter) + + else: + self.addfile(tarinfo) + + def addfile(self, tarinfo, fileobj=None): + """Add the TarInfo object `tarinfo' to the archive. If `fileobj' is + given, tarinfo.size bytes are read from it and added to the archive. + You can create TarInfo objects using gettarinfo(). + On Windows platforms, `fileobj' should always be opened with mode + 'rb' to avoid irritation about the file size. + """ + self._check("aw") + + tarinfo = copy.copy(tarinfo) + + buf = tarinfo.tobuf(self.format, self.encoding, self.errors) + self.fileobj.write(buf) + self.offset += len(buf) + + # If there's data to follow, append it. + if fileobj is not None: + copyfileobj(fileobj, self.fileobj, tarinfo.size) + blocks, remainder = divmod(tarinfo.size, BLOCKSIZE) + if remainder > 0: + self.fileobj.write(NUL * (BLOCKSIZE - remainder)) + blocks += 1 + self.offset += blocks * BLOCKSIZE + + self.members.append(tarinfo) + + def extractall(self, path=".", members=None): + """Extract all members from the archive to the current working + directory and set owner, modification time and permissions on + directories afterwards. `path' specifies a different directory + to extract to. `members' is optional and must be a subset of the + list returned by getmembers(). + """ + directories = [] + + if members is None: + members = self + + for tarinfo in members: + if tarinfo.isdir(): + # Extract directories with a safe mode. + directories.append(tarinfo) + tarinfo = copy.copy(tarinfo) + tarinfo.mode = 0o700 + # Do not set_attrs directories, as we will do that further down + self.extract(tarinfo, path, set_attrs=not tarinfo.isdir()) + + # Reverse sort directories. + directories.sort(key=lambda a: a.name) + directories.reverse() + + # Set correct owner, mtime and filemode on directories. + for tarinfo in directories: + dirpath = os.path.join(path, tarinfo.name) + try: + self.chown(tarinfo, dirpath) + self.utime(tarinfo, dirpath) + self.chmod(tarinfo, dirpath) + except ExtractError as e: + if self.errorlevel > 1: + raise + else: + self._dbg(1, "tarfile: %s" % e) + + def extract(self, member, path="", set_attrs=True): + """Extract a member from the archive to the current working directory, + using its full name. Its file information is extracted as accurately + as possible. `member' may be a filename or a TarInfo object. You can + specify a different directory using `path'. File attributes (owner, + mtime, mode) are set unless `set_attrs' is False. + """ + self._check("r") + + if isinstance(member, str): + tarinfo = self.getmember(member) + else: + tarinfo = member + + # Prepare the link target for makelink(). + if tarinfo.islnk(): + tarinfo._link_target = os.path.join(path, tarinfo.linkname) + + try: + self._extract_member(tarinfo, os.path.join(path, tarinfo.name), + set_attrs=set_attrs) + except EnvironmentError as e: + if self.errorlevel > 0: + raise + else: + if e.filename is None: + self._dbg(1, "tarfile: %s" % e.strerror) + else: + self._dbg(1, "tarfile: %s %r" % (e.strerror, e.filename)) + except ExtractError as e: + if self.errorlevel > 1: + raise + else: + self._dbg(1, "tarfile: %s" % e) + + def extractfile(self, member): + """Extract a member from the archive as a file object. `member' may be + a filename or a TarInfo object. If `member' is a regular file, a + file-like object is returned. If `member' is a link, a file-like + object is constructed from the link's target. If `member' is none of + the above, None is returned. + The file-like object is read-only and provides the following + methods: read(), readline(), readlines(), seek() and tell() + """ + self._check("r") + + if isinstance(member, str): + tarinfo = self.getmember(member) + else: + tarinfo = member + + if tarinfo.isreg(): + return self.fileobject(self, tarinfo) + + elif tarinfo.type not in SUPPORTED_TYPES: + # If a member's type is unknown, it is treated as a + # regular file. + return self.fileobject(self, tarinfo) + + elif tarinfo.islnk() or tarinfo.issym(): + if isinstance(self.fileobj, _Stream): + # A small but ugly workaround for the case that someone tries + # to extract a (sym)link as a file-object from a non-seekable + # stream of tar blocks. + raise StreamError("cannot extract (sym)link as file object") + else: + # A (sym)link's file object is its target's file object. + return self.extractfile(self._find_link_target(tarinfo)) + else: + # If there's no data associated with the member (directory, chrdev, + # blkdev, etc.), return None instead of a file object. + return None + + def _extract_member(self, tarinfo, targetpath, set_attrs=True): + """Extract the TarInfo object tarinfo to a physical + file called targetpath. + """ + # Fetch the TarInfo object for the given name + # and build the destination pathname, replacing + # forward slashes to platform specific separators. + targetpath = targetpath.rstrip("/") + targetpath = targetpath.replace("/", os.sep) + + # Create all upper directories. + upperdirs = os.path.dirname(targetpath) + if upperdirs and not os.path.exists(upperdirs): + # Create directories that are not part of the archive with + # default permissions. + os.makedirs(upperdirs) + + if tarinfo.islnk() or tarinfo.issym(): + self._dbg(1, "%s -> %s" % (tarinfo.name, tarinfo.linkname)) + else: + self._dbg(1, tarinfo.name) + + if tarinfo.isreg(): + self.makefile(tarinfo, targetpath) + elif tarinfo.isdir(): + self.makedir(tarinfo, targetpath) + elif tarinfo.isfifo(): + self.makefifo(tarinfo, targetpath) + elif tarinfo.ischr() or tarinfo.isblk(): + self.makedev(tarinfo, targetpath) + elif tarinfo.islnk() or tarinfo.issym(): + self.makelink(tarinfo, targetpath) + elif tarinfo.type not in SUPPORTED_TYPES: + self.makeunknown(tarinfo, targetpath) + else: + self.makefile(tarinfo, targetpath) + + if set_attrs: + self.chown(tarinfo, targetpath) + if not tarinfo.issym(): + self.chmod(tarinfo, targetpath) + self.utime(tarinfo, targetpath) + + #-------------------------------------------------------------------------- + # Below are the different file methods. They are called via + # _extract_member() when extract() is called. They can be replaced in a + # subclass to implement other functionality. + + def makedir(self, tarinfo, targetpath): + """Make a directory called targetpath. + """ + try: + # Use a safe mode for the directory, the real mode is set + # later in _extract_member(). + os.mkdir(targetpath, 0o700) + except EnvironmentError as e: + if e.errno != errno.EEXIST: + raise + + def makefile(self, tarinfo, targetpath): + """Make a file called targetpath. + """ + source = self.fileobj + source.seek(tarinfo.offset_data) + target = bltn_open(targetpath, "wb") + if tarinfo.sparse is not None: + for offset, size in tarinfo.sparse: + target.seek(offset) + copyfileobj(source, target, size) + else: + copyfileobj(source, target, tarinfo.size) + target.seek(tarinfo.size) + target.truncate() + target.close() + + def makeunknown(self, tarinfo, targetpath): + """Make a file from a TarInfo object with an unknown type + at targetpath. + """ + self.makefile(tarinfo, targetpath) + self._dbg(1, "tarfile: Unknown file type %r, " \ + "extracted as regular file." % tarinfo.type) + + def makefifo(self, tarinfo, targetpath): + """Make a fifo called targetpath. + """ + if hasattr(os, "mkfifo"): + os.mkfifo(targetpath) + else: + raise ExtractError("fifo not supported by system") + + def makedev(self, tarinfo, targetpath): + """Make a character or block device called targetpath. + """ + if not hasattr(os, "mknod") or not hasattr(os, "makedev"): + raise ExtractError("special devices not supported by system") + + mode = tarinfo.mode + if tarinfo.isblk(): + mode |= stat.S_IFBLK + else: + mode |= stat.S_IFCHR + + os.mknod(targetpath, mode, + os.makedev(tarinfo.devmajor, tarinfo.devminor)) + + def makelink(self, tarinfo, targetpath): + """Make a (symbolic) link called targetpath. If it cannot be created + (platform limitation), we try to make a copy of the referenced file + instead of a link. + """ + try: + # For systems that support symbolic and hard links. + if tarinfo.issym(): + os.symlink(tarinfo.linkname, targetpath) + else: + # See extract(). + if os.path.exists(tarinfo._link_target): + os.link(tarinfo._link_target, targetpath) + else: + self._extract_member(self._find_link_target(tarinfo), + targetpath) + except symlink_exception: + if tarinfo.issym(): + linkpath = os.path.join(os.path.dirname(tarinfo.name), + tarinfo.linkname) + else: + linkpath = tarinfo.linkname + else: + try: + self._extract_member(self._find_link_target(tarinfo), + targetpath) + except KeyError: + raise ExtractError("unable to resolve link inside archive") + + def chown(self, tarinfo, targetpath): + """Set owner of targetpath according to tarinfo. + """ + if pwd and hasattr(os, "geteuid") and os.geteuid() == 0: + # We have to be root to do so. + try: + g = grp.getgrnam(tarinfo.gname)[2] + except KeyError: + g = tarinfo.gid + try: + u = pwd.getpwnam(tarinfo.uname)[2] + except KeyError: + u = tarinfo.uid + try: + if tarinfo.issym() and hasattr(os, "lchown"): + os.lchown(targetpath, u, g) + else: + if sys.platform != "os2emx": + os.chown(targetpath, u, g) + except EnvironmentError as e: + raise ExtractError("could not change owner") + + def chmod(self, tarinfo, targetpath): + """Set file permissions of targetpath according to tarinfo. + """ + if hasattr(os, 'chmod'): + try: + os.chmod(targetpath, tarinfo.mode) + except EnvironmentError as e: + raise ExtractError("could not change mode") + + def utime(self, tarinfo, targetpath): + """Set modification time of targetpath according to tarinfo. + """ + if not hasattr(os, 'utime'): + return + try: + os.utime(targetpath, (tarinfo.mtime, tarinfo.mtime)) + except EnvironmentError as e: + raise ExtractError("could not change modification time") + + #-------------------------------------------------------------------------- + def next(self): + """Return the next member of the archive as a TarInfo object, when + TarFile is opened for reading. Return None if there is no more + available. + """ + self._check("ra") + if self.firstmember is not None: + m = self.firstmember + self.firstmember = None + return m + + # Read the next block. + self.fileobj.seek(self.offset) + tarinfo = None + while True: + try: + tarinfo = self.tarinfo.fromtarfile(self) + except EOFHeaderError as e: + if self.ignore_zeros: + self._dbg(2, "0x%X: %s" % (self.offset, e)) + self.offset += BLOCKSIZE + continue + except InvalidHeaderError as e: + if self.ignore_zeros: + self._dbg(2, "0x%X: %s" % (self.offset, e)) + self.offset += BLOCKSIZE + continue + elif self.offset == 0: + raise ReadError(str(e)) + except EmptyHeaderError: + if self.offset == 0: + raise ReadError("empty file") + except TruncatedHeaderError as e: + if self.offset == 0: + raise ReadError(str(e)) + except SubsequentHeaderError as e: + raise ReadError(str(e)) + break + + if tarinfo is not None: + self.members.append(tarinfo) + else: + self._loaded = True + + return tarinfo + + #-------------------------------------------------------------------------- + # Little helper methods: + + def _getmember(self, name, tarinfo=None, normalize=False): + """Find an archive member by name from bottom to top. + If tarinfo is given, it is used as the starting point. + """ + # Ensure that all members have been loaded. + members = self.getmembers() + + # Limit the member search list up to tarinfo. + if tarinfo is not None: + members = members[:members.index(tarinfo)] + + if normalize: + name = os.path.normpath(name) + + for member in reversed(members): + if normalize: + member_name = os.path.normpath(member.name) + else: + member_name = member.name + + if name == member_name: + return member + + def _load(self): + """Read through the entire archive file and look for readable + members. + """ + while True: + tarinfo = self.next() + if tarinfo is None: + break + self._loaded = True + + def _check(self, mode=None): + """Check if TarFile is still open, and if the operation's mode + corresponds to TarFile's mode. + """ + if self.closed: + raise IOError("%s is closed" % self.__class__.__name__) + if mode is not None and self.mode not in mode: + raise IOError("bad operation for mode %r" % self.mode) + + def _find_link_target(self, tarinfo): + """Find the target member of a symlink or hardlink member in the + archive. + """ + if tarinfo.issym(): + # Always search the entire archive. + linkname = os.path.dirname(tarinfo.name) + "/" + tarinfo.linkname + limit = None + else: + # Search the archive before the link, because a hard link is + # just a reference to an already archived file. + linkname = tarinfo.linkname + limit = tarinfo + + member = self._getmember(linkname, tarinfo=limit, normalize=True) + if member is None: + raise KeyError("linkname %r not found" % linkname) + return member + + def __iter__(self): + """Provide an iterator object. + """ + if self._loaded: + return iter(self.members) + else: + return TarIter(self) + + def _dbg(self, level, msg): + """Write debugging output to sys.stderr. + """ + if level <= self.debug: + print(msg, file=sys.stderr) + + def __enter__(self): + self._check() + return self + + def __exit__(self, type, value, traceback): + if type is None: + self.close() + else: + # An exception occurred. We must not call close() because + # it would try to write end-of-archive blocks and padding. + if not self._extfileobj: + self.fileobj.close() + self.closed = True +# class TarFile + +class TarIter(object): + """Iterator Class. + + for tarinfo in TarFile(...): + suite... + """ + + def __init__(self, tarfile): + """Construct a TarIter object. + """ + self.tarfile = tarfile + self.index = 0 + def __iter__(self): + """Return iterator object. + """ + return self + + def __next__(self): + """Return the next item using TarFile's next() method. + When all members have been read, set TarFile as _loaded. + """ + # Fix for SF #1100429: Under rare circumstances it can + # happen that getmembers() is called during iteration, + # which will cause TarIter to stop prematurely. + if not self.tarfile._loaded: + tarinfo = self.tarfile.next() + if not tarinfo: + self.tarfile._loaded = True + raise StopIteration + else: + try: + tarinfo = self.tarfile.members[self.index] + except IndexError: + raise StopIteration + self.index += 1 + return tarinfo + + next = __next__ # for Python 2.x + +#-------------------- +# exported functions +#-------------------- +def is_tarfile(name): + """Return True if name points to a tar archive that we + are able to handle, else return False. + """ + try: + t = open(name) + t.close() + return True + except TarError: + return False + +bltn_open = open +open = TarFile.open diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/compat.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/compat.py new file mode 100644 index 0000000..63af374 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/compat.py @@ -0,0 +1,1064 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +from __future__ import absolute_import + +import os +import re +import sys + +if sys.version_info[0] < 3: + from StringIO import StringIO + string_types = basestring, + text_type = unicode + from types import FileType as file_type + import __builtin__ as builtins + import ConfigParser as configparser + from ._backport import shutil + from urlparse import urlparse, urlunparse, urljoin, urlsplit, urlunsplit + from urllib import (urlretrieve, quote as _quote, unquote, url2pathname, + pathname2url, ContentTooShortError, splittype) + + def quote(s): + if isinstance(s, unicode): + s = s.encode('utf-8') + return _quote(s) + + import urllib2 + from urllib2 import (Request, urlopen, URLError, HTTPError, + HTTPBasicAuthHandler, HTTPPasswordMgr, + HTTPSHandler, HTTPHandler, HTTPRedirectHandler, + build_opener) + import httplib + import xmlrpclib + import Queue as queue + from HTMLParser import HTMLParser + import htmlentitydefs + raw_input = raw_input + from itertools import ifilter as filter + from itertools import ifilterfalse as filterfalse + + _userprog = None + def splituser(host): + """splituser('user[:passwd]@host[:port]') --> 'user[:passwd]', 'host[:port]'.""" + global _userprog + if _userprog is None: + import re + _userprog = re.compile('^(.*)@(.*)$') + + match = _userprog.match(host) + if match: return match.group(1, 2) + return None, host + +else: + from io import StringIO + string_types = str, + text_type = str + from io import TextIOWrapper as file_type + import builtins + import configparser + import shutil + from urllib.parse import (urlparse, urlunparse, urljoin, splituser, quote, + unquote, urlsplit, urlunsplit, splittype) + from urllib.request import (urlopen, urlretrieve, Request, url2pathname, + pathname2url, + HTTPBasicAuthHandler, HTTPPasswordMgr, + HTTPSHandler, HTTPHandler, HTTPRedirectHandler, + build_opener) + from urllib.error import HTTPError, URLError, ContentTooShortError + import http.client as httplib + import urllib.request as urllib2 + import xmlrpc.client as xmlrpclib + import queue + from html.parser import HTMLParser + import html.entities as htmlentitydefs + raw_input = input + from itertools import filterfalse + filter = filter + +try: + from ssl import match_hostname, CertificateError +except ImportError: + class CertificateError(ValueError): + pass + + + def _dnsname_to_pat(dn): + pats = [] + for frag in dn.split(r'.'): + if frag == '*': + # When '*' is a fragment by itself, it matches a non-empty + # dotless fragment. + pats.append('[^.]+') + else: + # Otherwise, '*' matches any dotless fragment. + frag = re.escape(frag) + pats.append(frag.replace(r'\*', '[^.]*')) + return re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE) + + + def match_hostname(cert, hostname): + """Verify that *cert* (in decoded format as returned by + SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 rules + are mostly followed, but IP addresses are not accepted for *hostname*. + + CertificateError is raised on failure. On success, the function + returns nothing. + """ + if not cert: + raise ValueError("empty or no certificate") + dnsnames = [] + san = cert.get('subjectAltName', ()) + for key, value in san: + if key == 'DNS': + if _dnsname_to_pat(value).match(hostname): + return + dnsnames.append(value) + if not dnsnames: + # The subject is only checked when there is no dNSName entry + # in subjectAltName + for sub in cert.get('subject', ()): + for key, value in sub: + # XXX according to RFC 2818, the most specific Common Name + # must be used. + if key == 'commonName': + if _dnsname_to_pat(value).match(hostname): + return + dnsnames.append(value) + if len(dnsnames) > 1: + raise CertificateError("hostname %r " + "doesn't match either of %s" + % (hostname, ', '.join(map(repr, dnsnames)))) + elif len(dnsnames) == 1: + raise CertificateError("hostname %r " + "doesn't match %r" + % (hostname, dnsnames[0])) + else: + raise CertificateError("no appropriate commonName or " + "subjectAltName fields were found") + + +try: + from types import SimpleNamespace as Container +except ImportError: + class Container(object): + """ + A generic container for when multiple values need to be returned + """ + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + +try: + from shutil import which +except ImportError: + # Implementation from Python 3.3 + def which(cmd, mode=os.F_OK | os.X_OK, path=None): + """Given a command, mode, and a PATH string, return the path which + conforms to the given mode on the PATH, or None if there is no such + file. + + `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result + of os.environ.get("PATH"), or can be overridden with a custom search + path. + + """ + # Check that a given file can be accessed with the correct mode. + # Additionally check that `file` is not a directory, as on Windows + # directories pass the os.access check. + def _access_check(fn, mode): + return (os.path.exists(fn) and os.access(fn, mode) + and not os.path.isdir(fn)) + + # If we're given a path with a directory part, look it up directly rather + # than referring to PATH directories. This includes checking relative to the + # current directory, e.g. ./script + if os.path.dirname(cmd): + if _access_check(cmd, mode): + return cmd + return None + + if path is None: + path = os.environ.get("PATH", os.defpath) + if not path: + return None + path = path.split(os.pathsep) + + if sys.platform == "win32": + # The current directory takes precedence on Windows. + if not os.curdir in path: + path.insert(0, os.curdir) + + # PATHEXT is necessary to check on Windows. + pathext = os.environ.get("PATHEXT", "").split(os.pathsep) + # See if the given file matches any of the expected path extensions. + # This will allow us to short circuit when given "python.exe". + # If it does match, only test that one, otherwise we have to try + # others. + if any(cmd.lower().endswith(ext.lower()) for ext in pathext): + files = [cmd] + else: + files = [cmd + ext for ext in pathext] + else: + # On other platforms you don't have things like PATHEXT to tell you + # what file suffixes are executable, so just pass on cmd as-is. + files = [cmd] + + seen = set() + for dir in path: + normdir = os.path.normcase(dir) + if not normdir in seen: + seen.add(normdir) + for thefile in files: + name = os.path.join(dir, thefile) + if _access_check(name, mode): + return name + return None + + +# ZipFile is a context manager in 2.7, but not in 2.6 + +from zipfile import ZipFile as BaseZipFile + +if hasattr(BaseZipFile, '__enter__'): + ZipFile = BaseZipFile +else: + from zipfile import ZipExtFile as BaseZipExtFile + + class ZipExtFile(BaseZipExtFile): + def __init__(self, base): + self.__dict__.update(base.__dict__) + + def __enter__(self): + return self + + def __exit__(self, *exc_info): + self.close() + # return None, so if an exception occurred, it will propagate + + class ZipFile(BaseZipFile): + def __enter__(self): + return self + + def __exit__(self, *exc_info): + self.close() + # return None, so if an exception occurred, it will propagate + + def open(self, *args, **kwargs): + base = BaseZipFile.open(self, *args, **kwargs) + return ZipExtFile(base) + +try: + from platform import python_implementation +except ImportError: # pragma: no cover + def python_implementation(): + """Return a string identifying the Python implementation.""" + if 'PyPy' in sys.version: + return 'PyPy' + if os.name == 'java': + return 'Jython' + if sys.version.startswith('IronPython'): + return 'IronPython' + return 'CPython' + +try: + import sysconfig +except ImportError: # pragma: no cover + from ._backport import sysconfig + +try: + callable = callable +except NameError: # pragma: no cover + from collections import Callable + + def callable(obj): + return isinstance(obj, Callable) + + +try: + fsencode = os.fsencode + fsdecode = os.fsdecode +except AttributeError: # pragma: no cover + _fsencoding = sys.getfilesystemencoding() + if _fsencoding == 'mbcs': + _fserrors = 'strict' + else: + _fserrors = 'surrogateescape' + + def fsencode(filename): + if isinstance(filename, bytes): + return filename + elif isinstance(filename, text_type): + return filename.encode(_fsencoding, _fserrors) + else: + raise TypeError("expect bytes or str, not %s" % + type(filename).__name__) + + def fsdecode(filename): + if isinstance(filename, text_type): + return filename + elif isinstance(filename, bytes): + return filename.decode(_fsencoding, _fserrors) + else: + raise TypeError("expect bytes or str, not %s" % + type(filename).__name__) + +try: + from tokenize import detect_encoding +except ImportError: # pragma: no cover + from codecs import BOM_UTF8, lookup + import re + + cookie_re = re.compile("coding[:=]\s*([-\w.]+)") + + def _get_normal_name(orig_enc): + """Imitates get_normal_name in tokenizer.c.""" + # Only care about the first 12 characters. + enc = orig_enc[:12].lower().replace("_", "-") + if enc == "utf-8" or enc.startswith("utf-8-"): + return "utf-8" + if enc in ("latin-1", "iso-8859-1", "iso-latin-1") or \ + enc.startswith(("latin-1-", "iso-8859-1-", "iso-latin-1-")): + return "iso-8859-1" + return orig_enc + + def detect_encoding(readline): + """ + The detect_encoding() function is used to detect the encoding that should + be used to decode a Python source file. It requires one argment, readline, + in the same way as the tokenize() generator. + + It will call readline a maximum of twice, and return the encoding used + (as a string) and a list of any lines (left as bytes) it has read in. + + It detects the encoding from the presence of a utf-8 bom or an encoding + cookie as specified in pep-0263. If both a bom and a cookie are present, + but disagree, a SyntaxError will be raised. If the encoding cookie is an + invalid charset, raise a SyntaxError. Note that if a utf-8 bom is found, + 'utf-8-sig' is returned. + + If no encoding is specified, then the default of 'utf-8' will be returned. + """ + try: + filename = readline.__self__.name + except AttributeError: + filename = None + bom_found = False + encoding = None + default = 'utf-8' + def read_or_stop(): + try: + return readline() + except StopIteration: + return b'' + + def find_cookie(line): + try: + # Decode as UTF-8. Either the line is an encoding declaration, + # in which case it should be pure ASCII, or it must be UTF-8 + # per default encoding. + line_string = line.decode('utf-8') + except UnicodeDecodeError: + msg = "invalid or missing encoding declaration" + if filename is not None: + msg = '{} for {!r}'.format(msg, filename) + raise SyntaxError(msg) + + matches = cookie_re.findall(line_string) + if not matches: + return None + encoding = _get_normal_name(matches[0]) + try: + codec = lookup(encoding) + except LookupError: + # This behaviour mimics the Python interpreter + if filename is None: + msg = "unknown encoding: " + encoding + else: + msg = "unknown encoding for {!r}: {}".format(filename, + encoding) + raise SyntaxError(msg) + + if bom_found: + if codec.name != 'utf-8': + # This behaviour mimics the Python interpreter + if filename is None: + msg = 'encoding problem: utf-8' + else: + msg = 'encoding problem for {!r}: utf-8'.format(filename) + raise SyntaxError(msg) + encoding += '-sig' + return encoding + + first = read_or_stop() + if first.startswith(BOM_UTF8): + bom_found = True + first = first[3:] + default = 'utf-8-sig' + if not first: + return default, [] + + encoding = find_cookie(first) + if encoding: + return encoding, [first] + + second = read_or_stop() + if not second: + return default, [first] + + encoding = find_cookie(second) + if encoding: + return encoding, [first, second] + + return default, [first, second] + +# For converting & <-> & etc. +try: + from html import escape +except ImportError: + from cgi import escape +if sys.version_info[:2] < (3, 4): + unescape = HTMLParser().unescape +else: + from html import unescape + +try: + from collections import ChainMap +except ImportError: # pragma: no cover + from collections import MutableMapping + + try: + from reprlib import recursive_repr as _recursive_repr + except ImportError: + def _recursive_repr(fillvalue='...'): + ''' + Decorator to make a repr function return fillvalue for a recursive + call + ''' + + def decorating_function(user_function): + repr_running = set() + + def wrapper(self): + key = id(self), get_ident() + if key in repr_running: + return fillvalue + repr_running.add(key) + try: + result = user_function(self) + finally: + repr_running.discard(key) + return result + + # Can't use functools.wraps() here because of bootstrap issues + wrapper.__module__ = getattr(user_function, '__module__') + wrapper.__doc__ = getattr(user_function, '__doc__') + wrapper.__name__ = getattr(user_function, '__name__') + wrapper.__annotations__ = getattr(user_function, '__annotations__', {}) + return wrapper + + return decorating_function + + class ChainMap(MutableMapping): + ''' A ChainMap groups multiple dicts (or other mappings) together + to create a single, updateable view. + + The underlying mappings are stored in a list. That list is public and can + accessed or updated using the *maps* attribute. There is no other state. + + Lookups search the underlying mappings successively until a key is found. + In contrast, writes, updates, and deletions only operate on the first + mapping. + + ''' + + def __init__(self, *maps): + '''Initialize a ChainMap by setting *maps* to the given mappings. + If no mappings are provided, a single empty dictionary is used. + + ''' + self.maps = list(maps) or [{}] # always at least one map + + def __missing__(self, key): + raise KeyError(key) + + def __getitem__(self, key): + for mapping in self.maps: + try: + return mapping[key] # can't use 'key in mapping' with defaultdict + except KeyError: + pass + return self.__missing__(key) # support subclasses that define __missing__ + + def get(self, key, default=None): + return self[key] if key in self else default + + def __len__(self): + return len(set().union(*self.maps)) # reuses stored hash values if possible + + def __iter__(self): + return iter(set().union(*self.maps)) + + def __contains__(self, key): + return any(key in m for m in self.maps) + + def __bool__(self): + return any(self.maps) + + @_recursive_repr() + def __repr__(self): + return '{0.__class__.__name__}({1})'.format( + self, ', '.join(map(repr, self.maps))) + + @classmethod + def fromkeys(cls, iterable, *args): + 'Create a ChainMap with a single dict created from the iterable.' + return cls(dict.fromkeys(iterable, *args)) + + def copy(self): + 'New ChainMap or subclass with a new copy of maps[0] and refs to maps[1:]' + return self.__class__(self.maps[0].copy(), *self.maps[1:]) + + __copy__ = copy + + def new_child(self): # like Django's Context.push() + 'New ChainMap with a new dict followed by all previous maps.' + return self.__class__({}, *self.maps) + + @property + def parents(self): # like Django's Context.pop() + 'New ChainMap from maps[1:].' + return self.__class__(*self.maps[1:]) + + def __setitem__(self, key, value): + self.maps[0][key] = value + + def __delitem__(self, key): + try: + del self.maps[0][key] + except KeyError: + raise KeyError('Key not found in the first mapping: {!r}'.format(key)) + + def popitem(self): + 'Remove and return an item pair from maps[0]. Raise KeyError is maps[0] is empty.' + try: + return self.maps[0].popitem() + except KeyError: + raise KeyError('No keys found in the first mapping.') + + def pop(self, key, *args): + 'Remove *key* from maps[0] and return its value. Raise KeyError if *key* not in maps[0].' + try: + return self.maps[0].pop(key, *args) + except KeyError: + raise KeyError('Key not found in the first mapping: {!r}'.format(key)) + + def clear(self): + 'Clear maps[0], leaving maps[1:] intact.' + self.maps[0].clear() + +try: + from imp import cache_from_source +except ImportError: # pragma: no cover + def cache_from_source(path, debug_override=None): + assert path.endswith('.py') + if debug_override is None: + debug_override = __debug__ + if debug_override: + suffix = 'c' + else: + suffix = 'o' + return path + suffix + +try: + from collections import OrderedDict +except ImportError: # pragma: no cover +## {{{ http://code.activestate.com/recipes/576693/ (r9) +# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy. +# Passes Python2.7's test suite and incorporates all the latest updates. + try: + from thread import get_ident as _get_ident + except ImportError: + from dummy_thread import get_ident as _get_ident + + try: + from _abcoll import KeysView, ValuesView, ItemsView + except ImportError: + pass + + + class OrderedDict(dict): + 'Dictionary that remembers insertion order' + # An inherited dict maps keys to values. + # The inherited dict provides __getitem__, __len__, __contains__, and get. + # The remaining methods are order-aware. + # Big-O running times for all methods are the same as for regular dictionaries. + + # The internal self.__map dictionary maps keys to links in a doubly linked list. + # The circular doubly linked list starts and ends with a sentinel element. + # The sentinel element never gets deleted (this simplifies the algorithm). + # Each link is stored as a list of length three: [PREV, NEXT, KEY]. + + def __init__(self, *args, **kwds): + '''Initialize an ordered dictionary. Signature is the same as for + regular dictionaries, but keyword arguments are not recommended + because their insertion order is arbitrary. + + ''' + if len(args) > 1: + raise TypeError('expected at most 1 arguments, got %d' % len(args)) + try: + self.__root + except AttributeError: + self.__root = root = [] # sentinel node + root[:] = [root, root, None] + self.__map = {} + self.__update(*args, **kwds) + + def __setitem__(self, key, value, dict_setitem=dict.__setitem__): + 'od.__setitem__(i, y) <==> od[i]=y' + # Setting a new item creates a new link which goes at the end of the linked + # list, and the inherited dictionary is updated with the new key/value pair. + if key not in self: + root = self.__root + last = root[0] + last[1] = root[0] = self.__map[key] = [last, root, key] + dict_setitem(self, key, value) + + def __delitem__(self, key, dict_delitem=dict.__delitem__): + 'od.__delitem__(y) <==> del od[y]' + # Deleting an existing item uses self.__map to find the link which is + # then removed by updating the links in the predecessor and successor nodes. + dict_delitem(self, key) + link_prev, link_next, key = self.__map.pop(key) + link_prev[1] = link_next + link_next[0] = link_prev + + def __iter__(self): + 'od.__iter__() <==> iter(od)' + root = self.__root + curr = root[1] + while curr is not root: + yield curr[2] + curr = curr[1] + + def __reversed__(self): + 'od.__reversed__() <==> reversed(od)' + root = self.__root + curr = root[0] + while curr is not root: + yield curr[2] + curr = curr[0] + + def clear(self): + 'od.clear() -> None. Remove all items from od.' + try: + for node in self.__map.itervalues(): + del node[:] + root = self.__root + root[:] = [root, root, None] + self.__map.clear() + except AttributeError: + pass + dict.clear(self) + + def popitem(self, last=True): + '''od.popitem() -> (k, v), return and remove a (key, value) pair. + Pairs are returned in LIFO order if last is true or FIFO order if false. + + ''' + if not self: + raise KeyError('dictionary is empty') + root = self.__root + if last: + link = root[0] + link_prev = link[0] + link_prev[1] = root + root[0] = link_prev + else: + link = root[1] + link_next = link[1] + root[1] = link_next + link_next[0] = root + key = link[2] + del self.__map[key] + value = dict.pop(self, key) + return key, value + + # -- the following methods do not depend on the internal structure -- + + def keys(self): + 'od.keys() -> list of keys in od' + return list(self) + + def values(self): + 'od.values() -> list of values in od' + return [self[key] for key in self] + + def items(self): + 'od.items() -> list of (key, value) pairs in od' + return [(key, self[key]) for key in self] + + def iterkeys(self): + 'od.iterkeys() -> an iterator over the keys in od' + return iter(self) + + def itervalues(self): + 'od.itervalues -> an iterator over the values in od' + for k in self: + yield self[k] + + def iteritems(self): + 'od.iteritems -> an iterator over the (key, value) items in od' + for k in self: + yield (k, self[k]) + + def update(*args, **kwds): + '''od.update(E, **F) -> None. Update od from dict/iterable E and F. + + If E is a dict instance, does: for k in E: od[k] = E[k] + If E has a .keys() method, does: for k in E.keys(): od[k] = E[k] + Or if E is an iterable of items, does: for k, v in E: od[k] = v + In either case, this is followed by: for k, v in F.items(): od[k] = v + + ''' + if len(args) > 2: + raise TypeError('update() takes at most 2 positional ' + 'arguments (%d given)' % (len(args),)) + elif not args: + raise TypeError('update() takes at least 1 argument (0 given)') + self = args[0] + # Make progressively weaker assumptions about "other" + other = () + if len(args) == 2: + other = args[1] + if isinstance(other, dict): + for key in other: + self[key] = other[key] + elif hasattr(other, 'keys'): + for key in other.keys(): + self[key] = other[key] + else: + for key, value in other: + self[key] = value + for key, value in kwds.items(): + self[key] = value + + __update = update # let subclasses override update without breaking __init__ + + __marker = object() + + def pop(self, key, default=__marker): + '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value. + If key is not found, d is returned if given, otherwise KeyError is raised. + + ''' + if key in self: + result = self[key] + del self[key] + return result + if default is self.__marker: + raise KeyError(key) + return default + + def setdefault(self, key, default=None): + 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od' + if key in self: + return self[key] + self[key] = default + return default + + def __repr__(self, _repr_running=None): + 'od.__repr__() <==> repr(od)' + if not _repr_running: _repr_running = {} + call_key = id(self), _get_ident() + if call_key in _repr_running: + return '...' + _repr_running[call_key] = 1 + try: + if not self: + return '%s()' % (self.__class__.__name__,) + return '%s(%r)' % (self.__class__.__name__, self.items()) + finally: + del _repr_running[call_key] + + def __reduce__(self): + 'Return state information for pickling' + items = [[k, self[k]] for k in self] + inst_dict = vars(self).copy() + for k in vars(OrderedDict()): + inst_dict.pop(k, None) + if inst_dict: + return (self.__class__, (items,), inst_dict) + return self.__class__, (items,) + + def copy(self): + 'od.copy() -> a shallow copy of od' + return self.__class__(self) + + @classmethod + def fromkeys(cls, iterable, value=None): + '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S + and values equal to v (which defaults to None). + + ''' + d = cls() + for key in iterable: + d[key] = value + return d + + def __eq__(self, other): + '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive + while comparison to a regular mapping is order-insensitive. + + ''' + if isinstance(other, OrderedDict): + return len(self)==len(other) and self.items() == other.items() + return dict.__eq__(self, other) + + def __ne__(self, other): + return not self == other + + # -- the following methods are only used in Python 2.7 -- + + def viewkeys(self): + "od.viewkeys() -> a set-like object providing a view on od's keys" + return KeysView(self) + + def viewvalues(self): + "od.viewvalues() -> an object providing a view on od's values" + return ValuesView(self) + + def viewitems(self): + "od.viewitems() -> a set-like object providing a view on od's items" + return ItemsView(self) + +try: + from logging.config import BaseConfigurator, valid_ident +except ImportError: # pragma: no cover + IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I) + + + def valid_ident(s): + m = IDENTIFIER.match(s) + if not m: + raise ValueError('Not a valid Python identifier: %r' % s) + return True + + + # The ConvertingXXX classes are wrappers around standard Python containers, + # and they serve to convert any suitable values in the container. The + # conversion converts base dicts, lists and tuples to their wrapped + # equivalents, whereas strings which match a conversion format are converted + # appropriately. + # + # Each wrapper should have a configurator attribute holding the actual + # configurator to use for conversion. + + class ConvertingDict(dict): + """A converting dictionary wrapper.""" + + def __getitem__(self, key): + value = dict.__getitem__(self, key) + result = self.configurator.convert(value) + #If the converted value is different, save for next time + if value is not result: + self[key] = result + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + def get(self, key, default=None): + value = dict.get(self, key, default) + result = self.configurator.convert(value) + #If the converted value is different, save for next time + if value is not result: + self[key] = result + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + def pop(self, key, default=None): + value = dict.pop(self, key, default) + result = self.configurator.convert(value) + if value is not result: + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + class ConvertingList(list): + """A converting list wrapper.""" + def __getitem__(self, key): + value = list.__getitem__(self, key) + result = self.configurator.convert(value) + #If the converted value is different, save for next time + if value is not result: + self[key] = result + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + def pop(self, idx=-1): + value = list.pop(self, idx) + result = self.configurator.convert(value) + if value is not result: + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + return result + + class ConvertingTuple(tuple): + """A converting tuple wrapper.""" + def __getitem__(self, key): + value = tuple.__getitem__(self, key) + result = self.configurator.convert(value) + if value is not result: + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + class BaseConfigurator(object): + """ + The configurator base class which defines some useful defaults. + """ + + CONVERT_PATTERN = re.compile(r'^(?P[a-z]+)://(?P.*)$') + + WORD_PATTERN = re.compile(r'^\s*(\w+)\s*') + DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*') + INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*') + DIGIT_PATTERN = re.compile(r'^\d+$') + + value_converters = { + 'ext' : 'ext_convert', + 'cfg' : 'cfg_convert', + } + + # We might want to use a different one, e.g. importlib + importer = staticmethod(__import__) + + def __init__(self, config): + self.config = ConvertingDict(config) + self.config.configurator = self + + def resolve(self, s): + """ + Resolve strings to objects using standard import and attribute + syntax. + """ + name = s.split('.') + used = name.pop(0) + try: + found = self.importer(used) + for frag in name: + used += '.' + frag + try: + found = getattr(found, frag) + except AttributeError: + self.importer(used) + found = getattr(found, frag) + return found + except ImportError: + e, tb = sys.exc_info()[1:] + v = ValueError('Cannot resolve %r: %s' % (s, e)) + v.__cause__, v.__traceback__ = e, tb + raise v + + def ext_convert(self, value): + """Default converter for the ext:// protocol.""" + return self.resolve(value) + + def cfg_convert(self, value): + """Default converter for the cfg:// protocol.""" + rest = value + m = self.WORD_PATTERN.match(rest) + if m is None: + raise ValueError("Unable to convert %r" % value) + else: + rest = rest[m.end():] + d = self.config[m.groups()[0]] + #print d, rest + while rest: + m = self.DOT_PATTERN.match(rest) + if m: + d = d[m.groups()[0]] + else: + m = self.INDEX_PATTERN.match(rest) + if m: + idx = m.groups()[0] + if not self.DIGIT_PATTERN.match(idx): + d = d[idx] + else: + try: + n = int(idx) # try as number first (most likely) + d = d[n] + except TypeError: + d = d[idx] + if m: + rest = rest[m.end():] + else: + raise ValueError('Unable to convert ' + '%r at %r' % (value, rest)) + #rest should be empty + return d + + def convert(self, value): + """ + Convert values to an appropriate type. dicts, lists and tuples are + replaced by their converting alternatives. Strings are checked to + see if they have a conversion format and are converted if they do. + """ + if not isinstance(value, ConvertingDict) and isinstance(value, dict): + value = ConvertingDict(value) + value.configurator = self + elif not isinstance(value, ConvertingList) and isinstance(value, list): + value = ConvertingList(value) + value.configurator = self + elif not isinstance(value, ConvertingTuple) and\ + isinstance(value, tuple): + value = ConvertingTuple(value) + value.configurator = self + elif isinstance(value, string_types): + m = self.CONVERT_PATTERN.match(value) + if m: + d = m.groupdict() + prefix = d['prefix'] + converter = self.value_converters.get(prefix, None) + if converter: + suffix = d['suffix'] + converter = getattr(self, converter) + value = converter(suffix) + return value + + def configure_custom(self, config): + """Configure an object with a user-supplied factory.""" + c = config.pop('()') + if not callable(c): + c = self.resolve(c) + props = config.pop('.', None) + # Check for valid identifiers + kwargs = dict([(k, config[k]) for k in config if valid_ident(k)]) + result = c(**kwargs) + if props: + for name, value in props.items(): + setattr(result, name, value) + return result + + def as_tuple(self, value): + """Utility function which converts lists to tuples.""" + if isinstance(value, list): + value = tuple(value) + return value diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/database.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/database.py new file mode 100644 index 0000000..0f013ec --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/database.py @@ -0,0 +1,1301 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2013 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +"""PEP 376 implementation.""" + +from __future__ import unicode_literals + +import base64 +import codecs +import contextlib +import hashlib +import logging +import os +import posixpath +import sys +import zipimport + +from . import DistlibException, resources +from .compat import StringIO +from .version import get_scheme, UnsupportedVersionError +from .metadata import Metadata, METADATA_FILENAME +from .util import (parse_requirement, cached_property, parse_name_and_version, + read_exports, write_exports, CSVReader, CSVWriter) + + +__all__ = ['Distribution', 'BaseInstalledDistribution', + 'InstalledDistribution', 'EggInfoDistribution', + 'DistributionPath'] + + +logger = logging.getLogger(__name__) + +EXPORTS_FILENAME = 'pydist-exports.json' +COMMANDS_FILENAME = 'pydist-commands.json' + +DIST_FILES = ('INSTALLER', METADATA_FILENAME, 'RECORD', 'REQUESTED', + 'RESOURCES', EXPORTS_FILENAME, 'SHARED') + +DISTINFO_EXT = '.dist-info' + + +class _Cache(object): + """ + A simple cache mapping names and .dist-info paths to distributions + """ + def __init__(self): + """ + Initialise an instance. There is normally one for each DistributionPath. + """ + self.name = {} + self.path = {} + self.generated = False + + def clear(self): + """ + Clear the cache, setting it to its initial state. + """ + self.name.clear() + self.path.clear() + self.generated = False + + def add(self, dist): + """ + Add a distribution to the cache. + :param dist: The distribution to add. + """ + if dist.path not in self.path: + self.path[dist.path] = dist + self.name.setdefault(dist.key, []).append(dist) + + +class DistributionPath(object): + """ + Represents a set of distributions installed on a path (typically sys.path). + """ + def __init__(self, path=None, include_egg=False): + """ + Create an instance from a path, optionally including legacy (distutils/ + setuptools/distribute) distributions. + :param path: The path to use, as a list of directories. If not specified, + sys.path is used. + :param include_egg: If True, this instance will look for and return legacy + distributions as well as those based on PEP 376. + """ + if path is None: + path = sys.path + self.path = path + self._include_dist = True + self._include_egg = include_egg + + self._cache = _Cache() + self._cache_egg = _Cache() + self._cache_enabled = True + self._scheme = get_scheme('default') + + def _get_cache_enabled(self): + return self._cache_enabled + + def _set_cache_enabled(self, value): + self._cache_enabled = value + + cache_enabled = property(_get_cache_enabled, _set_cache_enabled) + + def clear_cache(self): + """ + Clears the internal cache. + """ + self._cache.clear() + self._cache_egg.clear() + + + def _yield_distributions(self): + """ + Yield .dist-info and/or .egg(-info) distributions. + """ + # We need to check if we've seen some resources already, because on + # some Linux systems (e.g. some Debian/Ubuntu variants) there are + # symlinks which alias other files in the environment. + seen = set() + for path in self.path: + finder = resources.finder_for_path(path) + if finder is None: + continue + r = finder.find('') + if not r or not r.is_container: + continue + rset = sorted(r.resources) + for entry in rset: + r = finder.find(entry) + if not r or r.path in seen: + continue + if self._include_dist and entry.endswith(DISTINFO_EXT): + metadata_path = posixpath.join(entry, METADATA_FILENAME) + pydist = finder.find(metadata_path) + if not pydist: + continue + + metadata = Metadata(fileobj=pydist.as_stream(), + scheme='legacy') + logger.debug('Found %s', r.path) + seen.add(r.path) + yield new_dist_class(r.path, metadata=metadata, + env=self) + elif self._include_egg and entry.endswith(('.egg-info', + '.egg')): + logger.debug('Found %s', r.path) + seen.add(r.path) + yield old_dist_class(r.path, self) + + def _generate_cache(self): + """ + Scan the path for distributions and populate the cache with + those that are found. + """ + gen_dist = not self._cache.generated + gen_egg = self._include_egg and not self._cache_egg.generated + if gen_dist or gen_egg: + for dist in self._yield_distributions(): + if isinstance(dist, InstalledDistribution): + self._cache.add(dist) + else: + self._cache_egg.add(dist) + + if gen_dist: + self._cache.generated = True + if gen_egg: + self._cache_egg.generated = True + + @classmethod + def distinfo_dirname(cls, name, version): + """ + The *name* and *version* parameters are converted into their + filename-escaped form, i.e. any ``'-'`` characters are replaced + with ``'_'`` other than the one in ``'dist-info'`` and the one + separating the name from the version number. + + :parameter name: is converted to a standard distribution name by replacing + any runs of non- alphanumeric characters with a single + ``'-'``. + :type name: string + :parameter version: is converted to a standard version string. Spaces + become dots, and all other non-alphanumeric characters + (except dots) become dashes, with runs of multiple + dashes condensed to a single dash. + :type version: string + :returns: directory name + :rtype: string""" + name = name.replace('-', '_') + return '-'.join([name, version]) + DISTINFO_EXT + + def get_distributions(self): + """ + Provides an iterator that looks for distributions and returns + :class:`InstalledDistribution` or + :class:`EggInfoDistribution` instances for each one of them. + + :rtype: iterator of :class:`InstalledDistribution` and + :class:`EggInfoDistribution` instances + """ + if not self._cache_enabled: + for dist in self._yield_distributions(): + yield dist + else: + self._generate_cache() + + for dist in self._cache.path.values(): + yield dist + + if self._include_egg: + for dist in self._cache_egg.path.values(): + yield dist + + def get_distribution(self, name): + """ + Looks for a named distribution on the path. + + This function only returns the first result found, as no more than one + value is expected. If nothing is found, ``None`` is returned. + + :rtype: :class:`InstalledDistribution`, :class:`EggInfoDistribution` + or ``None`` + """ + result = None + name = name.lower() + if not self._cache_enabled: + for dist in self._yield_distributions(): + if dist.key == name: + result = dist + break + else: + self._generate_cache() + + if name in self._cache.name: + result = self._cache.name[name][0] + elif self._include_egg and name in self._cache_egg.name: + result = self._cache_egg.name[name][0] + return result + + def provides_distribution(self, name, version=None): + """ + Iterates over all distributions to find which distributions provide *name*. + If a *version* is provided, it will be used to filter the results. + + This function only returns the first result found, since no more than + one values are expected. If the directory is not found, returns ``None``. + + :parameter version: a version specifier that indicates the version + required, conforming to the format in ``PEP-345`` + + :type name: string + :type version: string + """ + matcher = None + if not version is None: + try: + matcher = self._scheme.matcher('%s (%s)' % (name, version)) + except ValueError: + raise DistlibException('invalid name or version: %r, %r' % + (name, version)) + + for dist in self.get_distributions(): + provided = dist.provides + + for p in provided: + p_name, p_ver = parse_name_and_version(p) + if matcher is None: + if p_name == name: + yield dist + break + else: + if p_name == name and matcher.match(p_ver): + yield dist + break + + def get_file_path(self, name, relative_path): + """ + Return the path to a resource file. + """ + dist = self.get_distribution(name) + if dist is None: + raise LookupError('no distribution named %r found' % name) + return dist.get_resource_path(relative_path) + + def get_exported_entries(self, category, name=None): + """ + Return all of the exported entries in a particular category. + + :param category: The category to search for entries. + :param name: If specified, only entries with that name are returned. + """ + for dist in self.get_distributions(): + r = dist.exports + if category in r: + d = r[category] + if name is not None: + if name in d: + yield d[name] + else: + for v in d.values(): + yield v + + +class Distribution(object): + """ + A base class for distributions, whether installed or from indexes. + Either way, it must have some metadata, so that's all that's needed + for construction. + """ + + build_time_dependency = False + """ + Set to True if it's known to be only a build-time dependency (i.e. + not needed after installation). + """ + + requested = False + """A boolean that indicates whether the ``REQUESTED`` metadata file is + present (in other words, whether the package was installed by user + request or it was installed as a dependency).""" + + def __init__(self, metadata): + """ + Initialise an instance. + :param metadata: The instance of :class:`Metadata` describing this + distribution. + """ + self.metadata = metadata + self.name = metadata.name + self.key = self.name.lower() # for case-insensitive comparisons + self.version = metadata.version + self.locator = None + self.digest = None + self.extras = None # additional features requested + self.context = None # environment marker overrides + + @property + def source_url(self): + """ + The source archive download URL for this distribution. + """ + return self.metadata.source_url + + download_url = source_url # Backward compatibility + + @property + def name_and_version(self): + """ + A utility property which displays the name and version in parentheses. + """ + return '%s (%s)' % (self.name, self.version) + + @property + def provides(self): + """ + A set of distribution names and versions provided by this distribution. + :return: A set of "name (version)" strings. + """ + plist = self.metadata.provides + s = '%s (%s)' % (self.name, self.version) + if s not in plist: + plist.append(s) + return plist + + def _get_requirements(self, req_attr): + reqts = getattr(self.metadata, req_attr) + return set(self.metadata.get_requirements(reqts, extras=self.extras, + env=self.context)) + + @property + def run_requires(self): + return self._get_requirements('run_requires') + + @property + def meta_requires(self): + return self._get_requirements('meta_requires') + + @property + def build_requires(self): + return self._get_requirements('build_requires') + + @property + def test_requires(self): + return self._get_requirements('test_requires') + + @property + def dev_requires(self): + return self._get_requirements('dev_requires') + + def matches_requirement(self, req): + """ + Say if this instance matches (fulfills) a requirement. + :param req: The requirement to match. + :rtype req: str + :return: True if it matches, else False. + """ + # Requirement may contain extras - parse to lose those + # from what's passed to the matcher + r = parse_requirement(req) + scheme = get_scheme(self.metadata.scheme) + try: + matcher = scheme.matcher(r.requirement) + except UnsupportedVersionError: + # XXX compat-mode if cannot read the version + logger.warning('could not read version %r - using name only', + req) + name = req.split()[0] + matcher = scheme.matcher(name) + + name = matcher.key # case-insensitive + + result = False + for p in self.provides: + p_name, p_ver = parse_name_and_version(p) + if p_name != name: + continue + try: + result = matcher.match(p_ver) + break + except UnsupportedVersionError: + pass + return result + + def __repr__(self): + """ + Return a textual representation of this instance, + """ + if self.source_url: + suffix = ' [%s]' % self.source_url + else: + suffix = '' + return '' % (self.name, self.version, suffix) + + def __eq__(self, other): + """ + See if this distribution is the same as another. + :param other: The distribution to compare with. To be equal to one + another. distributions must have the same type, name, + version and source_url. + :return: True if it is the same, else False. + """ + if type(other) is not type(self): + result = False + else: + result = (self.name == other.name and + self.version == other.version and + self.source_url == other.source_url) + return result + + def __hash__(self): + """ + Compute hash in a way which matches the equality test. + """ + return hash(self.name) + hash(self.version) + hash(self.source_url) + + +class BaseInstalledDistribution(Distribution): + """ + This is the base class for installed distributions (whether PEP 376 or + legacy). + """ + + hasher = None + + def __init__(self, metadata, path, env=None): + """ + Initialise an instance. + :param metadata: An instance of :class:`Metadata` which describes the + distribution. This will normally have been initialised + from a metadata file in the ``path``. + :param path: The path of the ``.dist-info`` or ``.egg-info`` + directory for the distribution. + :param env: This is normally the :class:`DistributionPath` + instance where this distribution was found. + """ + super(BaseInstalledDistribution, self).__init__(metadata) + self.path = path + self.dist_path = env + + def get_hash(self, data, hasher=None): + """ + Get the hash of some data, using a particular hash algorithm, if + specified. + + :param data: The data to be hashed. + :type data: bytes + :param hasher: The name of a hash implementation, supported by hashlib, + or ``None``. Examples of valid values are ``'sha1'``, + ``'sha224'``, ``'sha384'``, '``sha256'``, ``'md5'`` and + ``'sha512'``. If no hasher is specified, the ``hasher`` + attribute of the :class:`InstalledDistribution` instance + is used. If the hasher is determined to be ``None``, MD5 + is used as the hashing algorithm. + :returns: The hash of the data. If a hasher was explicitly specified, + the returned hash will be prefixed with the specified hasher + followed by '='. + :rtype: str + """ + if hasher is None: + hasher = self.hasher + if hasher is None: + hasher = hashlib.md5 + prefix = '' + else: + hasher = getattr(hashlib, hasher) + prefix = '%s=' % self.hasher + digest = hasher(data).digest() + digest = base64.urlsafe_b64encode(digest).rstrip(b'=').decode('ascii') + return '%s%s' % (prefix, digest) + + +class InstalledDistribution(BaseInstalledDistribution): + """ + Created with the *path* of the ``.dist-info`` directory provided to the + constructor. It reads the metadata contained in ``pydist.json`` when it is + instantiated., or uses a passed in Metadata instance (useful for when + dry-run mode is being used). + """ + + hasher = 'sha256' + + def __init__(self, path, metadata=None, env=None): + self.finder = finder = resources.finder_for_path(path) + if finder is None: + import pdb; pdb.set_trace () + if env and env._cache_enabled and path in env._cache.path: + metadata = env._cache.path[path].metadata + elif metadata is None: + r = finder.find(METADATA_FILENAME) + # Temporary - for legacy support + if r is None: + r = finder.find('METADATA') + if r is None: + raise ValueError('no %s found in %s' % (METADATA_FILENAME, + path)) + with contextlib.closing(r.as_stream()) as stream: + metadata = Metadata(fileobj=stream, scheme='legacy') + + super(InstalledDistribution, self).__init__(metadata, path, env) + + if env and env._cache_enabled: + env._cache.add(self) + + try: + r = finder.find('REQUESTED') + except AttributeError: + import pdb; pdb.set_trace () + self.requested = r is not None + + def __repr__(self): + return '' % ( + self.name, self.version, self.path) + + def __str__(self): + return "%s %s" % (self.name, self.version) + + def _get_records(self): + """ + Get the list of installed files for the distribution + :return: A list of tuples of path, hash and size. Note that hash and + size might be ``None`` for some entries. The path is exactly + as stored in the file (which is as in PEP 376). + """ + results = [] + r = self.get_distinfo_resource('RECORD') + with contextlib.closing(r.as_stream()) as stream: + with CSVReader(stream=stream) as record_reader: + # Base location is parent dir of .dist-info dir + #base_location = os.path.dirname(self.path) + #base_location = os.path.abspath(base_location) + for row in record_reader: + missing = [None for i in range(len(row), 3)] + path, checksum, size = row + missing + #if not os.path.isabs(path): + # path = path.replace('/', os.sep) + # path = os.path.join(base_location, path) + results.append((path, checksum, size)) + return results + + @cached_property + def exports(self): + """ + Return the information exported by this distribution. + :return: A dictionary of exports, mapping an export category to a dict + of :class:`ExportEntry` instances describing the individual + export entries, and keyed by name. + """ + result = {} + r = self.get_distinfo_resource(EXPORTS_FILENAME) + if r: + result = self.read_exports() + return result + + def read_exports(self): + """ + Read exports data from a file in .ini format. + + :return: A dictionary of exports, mapping an export category to a list + of :class:`ExportEntry` instances describing the individual + export entries. + """ + result = {} + r = self.get_distinfo_resource(EXPORTS_FILENAME) + if r: + with contextlib.closing(r.as_stream()) as stream: + result = read_exports(stream) + return result + + def write_exports(self, exports): + """ + Write a dictionary of exports to a file in .ini format. + :param exports: A dictionary of exports, mapping an export category to + a list of :class:`ExportEntry` instances describing the + individual export entries. + """ + rf = self.get_distinfo_file(EXPORTS_FILENAME) + with open(rf, 'w') as f: + write_exports(exports, f) + + def get_resource_path(self, relative_path): + """ + NOTE: This API may change in the future. + + Return the absolute path to a resource file with the given relative + path. + + :param relative_path: The path, relative to .dist-info, of the resource + of interest. + :return: The absolute path where the resource is to be found. + """ + r = self.get_distinfo_resource('RESOURCES') + with contextlib.closing(r.as_stream()) as stream: + with CSVReader(stream=stream) as resources_reader: + for relative, destination in resources_reader: + if relative == relative_path: + return destination + raise KeyError('no resource file with relative path %r ' + 'is installed' % relative_path) + + def list_installed_files(self): + """ + Iterates over the ``RECORD`` entries and returns a tuple + ``(path, hash, size)`` for each line. + + :returns: iterator of (path, hash, size) + """ + for result in self._get_records(): + yield result + + def write_installed_files(self, paths, prefix, dry_run=False): + """ + Writes the ``RECORD`` file, using the ``paths`` iterable passed in. Any + existing ``RECORD`` file is silently overwritten. + + prefix is used to determine when to write absolute paths. + """ + prefix = os.path.join(prefix, '') + base = os.path.dirname(self.path) + base_under_prefix = base.startswith(prefix) + base = os.path.join(base, '') + record_path = self.get_distinfo_file('RECORD') + logger.info('creating %s', record_path) + if dry_run: + return None + with CSVWriter(record_path) as writer: + for path in paths: + if os.path.isdir(path) or path.endswith(('.pyc', '.pyo')): + # do not put size and hash, as in PEP-376 + hash_value = size = '' + else: + size = '%d' % os.path.getsize(path) + with open(path, 'rb') as fp: + hash_value = self.get_hash(fp.read()) + if path.startswith(base) or (base_under_prefix and + path.startswith(prefix)): + path = os.path.relpath(path, base) + writer.writerow((path, hash_value, size)) + + # add the RECORD file itself + if record_path.startswith(base): + record_path = os.path.relpath(record_path, base) + writer.writerow((record_path, '', '')) + return record_path + + def check_installed_files(self): + """ + Checks that the hashes and sizes of the files in ``RECORD`` are + matched by the files themselves. Returns a (possibly empty) list of + mismatches. Each entry in the mismatch list will be a tuple consisting + of the path, 'exists', 'size' or 'hash' according to what didn't match + (existence is checked first, then size, then hash), the expected + value and the actual value. + """ + mismatches = [] + base = os.path.dirname(self.path) + record_path = self.get_distinfo_file('RECORD') + for path, hash_value, size in self.list_installed_files(): + if not os.path.isabs(path): + path = os.path.join(base, path) + if path == record_path: + continue + if not os.path.exists(path): + mismatches.append((path, 'exists', True, False)) + elif os.path.isfile(path): + actual_size = str(os.path.getsize(path)) + if size and actual_size != size: + mismatches.append((path, 'size', size, actual_size)) + elif hash_value: + if '=' in hash_value: + hasher = hash_value.split('=', 1)[0] + else: + hasher = None + + with open(path, 'rb') as f: + actual_hash = self.get_hash(f.read(), hasher) + if actual_hash != hash_value: + mismatches.append((path, 'hash', hash_value, actual_hash)) + return mismatches + + @cached_property + def shared_locations(self): + """ + A dictionary of shared locations whose keys are in the set 'prefix', + 'purelib', 'platlib', 'scripts', 'headers', 'data' and 'namespace'. + The corresponding value is the absolute path of that category for + this distribution, and takes into account any paths selected by the + user at installation time (e.g. via command-line arguments). In the + case of the 'namespace' key, this would be a list of absolute paths + for the roots of namespace packages in this distribution. + + The first time this property is accessed, the relevant information is + read from the SHARED file in the .dist-info directory. + """ + result = {} + shared_path = os.path.join(self.path, 'SHARED') + if os.path.isfile(shared_path): + with codecs.open(shared_path, 'r', encoding='utf-8') as f: + lines = f.read().splitlines() + for line in lines: + key, value = line.split('=', 1) + if key == 'namespace': + result.setdefault(key, []).append(value) + else: + result[key] = value + return result + + def write_shared_locations(self, paths, dry_run=False): + """ + Write shared location information to the SHARED file in .dist-info. + :param paths: A dictionary as described in the documentation for + :meth:`shared_locations`. + :param dry_run: If True, the action is logged but no file is actually + written. + :return: The path of the file written to. + """ + shared_path = os.path.join(self.path, 'SHARED') + logger.info('creating %s', shared_path) + if dry_run: + return None + lines = [] + for key in ('prefix', 'lib', 'headers', 'scripts', 'data'): + path = paths[key] + if os.path.isdir(paths[key]): + lines.append('%s=%s' % (key, path)) + for ns in paths.get('namespace', ()): + lines.append('namespace=%s' % ns) + + with codecs.open(shared_path, 'w', encoding='utf-8') as f: + f.write('\n'.join(lines)) + return shared_path + + def get_distinfo_resource(self, path): + if path not in DIST_FILES: + raise DistlibException('invalid path for a dist-info file: ' + '%r at %r' % (path, self.path)) + finder = resources.finder_for_path(self.path) + if finder is None: + raise DistlibException('Unable to get a finder for %s' % self.path) + return finder.find(path) + + def get_distinfo_file(self, path): + """ + Returns a path located under the ``.dist-info`` directory. Returns a + string representing the path. + + :parameter path: a ``'/'``-separated path relative to the + ``.dist-info`` directory or an absolute path; + If *path* is an absolute path and doesn't start + with the ``.dist-info`` directory path, + a :class:`DistlibException` is raised + :type path: str + :rtype: str + """ + # Check if it is an absolute path # XXX use relpath, add tests + if path.find(os.sep) >= 0: + # it's an absolute path? + distinfo_dirname, path = path.split(os.sep)[-2:] + if distinfo_dirname != self.path.split(os.sep)[-1]: + raise DistlibException( + 'dist-info file %r does not belong to the %r %s ' + 'distribution' % (path, self.name, self.version)) + + # The file must be relative + if path not in DIST_FILES: + raise DistlibException('invalid path for a dist-info file: ' + '%r at %r' % (path, self.path)) + + return os.path.join(self.path, path) + + def list_distinfo_files(self): + """ + Iterates over the ``RECORD`` entries and returns paths for each line if + the path is pointing to a file located in the ``.dist-info`` directory + or one of its subdirectories. + + :returns: iterator of paths + """ + base = os.path.dirname(self.path) + for path, checksum, size in self._get_records(): + # XXX add separator or use real relpath algo + if not os.path.isabs(path): + path = os.path.join(base, path) + if path.startswith(self.path): + yield path + + def __eq__(self, other): + return (isinstance(other, InstalledDistribution) and + self.path == other.path) + + # See http://docs.python.org/reference/datamodel#object.__hash__ + __hash__ = object.__hash__ + + +class EggInfoDistribution(BaseInstalledDistribution): + """Created with the *path* of the ``.egg-info`` directory or file provided + to the constructor. It reads the metadata contained in the file itself, or + if the given path happens to be a directory, the metadata is read from the + file ``PKG-INFO`` under that directory.""" + + requested = True # as we have no way of knowing, assume it was + shared_locations = {} + + def __init__(self, path, env=None): + def set_name_and_version(s, n, v): + s.name = n + s.key = n.lower() # for case-insensitive comparisons + s.version = v + + self.path = path + self.dist_path = env + if env and env._cache_enabled and path in env._cache_egg.path: + metadata = env._cache_egg.path[path].metadata + set_name_and_version(self, metadata.name, metadata.version) + else: + metadata = self._get_metadata(path) + + # Need to be set before caching + set_name_and_version(self, metadata.name, metadata.version) + + if env and env._cache_enabled: + env._cache_egg.add(self) + super(EggInfoDistribution, self).__init__(metadata, path, env) + + def _get_metadata(self, path): + requires = None + + def parse_requires_data(data): + """Create a list of dependencies from a requires.txt file. + + *data*: the contents of a setuptools-produced requires.txt file. + """ + reqs = [] + lines = data.splitlines() + for line in lines: + line = line.strip() + if line.startswith('['): + logger.warning('Unexpected line: quitting requirement scan: %r', + line) + break + r = parse_requirement(line) + if not r: + logger.warning('Not recognised as a requirement: %r', line) + continue + if r.extras: + logger.warning('extra requirements in requires.txt are ' + 'not supported') + if not r.constraints: + reqs.append(r.name) + else: + cons = ', '.join('%s%s' % c for c in r.constraints) + reqs.append('%s (%s)' % (r.name, cons)) + return reqs + + def parse_requires_path(req_path): + """Create a list of dependencies from a requires.txt file. + + *req_path*: the path to a setuptools-produced requires.txt file. + """ + + reqs = [] + try: + with codecs.open(req_path, 'r', 'utf-8') as fp: + reqs = parse_requires_data(fp.read()) + except IOError: + pass + return reqs + + if path.endswith('.egg'): + if os.path.isdir(path): + meta_path = os.path.join(path, 'EGG-INFO', 'PKG-INFO') + metadata = Metadata(path=meta_path, scheme='legacy') + req_path = os.path.join(path, 'EGG-INFO', 'requires.txt') + requires = parse_requires_path(req_path) + else: + # FIXME handle the case where zipfile is not available + zipf = zipimport.zipimporter(path) + fileobj = StringIO( + zipf.get_data('EGG-INFO/PKG-INFO').decode('utf8')) + metadata = Metadata(fileobj=fileobj, scheme='legacy') + try: + data = zipf.get_data('EGG-INFO/requires.txt') + requires = parse_requires_data(data.decode('utf-8')) + except IOError: + requires = None + elif path.endswith('.egg-info'): + if os.path.isdir(path): + path = os.path.join(path, 'PKG-INFO') + req_path = os.path.join(path, 'requires.txt') + requires = parse_requires_path(req_path) + metadata = Metadata(path=path, scheme='legacy') + else: + raise DistlibException('path must end with .egg-info or .egg, ' + 'got %r' % path) + + if requires: + metadata.add_requirements(requires) + return metadata + + def __repr__(self): + return '' % ( + self.name, self.version, self.path) + + def __str__(self): + return "%s %s" % (self.name, self.version) + + def check_installed_files(self): + """ + Checks that the hashes and sizes of the files in ``RECORD`` are + matched by the files themselves. Returns a (possibly empty) list of + mismatches. Each entry in the mismatch list will be a tuple consisting + of the path, 'exists', 'size' or 'hash' according to what didn't match + (existence is checked first, then size, then hash), the expected + value and the actual value. + """ + mismatches = [] + record_path = os.path.join(self.path, 'installed-files.txt') + if os.path.exists(record_path): + for path, _, _ in self.list_installed_files(): + if path == record_path: + continue + if not os.path.exists(path): + mismatches.append((path, 'exists', True, False)) + return mismatches + + def list_installed_files(self): + """ + Iterates over the ``installed-files.txt`` entries and returns a tuple + ``(path, hash, size)`` for each line. + + :returns: a list of (path, hash, size) + """ + + def _md5(path): + f = open(path, 'rb') + try: + content = f.read() + finally: + f.close() + return hashlib.md5(content).hexdigest() + + def _size(path): + return os.stat(path).st_size + + record_path = os.path.join(self.path, 'installed-files.txt') + result = [] + if os.path.exists(record_path): + with codecs.open(record_path, 'r', encoding='utf-8') as f: + for line in f: + line = line.strip() + p = os.path.normpath(os.path.join(self.path, line)) + # "./" is present as a marker between installed files + # and installation metadata files + if not os.path.exists(p): + logger.warning('Non-existent file: %s', p) + if p.endswith(('.pyc', '.pyo')): + continue + #otherwise fall through and fail + if not os.path.isdir(p): + result.append((p, _md5(p), _size(p))) + result.append((record_path, None, None)) + return result + + def list_distinfo_files(self, absolute=False): + """ + Iterates over the ``installed-files.txt`` entries and returns paths for + each line if the path is pointing to a file located in the + ``.egg-info`` directory or one of its subdirectories. + + :parameter absolute: If *absolute* is ``True``, each returned path is + transformed into a local absolute path. Otherwise the + raw value from ``installed-files.txt`` is returned. + :type absolute: boolean + :returns: iterator of paths + """ + record_path = os.path.join(self.path, 'installed-files.txt') + skip = True + with codecs.open(record_path, 'r', encoding='utf-8') as f: + for line in f: + line = line.strip() + if line == './': + skip = False + continue + if not skip: + p = os.path.normpath(os.path.join(self.path, line)) + if p.startswith(self.path): + if absolute: + yield p + else: + yield line + + def __eq__(self, other): + return (isinstance(other, EggInfoDistribution) and + self.path == other.path) + + # See http://docs.python.org/reference/datamodel#object.__hash__ + __hash__ = object.__hash__ + +new_dist_class = InstalledDistribution +old_dist_class = EggInfoDistribution + + +class DependencyGraph(object): + """ + Represents a dependency graph between distributions. + + The dependency relationships are stored in an ``adjacency_list`` that maps + distributions to a list of ``(other, label)`` tuples where ``other`` + is a distribution and the edge is labeled with ``label`` (i.e. the version + specifier, if such was provided). Also, for more efficient traversal, for + every distribution ``x``, a list of predecessors is kept in + ``reverse_list[x]``. An edge from distribution ``a`` to + distribution ``b`` means that ``a`` depends on ``b``. If any missing + dependencies are found, they are stored in ``missing``, which is a + dictionary that maps distributions to a list of requirements that were not + provided by any other distributions. + """ + + def __init__(self): + self.adjacency_list = {} + self.reverse_list = {} + self.missing = {} + + def add_distribution(self, distribution): + """Add the *distribution* to the graph. + + :type distribution: :class:`distutils2.database.InstalledDistribution` + or :class:`distutils2.database.EggInfoDistribution` + """ + self.adjacency_list[distribution] = [] + self.reverse_list[distribution] = [] + #self.missing[distribution] = [] + + def add_edge(self, x, y, label=None): + """Add an edge from distribution *x* to distribution *y* with the given + *label*. + + :type x: :class:`distutils2.database.InstalledDistribution` or + :class:`distutils2.database.EggInfoDistribution` + :type y: :class:`distutils2.database.InstalledDistribution` or + :class:`distutils2.database.EggInfoDistribution` + :type label: ``str`` or ``None`` + """ + self.adjacency_list[x].append((y, label)) + # multiple edges are allowed, so be careful + if x not in self.reverse_list[y]: + self.reverse_list[y].append(x) + + def add_missing(self, distribution, requirement): + """ + Add a missing *requirement* for the given *distribution*. + + :type distribution: :class:`distutils2.database.InstalledDistribution` + or :class:`distutils2.database.EggInfoDistribution` + :type requirement: ``str`` + """ + logger.debug('%s missing %r', distribution, requirement) + self.missing.setdefault(distribution, []).append(requirement) + + def _repr_dist(self, dist): + return '%s %s' % (dist.name, dist.version) + + def repr_node(self, dist, level=1): + """Prints only a subgraph""" + output = [self._repr_dist(dist)] + for other, label in self.adjacency_list[dist]: + dist = self._repr_dist(other) + if label is not None: + dist = '%s [%s]' % (dist, label) + output.append(' ' * level + str(dist)) + suboutput = self.repr_node(other, level + 1) + subs = suboutput.split('\n') + output.extend(subs[1:]) + return '\n'.join(output) + + def to_dot(self, f, skip_disconnected=True): + """Writes a DOT output for the graph to the provided file *f*. + + If *skip_disconnected* is set to ``True``, then all distributions + that are not dependent on any other distribution are skipped. + + :type f: has to support ``file``-like operations + :type skip_disconnected: ``bool`` + """ + disconnected = [] + + f.write("digraph dependencies {\n") + for dist, adjs in self.adjacency_list.items(): + if len(adjs) == 0 and not skip_disconnected: + disconnected.append(dist) + for other, label in adjs: + if not label is None: + f.write('"%s" -> "%s" [label="%s"]\n' % + (dist.name, other.name, label)) + else: + f.write('"%s" -> "%s"\n' % (dist.name, other.name)) + if not skip_disconnected and len(disconnected) > 0: + f.write('subgraph disconnected {\n') + f.write('label = "Disconnected"\n') + f.write('bgcolor = red\n') + + for dist in disconnected: + f.write('"%s"' % dist.name) + f.write('\n') + f.write('}\n') + f.write('}\n') + + def topological_sort(self): + """ + Perform a topological sort of the graph. + :return: A tuple, the first element of which is a topologically sorted + list of distributions, and the second element of which is a + list of distributions that cannot be sorted because they have + circular dependencies and so form a cycle. + """ + result = [] + # Make a shallow copy of the adjacency list + alist = {} + for k, v in self.adjacency_list.items(): + alist[k] = v[:] + while True: + # See what we can remove in this run + to_remove = [] + for k, v in list(alist.items())[:]: + if not v: + to_remove.append(k) + del alist[k] + if not to_remove: + # What's left in alist (if anything) is a cycle. + break + # Remove from the adjacency list of others + for k, v in alist.items(): + alist[k] = [(d, r) for d, r in v if d not in to_remove] + logger.debug('Moving to result: %s', + ['%s (%s)' % (d.name, d.version) for d in to_remove]) + result.extend(to_remove) + return result, list(alist.keys()) + + def __repr__(self): + """Representation of the graph""" + output = [] + for dist, adjs in self.adjacency_list.items(): + output.append(self.repr_node(dist)) + return '\n'.join(output) + + +def make_graph(dists, scheme='default'): + """Makes a dependency graph from the given distributions. + + :parameter dists: a list of distributions + :type dists: list of :class:`distutils2.database.InstalledDistribution` and + :class:`distutils2.database.EggInfoDistribution` instances + :rtype: a :class:`DependencyGraph` instance + """ + scheme = get_scheme(scheme) + graph = DependencyGraph() + provided = {} # maps names to lists of (version, dist) tuples + + # first, build the graph and find out what's provided + for dist in dists: + graph.add_distribution(dist) + + for p in dist.provides: + name, version = parse_name_and_version(p) + logger.debug('Add to provided: %s, %s, %s', name, version, dist) + provided.setdefault(name, []).append((version, dist)) + + # now make the edges + for dist in dists: + requires = (dist.run_requires | dist.meta_requires | + dist.build_requires | dist.dev_requires) + for req in requires: + try: + matcher = scheme.matcher(req) + except UnsupportedVersionError: + # XXX compat-mode if cannot read the version + logger.warning('could not read version %r - using name only', + req) + name = req.split()[0] + matcher = scheme.matcher(name) + + name = matcher.key # case-insensitive + + matched = False + if name in provided: + for version, provider in provided[name]: + try: + match = matcher.match(version) + except UnsupportedVersionError: + match = False + + if match: + graph.add_edge(dist, provider, req) + matched = True + break + if not matched: + graph.add_missing(dist, req) + return graph + + +def get_dependent_dists(dists, dist): + """Recursively generate a list of distributions from *dists* that are + dependent on *dist*. + + :param dists: a list of distributions + :param dist: a distribution, member of *dists* for which we are interested + """ + if dist not in dists: + raise DistlibException('given distribution %r is not a member ' + 'of the list' % dist.name) + graph = make_graph(dists) + + dep = [dist] # dependent distributions + todo = graph.reverse_list[dist] # list of nodes we should inspect + + while todo: + d = todo.pop() + dep.append(d) + for succ in graph.reverse_list[d]: + if succ not in dep: + todo.append(succ) + + dep.pop(0) # remove dist from dep, was there to prevent infinite loops + return dep + + +def get_required_dists(dists, dist): + """Recursively generate a list of distributions from *dists* that are + required by *dist*. + + :param dists: a list of distributions + :param dist: a distribution, member of *dists* for which we are interested + """ + if dist not in dists: + raise DistlibException('given distribution %r is not a member ' + 'of the list' % dist.name) + graph = make_graph(dists) + + req = [] # required distributions + todo = graph.adjacency_list[dist] # list of nodes we should inspect + + while todo: + d = todo.pop()[0] + req.append(d) + for pred in graph.adjacency_list[d]: + if pred not in req: + todo.append(pred) + + return req + + +def make_dist(name, version, **kwargs): + """ + A convenience method for making a dist given just a name and version. + """ + summary = kwargs.pop('summary', 'Placeholder for summary') + md = Metadata(**kwargs) + md.name = name + md.version = version + md.summary = summary or 'Plaeholder for summary' + return Distribution(md) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/index.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/index.py new file mode 100644 index 0000000..30b2aa5 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/index.py @@ -0,0 +1,480 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +import hashlib +import logging +import os +import shutil +import subprocess +import tempfile +try: + from threading import Thread +except ImportError: + from dummy_threading import Thread + +from distlib import DistlibException +from distlib.compat import (HTTPBasicAuthHandler, Request, HTTPPasswordMgr, + urlparse, build_opener) +from distlib.util import cached_property, zip_dir + +logger = logging.getLogger(__name__) + +DEFAULT_INDEX = 'https://pypi.python.org/pypi' +DEFAULT_REALM = 'pypi' + +class PackageIndex(object): + """ + This class represents a package index compatible with PyPI, the Python + Package Index. + """ + + boundary = b'----------ThIs_Is_tHe_distlib_index_bouNdaRY_$' + + def __init__(self, url=None): + """ + Initialise an instance. + + :param url: The URL of the index. If not specified, the URL for PyPI is + used. + """ + self.url = url or DEFAULT_INDEX + self.read_configuration() + scheme, netloc, path, params, query, frag = urlparse(self.url) + if params or query or frag or scheme not in ('http', 'https'): + raise DistlibException('invalid repository: %s' % self.url) + self.password_handler = None + self.ssl_verifier = None + self.gpg = None + self.gpg_home = None + with open(os.devnull, 'w') as sink: + for s in ('gpg2', 'gpg'): + try: + rc = subprocess.check_call([s, '--version'], stdout=sink, + stderr=sink) + if rc == 0: + self.gpg = s + break + except OSError: + pass + + def _get_pypirc_command(self): + """ + Get the distutils command for interacting with PyPI configurations. + :return: the command. + """ + from distutils.core import Distribution + from distutils.config import PyPIRCCommand + d = Distribution() + return PyPIRCCommand(d) + + def read_configuration(self): + """ + Read the PyPI access configuration as supported by distutils, getting + PyPI to do the acutal work. This populates ``username``, ``password``, + ``realm`` and ``url`` attributes from the configuration. + """ + # get distutils to do the work + c = self._get_pypirc_command() + c.repository = self.url + cfg = c._read_pypirc() + self.username = cfg.get('username') + self.password = cfg.get('password') + self.realm = cfg.get('realm', 'pypi') + self.url = cfg.get('repository', self.url) + + def save_configuration(self): + """ + Save the PyPI access configuration. You must have set ``username`` and + ``password`` attributes before calling this method. + + Again, distutils is used to do the actual work. + """ + self.check_credentials() + # get distutils to do the work + c = self._get_pypirc_command() + c._store_pypirc(self.username, self.password) + + def check_credentials(self): + """ + Check that ``username`` and ``password`` have been set, and raise an + exception if not. + """ + if self.username is None or self.password is None: + raise DistlibException('username and password must be set') + pm = HTTPPasswordMgr() + _, netloc, _, _, _, _ = urlparse(self.url) + pm.add_password(self.realm, netloc, self.username, self.password) + self.password_handler = HTTPBasicAuthHandler(pm) + + def register(self, metadata): + """ + Register a distribution on PyPI, using the provided metadata. + + :param metadata: A :class:`Metadata` instance defining at least a name + and version number for the distribution to be + registered. + :return: The HTTP response received from PyPI upon submission of the + request. + """ + self.check_credentials() + metadata.validate() + d = metadata.todict() + d[':action'] = 'verify' + request = self.encode_request(d.items(), []) + response = self.send_request(request) + d[':action'] = 'submit' + request = self.encode_request(d.items(), []) + return self.send_request(request) + + def _reader(self, name, stream, outbuf): + """ + Thread runner for reading lines of from a subprocess into a buffer. + + :param name: The logical name of the stream (used for logging only). + :param stream: The stream to read from. This will typically a pipe + connected to the output stream of a subprocess. + :param outbuf: The list to append the read lines to. + """ + while True: + s = stream.readline() + if not s: + break + s = s.decode('utf-8').rstrip() + outbuf.append(s) + logger.debug('%s: %s' % (name, s)) + stream.close() + + def get_sign_command(self, filename, signer, sign_password): + """ + Return a suitable command for signing a file. + + :param filename: The pathname to the file to be signed. + :param signer: The identifier of the signer of the file. + :param sign_password: The passphrase for the signer's + private key used for signing. + :return: The signing command as a list suitable to be + passed to :class:`subprocess.Popen`. + """ + cmd = [self.gpg, '--status-fd', '2', '--no-tty'] + if self.gpg_home: + cmd.extend(['--homedir', self.gpg_home]) + if sign_password is not None: + cmd.extend(['--batch', '--passphrase-fd', '0']) + td = tempfile.mkdtemp() + sf = os.path.join(td, os.path.basename(filename) + '.asc') + cmd.extend(['--detach-sign', '--armor', '--local-user', + signer, '--output', sf, filename]) + logger.debug('invoking: %s', ' '.join(cmd)) + return cmd, sf + + def run_command(self, cmd, input_data=None): + """ + Run a command in a child process , passing it any input data specified. + + :param cmd: The command to run. + :param input_data: If specified, this must be a byte string containing + data to be sent to the child process. + :return: A tuple consisting of the subprocess' exit code, a list of + lines read from the subprocess' ``stdout``, and a list of + lines read from the subprocess' ``stderr``. + """ + kwargs = { + 'stdout': subprocess.PIPE, + 'stderr': subprocess.PIPE, + } + if input_data is not None: + kwargs['stdin'] = subprocess.PIPE + stdout = [] + stderr = [] + p = subprocess.Popen(cmd, **kwargs) + # We don't use communicate() here because we may need to + # get clever with interacting with the command + t1 = Thread(target=self._reader, args=('stdout', p.stdout, stdout)) + t1.start() + t2 = Thread(target=self._reader, args=('stderr', p.stderr, stderr)) + t2.start() + if input_data is not None: + p.stdin.write(input_data) + p.stdin.close() + + p.wait() + t1.join() + t2.join() + return p.returncode, stdout, stderr + + def sign_file(self, filename, signer, sign_password): + """ + Sign a file. + + :param filename: The pathname to the file to be signed. + :param signer: The identifier of the signer of the file. + :param sign_password: The passphrase for the signer's + private key used for signing. + :return: The absolute pathname of the file where the signature is + stored. + """ + cmd, sig_file = self.get_sign_command(filename, signer, sign_password) + rc, stdout, stderr = self.run_command(cmd, + sign_password.encode('utf-8')) + if rc != 0: + raise DistlibException('sign command failed with error ' + 'code %s' % rc) + return sig_file + + def upload_file(self, metadata, filename, signer=None, sign_password=None, + filetype='sdist', pyversion='source'): + """ + Upload a release file to the index. + + :param metadata: A :class:`Metadata` instance defining at least a name + and version number for the file to be uploaded. + :param filename: The pathname of the file to be uploaded. + :param signer: The identifier of the signer of the file. + :param sign_password: The passphrase for the signer's + private key used for signing. + :param filetype: The type of the file being uploaded. This is the + distutils command which produced that file, e.g. + ``sdist`` or ``bdist_wheel``. + :param pyversion: The version of Python which the release relates + to. For code compatible with any Python, this would + be ``source``, otherwise it would be e.g. ``3.2``. + :return: The HTTP response received from PyPI upon submission of the + request. + """ + self.check_credentials() + if not os.path.exists(filename): + raise DistlibException('not found: %s' % filename) + metadata.validate() + d = metadata.todict() + sig_file = None + if signer: + if not self.gpg: + logger.warning('no signing program available - not signed') + else: + sig_file = self.sign_file(filename, signer, sign_password) + with open(filename, 'rb') as f: + file_data = f.read() + md5_digest = hashlib.md5(file_data).hexdigest() + sha256_digest = hashlib.sha256(file_data).hexdigest() + d.update({ + ':action': 'file_upload', + 'protcol_version': '1', + 'filetype': filetype, + 'pyversion': pyversion, + 'md5_digest': md5_digest, + 'sha256_digest': sha256_digest, + }) + files = [('content', os.path.basename(filename), file_data)] + if sig_file: + with open(sig_file, 'rb') as f: + sig_data = f.read() + files.append(('gpg_signature', os.path.basename(sig_file), + sig_data)) + shutil.rmtree(os.path.dirname(sig_file)) + request = self.encode_request(d.items(), files) + return self.send_request(request) + + def upload_documentation(self, metadata, doc_dir): + """ + Upload documentation to the index. + + :param metadata: A :class:`Metadata` instance defining at least a name + and version number for the documentation to be + uploaded. + :param doc_dir: The pathname of the directory which contains the + documentation. This should be the directory that + contains the ``index.html`` for the documentation. + :return: The HTTP response received from PyPI upon submission of the + request. + """ + self.check_credentials() + if not os.path.isdir(doc_dir): + raise DistlibException('not a directory: %r' % doc_dir) + fn = os.path.join(doc_dir, 'index.html') + if not os.path.exists(fn): + raise DistlibException('not found: %r' % fn) + metadata.validate() + name, version = metadata.name, metadata.version + zip_data = zip_dir(doc_dir).getvalue() + fields = [(':action', 'doc_upload'), + ('name', name), ('version', version)] + files = [('content', name, zip_data)] + request = self.encode_request(fields, files) + return self.send_request(request) + + def get_verify_command(self, signature_filename, data_filename): + """ + Return a suitable command for verifying a file. + + :param signature_filename: The pathname to the file containing the + signature. + :param data_filename: The pathname to the file containing the + signed data. + :return: The verifying command as a list suitable to be + passed to :class:`subprocess.Popen`. + """ + cmd = [self.gpg, '--status-fd', '2', '--no-tty'] + if self.gpg_home: + cmd.extend(['--homedir', self.gpg_home]) + cmd.extend(['--verify', signature_filename, data_filename]) + logger.debug('invoking: %s', ' '.join(cmd)) + return cmd + + def verify_signature(self, signature_filename, data_filename): + """ + Verify a signature for a file. + + :param signature_filename: The pathname to the file containing the + signature. + :param data_filename: The pathname to the file containing the + signed data. + :return: True if the signature was verified, else False. + """ + if not self.gpg: + raise DistlibException('verification unavailable because gpg ' + 'unavailable') + cmd = self.get_verify_command(signature_filename, data_filename) + rc, stdout, stderr = self.run_command(cmd) + if rc not in (0, 1): + raise DistlibException('verify command failed with error ' + 'code %s' % rc) + return rc == 0 + + def download_file(self, url, destfile, digest=None, reporthook=None): + """ + This is a convenience method for downloading a file from an URL. + Normally, this will be a file from the index, though currently + no check is made for this (i.e. a file can be downloaded from + anywhere). + + The method is just like the :func:`urlretrieve` function in the + standard library, except that it allows digest computation to be + done during download and checking that the downloaded data + matched any expected value. + + :param url: The URL of the file to be downloaded (assumed to be + available via an HTTP GET request). + :param destfile: The pathname where the downloaded file is to be + saved. + :param digest: If specified, this must be a (hasher, value) + tuple, where hasher is the algorithm used (e.g. + ``'md5'``) and ``value`` is the expected value. + :param reporthook: The same as for :func:`urlretrieve` in the + standard library. + """ + if digest is None: + digester = None + logger.debug('No digest specified') + else: + if isinstance(digest, (list, tuple)): + hasher, digest = digest + else: + hasher = 'md5' + digester = getattr(hashlib, hasher)() + logger.debug('Digest specified: %s' % digest) + # The following code is equivalent to urlretrieve. + # We need to do it this way so that we can compute the + # digest of the file as we go. + with open(destfile, 'wb') as dfp: + # addinfourl is not a context manager on 2.x + # so we have to use try/finally + sfp = self.send_request(Request(url)) + try: + headers = sfp.info() + blocksize = 8192 + size = -1 + read = 0 + blocknum = 0 + if "content-length" in headers: + size = int(headers["Content-Length"]) + if reporthook: + reporthook(blocknum, blocksize, size) + while True: + block = sfp.read(blocksize) + if not block: + break + read += len(block) + dfp.write(block) + if digester: + digester.update(block) + blocknum += 1 + if reporthook: + reporthook(blocknum, blocksize, size) + finally: + sfp.close() + + # check that we got the whole file, if we can + if size >= 0 and read < size: + raise DistlibException( + 'retrieval incomplete: got only %d out of %d bytes' + % (read, size)) + # if we have a digest, it must match. + if digester: + actual = digester.hexdigest() + if digest != actual: + raise DistlibException('%s digest mismatch for %s: expected ' + '%s, got %s' % (hasher, destfile, + digest, actual)) + logger.debug('Digest verified: %s', digest) + + def send_request(self, req): + """ + Send a standard library :class:`Request` to PyPI and return its + response. + + :param req: The request to send. + :return: The HTTP response from PyPI (a standard library HTTPResponse). + """ + handlers = [] + if self.password_handler: + handlers.append(self.password_handler) + if self.ssl_verifier: + handlers.append(self.ssl_verifier) + opener = build_opener(*handlers) + return opener.open(req) + + def encode_request(self, fields, files): + """ + Encode fields and files for posting to an HTTP server. + + :param fields: The fields to send as a list of (fieldname, value) + tuples. + :param files: The files to send as a list of (fieldname, filename, + file_bytes) tuple. + """ + # Adapted from packaging, which in turn was adapted from + # http://code.activestate.com/recipes/146306 + + parts = [] + boundary = self.boundary + for k, values in fields: + if not isinstance(values, (list, tuple)): + values = [values] + + for v in values: + parts.extend(( + b'--' + boundary, + ('Content-Disposition: form-data; name="%s"' % + k).encode('utf-8'), + b'', + v.encode('utf-8'))) + for key, filename, value in files: + parts.extend(( + b'--' + boundary, + ('Content-Disposition: form-data; name="%s"; filename="%s"' % + (key, filename)).encode('utf-8'), + b'', + value)) + + parts.extend((b'--' + boundary + b'--', b'')) + + body = b'\r\n'.join(parts) + ct = b'multipart/form-data; boundary=' + boundary + headers = { + 'Content-type': ct, + 'Content-length': str(len(body)) + } + return Request(self.url, body, headers) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/locators.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/locators.py new file mode 100644 index 0000000..ffe9931 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/locators.py @@ -0,0 +1,1187 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2013 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# + +import gzip +from io import BytesIO +import json +import logging +import os +import posixpath +import re +try: + import threading +except ImportError: + import dummy_threading as threading +import zlib + +from . import DistlibException +from .compat import (urljoin, urlparse, urlunparse, url2pathname, pathname2url, + queue, quote, unescape, string_types, build_opener, + HTTPRedirectHandler as BaseRedirectHandler, + Request, HTTPError, URLError) +from .database import Distribution, DistributionPath, make_dist +from .metadata import Metadata +from .util import (cached_property, parse_credentials, ensure_slash, + split_filename, get_project_data, parse_requirement, + parse_name_and_version, ServerProxy) +from .version import get_scheme, UnsupportedVersionError +from .wheel import Wheel, is_compatible + +logger = logging.getLogger(__name__) + +HASHER_HASH = re.compile('^(\w+)=([a-f0-9]+)') +CHARSET = re.compile(r';\s*charset\s*=\s*(.*)\s*$', re.I) +HTML_CONTENT_TYPE = re.compile('text/html|application/x(ht)?ml') +DEFAULT_INDEX = 'http://python.org/pypi' + +def get_all_distribution_names(url=None): + """ + Return all distribution names known by an index. + :param url: The URL of the index. + :return: A list of all known distribution names. + """ + if url is None: + url = DEFAULT_INDEX + client = ServerProxy(url, timeout=3.0) + return client.list_packages() + +class RedirectHandler(BaseRedirectHandler): + """ + A class to work around a bug in some Python 3.2.x releases. + """ + # There's a bug in the base version for some 3.2.x + # (e.g. 3.2.2 on Ubuntu Oneiric). If a Location header + # returns e.g. /abc, it bails because it says the scheme '' + # is bogus, when actually it should use the request's + # URL for the scheme. See Python issue #13696. + def http_error_302(self, req, fp, code, msg, headers): + # Some servers (incorrectly) return multiple Location headers + # (so probably same goes for URI). Use first header. + newurl = None + for key in ('location', 'uri'): + if key in headers: + newurl = headers[key] + break + if newurl is None: + return + urlparts = urlparse(newurl) + if urlparts.scheme == '': + newurl = urljoin(req.get_full_url(), newurl) + if hasattr(headers, 'replace_header'): + headers.replace_header(key, newurl) + else: + headers[key] = newurl + return BaseRedirectHandler.http_error_302(self, req, fp, code, msg, + headers) + + http_error_301 = http_error_303 = http_error_307 = http_error_302 + +class Locator(object): + """ + A base class for locators - things that locate distributions. + """ + source_extensions = ('.tar.gz', '.tar.bz2', '.tar', '.zip', '.tgz', '.tbz') + binary_extensions = ('.egg', '.exe', '.whl') + excluded_extensions = ('.pdf',) + + # A list of tags indicating which wheels you want to match. The default + # value of None matches against the tags compatible with the running + # Python. If you want to match other values, set wheel_tags on a locator + # instance to a list of tuples (pyver, abi, arch) which you want to match. + wheel_tags = None + + downloadable_extensions = source_extensions + ('.whl',) + + def __init__(self, scheme='default'): + """ + Initialise an instance. + :param scheme: Because locators look for most recent versions, they + need to know the version scheme to use. This specifies + the current PEP-recommended scheme - use ``'legacy'`` + if you need to support existing distributions on PyPI. + """ + self._cache = {} + self.scheme = scheme + # Because of bugs in some of the handlers on some of the platforms, + # we use our own opener rather than just using urlopen. + self.opener = build_opener(RedirectHandler()) + # If get_project() is called from locate(), the matcher instance + # is set from the requirement passed to locate(). See issue #18 for + # why this can be useful to know. + self.matcher = None + + def clear_cache(self): + self._cache.clear() + + def _get_scheme(self): + return self._scheme + + def _set_scheme(self, value): + self._scheme = value + + scheme = property(_get_scheme, _set_scheme) + + def _get_project(self, name): + """ + For a given project, get a dictionary mapping available versions to Distribution + instances. + + This should be implemented in subclasses. + + If called from a locate() request, self.matcher will be set to a + matcher for the requirement to satisfy, otherwise it will be None. + """ + raise NotImplementedError('Please implement in the subclass') + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + raise NotImplementedError('Please implement in the subclass') + + def get_project(self, name): + """ + For a given project, get a dictionary mapping available versions to Distribution + instances. + + This calls _get_project to do all the work, and just implements a caching layer on top. + """ + if self._cache is None: + result = self._get_project(name) + elif name in self._cache: + result = self._cache[name] + else: + result = self._get_project(name) + self._cache[name] = result + return result + + def score_url(self, url): + """ + Give an url a score which can be used to choose preferred URLs + for a given project release. + """ + t = urlparse(url) + return (t.scheme != 'https', 'pypi.python.org' in t.netloc, + posixpath.basename(t.path)) + + def prefer_url(self, url1, url2): + """ + Choose one of two URLs where both are candidates for distribution + archives for the same version of a distribution (for example, + .tar.gz vs. zip). + + The current implement favours http:// URLs over https://, archives + from PyPI over those from other locations and then the archive name. + """ + result = url2 + if url1: + s1 = self.score_url(url1) + s2 = self.score_url(url2) + if s1 > s2: + result = url1 + if result != url2: + logger.debug('Not replacing %r with %r', url1, url2) + else: + logger.debug('Replacing %r with %r', url1, url2) + return result + + def split_filename(self, filename, project_name): + """ + Attempt to split a filename in project name, version and Python version. + """ + return split_filename(filename, project_name) + + def convert_url_to_download_info(self, url, project_name): + """ + See if a URL is a candidate for a download URL for a project (the URL + has typically been scraped from an HTML page). + + If it is, a dictionary is returned with keys "name", "version", + "filename" and "url"; otherwise, None is returned. + """ + def same_project(name1, name2): + name1, name2 = name1.lower(), name2.lower() + if name1 == name2: + result = True + else: + # distribute replaces '-' by '_' in project names, so it + # can tell where the version starts in a filename. + result = name1.replace('_', '-') == name2.replace('_', '-') + return result + + result = None + scheme, netloc, path, params, query, frag = urlparse(url) + if frag.lower().startswith('egg='): + logger.debug('%s: version hint in fragment: %r', + project_name, frag) + m = HASHER_HASH.match(frag) + if m: + algo, digest = m.groups() + else: + algo, digest = None, None + origpath = path + if path and path[-1] == '/': + path = path[:-1] + if path.endswith('.whl'): + try: + wheel = Wheel(path) + if is_compatible(wheel, self.wheel_tags): + if project_name is None: + include = True + else: + include = same_project(wheel.name, project_name) + if include: + result = { + 'name': wheel.name, + 'version': wheel.version, + 'filename': wheel.filename, + 'url': urlunparse((scheme, netloc, origpath, + params, query, '')), + 'python-version': ', '.join( + ['.'.join(list(v[2:])) for v in wheel.pyver]), + } + except Exception as e: + logger.warning('invalid path for wheel: %s', path) + elif path.endswith(self.downloadable_extensions): + path = filename = posixpath.basename(path) + for ext in self.downloadable_extensions: + if path.endswith(ext): + path = path[:-len(ext)] + t = self.split_filename(path, project_name) + if not t: + logger.debug('No match for project/version: %s', path) + else: + name, version, pyver = t + if not project_name or same_project(project_name, name): + result = { + 'name': name, + 'version': version, + 'filename': filename, + 'url': urlunparse((scheme, netloc, origpath, + params, query, '')), + #'packagetype': 'sdist', + } + if pyver: + result['python-version'] = pyver + break + if result and algo: + result['%s_digest' % algo] = digest + return result + + def _get_digest(self, info): + """ + Get a digest from a dictionary by looking at keys of the form + 'algo_digest'. + + Returns a 2-tuple (algo, digest) if found, else None. Currently + looks only for SHA256, then MD5. + """ + result = None + for algo in ('sha256', 'md5'): + key = '%s_digest' % algo + if key in info: + result = (algo, info[key]) + break + return result + + def _update_version_data(self, result, info): + """ + Update a result dictionary (the final result from _get_project) with a dictionary for a + specific version, whih typically holds information gleaned from a filename or URL for an + archive for the distribution. + """ + name = info.pop('name') + version = info.pop('version') + if version in result: + dist = result[version] + md = dist.metadata + else: + dist = make_dist(name, version, scheme=self.scheme) + md = dist.metadata + dist.digest = self._get_digest(info) + if md.source_url != info['url']: + md.source_url = self.prefer_url(md.source_url, info['url']) + dist.locator = self + result[version] = dist + + def locate(self, requirement, prereleases=False): + """ + Find the most recent distribution which matches the given + requirement. + + :param requirement: A requirement of the form 'foo (1.0)' or perhaps + 'foo (>= 1.0, < 2.0, != 1.3)' + :param prereleases: If ``True``, allow pre-release versions + to be located. Otherwise, pre-release versions + are not returned. + :return: A :class:`Distribution` instance, or ``None`` if no such + distribution could be located. + """ + result = None + r = parse_requirement(requirement) + if r is None: + raise DistlibException('Not a valid requirement: %r' % requirement) + scheme = get_scheme(self.scheme) + self.matcher = matcher = scheme.matcher(r.requirement) + logger.debug('matcher: %s (%s)', matcher, type(matcher).__name__) + versions = self.get_project(r.name) + if versions: + # sometimes, versions are invalid + slist = [] + vcls = matcher.version_class + for k in versions: + try: + if not matcher.match(k): + logger.debug('%s did not match %r', matcher, k) + else: + if prereleases or not vcls(k).is_prerelease: + slist.append(k) + else: + logger.debug('skipping pre-release ' + 'version %s of %s', k, matcher.name) + except Exception: + logger.warning('error matching %s with %r', matcher, k) + pass # slist.append(k) + if len(slist) > 1: + slist = sorted(slist, key=scheme.key) + if slist: + logger.debug('sorted list: %s', slist) + result = versions[slist[-1]] + if result and r.extras: + result.extras = r.extras + self.matcher = None + return result + + +class PyPIRPCLocator(Locator): + """ + This locator uses XML-RPC to locate distributions. It therefore + cannot be used with simple mirrors (that only mirror file content). + """ + def __init__(self, url, **kwargs): + """ + Initialise an instance. + + :param url: The URL to use for XML-RPC. + :param kwargs: Passed to the superclass constructor. + """ + super(PyPIRPCLocator, self).__init__(**kwargs) + self.base_url = url + self.client = ServerProxy(url, timeout=3.0) + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + return set(self.client.list_packages()) + + def _get_project(self, name): + result = {} + versions = self.client.package_releases(name, True) + for v in versions: + urls = self.client.release_urls(name, v) + data = self.client.release_data(name, v) + metadata = Metadata(scheme=self.scheme) + metadata.name = data['name'] + metadata.version = data['version'] + metadata.license = data.get('license') + metadata.keywords = data.get('keywords', []) + metadata.summary = data.get('summary') + dist = Distribution(metadata) + if urls: + info = urls[0] + metadata.source_url = info['url'] + dist.digest = self._get_digest(info) + dist.locator = self + result[v] = dist + return result + +class PyPIJSONLocator(Locator): + """ + This locator uses PyPI's JSON interface. It's very limited in functionality + nad probably not worth using. + """ + def __init__(self, url, **kwargs): + super(PyPIJSONLocator, self).__init__(**kwargs) + self.base_url = ensure_slash(url) + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + raise NotImplementedError('Not available from this locator') + + def _get_project(self, name): + result = {} + url = urljoin(self.base_url, '%s/json' % quote(name)) + try: + resp = self.opener.open(url) + data = resp.read().decode() # for now + d = json.loads(data) + md = Metadata(scheme=self.scheme) + data = d['info'] + md.name = data['name'] + md.version = data['version'] + md.license = data.get('license') + md.keywords = data.get('keywords', []) + md.summary = data.get('summary') + dist = Distribution(md) + urls = d['urls'] + if urls: + info = urls[0] + md.source_url = info['url'] + dist.digest = self._get_digest(info) + dist.locator = self + result[md.version] = dist + except Exception as e: + logger.exception('JSON fetch failed: %s', e) + return result + + +class Page(object): + """ + This class represents a scraped HTML page. + """ + # The following slightly hairy-looking regex just looks for the contents of + # an anchor link, which has an attribute "href" either immediately preceded + # or immediately followed by a "rel" attribute. The attribute values can be + # declared with double quotes, single quotes or no quotes - which leads to + # the length of the expression. + _href = re.compile(""" +(rel\s*=\s*(?:"(?P[^"]*)"|'(?P[^']*)'|(?P[^>\s\n]*))\s+)? +href\s*=\s*(?:"(?P[^"]*)"|'(?P[^']*)'|(?P[^>\s\n]*)) +(\s+rel\s*=\s*(?:"(?P[^"]*)"|'(?P[^']*)'|(?P[^>\s\n]*)))? +""", re.I | re.S | re.X) + _base = re.compile(r"""]+)""", re.I | re.S) + + def __init__(self, data, url): + """ + Initialise an instance with the Unicode page contents and the URL they + came from. + """ + self.data = data + self.base_url = self.url = url + m = self._base.search(self.data) + if m: + self.base_url = m.group(1) + + _clean_re = re.compile(r'[^a-z0-9$&+,/:;=?@.#%_\\|-]', re.I) + + @cached_property + def links(self): + """ + Return the URLs of all the links on a page together with information + about their "rel" attribute, for determining which ones to treat as + downloads and which ones to queue for further scraping. + """ + def clean(url): + "Tidy up an URL." + scheme, netloc, path, params, query, frag = urlparse(url) + return urlunparse((scheme, netloc, quote(path), + params, query, frag)) + + result = set() + for match in self._href.finditer(self.data): + d = match.groupdict('') + rel = (d['rel1'] or d['rel2'] or d['rel3'] or + d['rel4'] or d['rel5'] or d['rel6']) + url = d['url1'] or d['url2'] or d['url3'] + url = urljoin(self.base_url, url) + url = unescape(url) + url = self._clean_re.sub(lambda m: '%%%2x' % ord(m.group(0)), url) + result.add((url, rel)) + # We sort the result, hoping to bring the most recent versions + # to the front + result = sorted(result, key=lambda t: t[0], reverse=True) + return result + + +class SimpleScrapingLocator(Locator): + """ + A locator which scrapes HTML pages to locate downloads for a distribution. + This runs multiple threads to do the I/O; performance is at least as good + as pip's PackageFinder, which works in an analogous fashion. + """ + + # These are used to deal with various Content-Encoding schemes. + decoders = { + 'deflate': zlib.decompress, + 'gzip': lambda b: gzip.GzipFile(fileobj=BytesIO(d)).read(), + 'none': lambda b: b, + } + + def __init__(self, url, timeout=None, num_workers=10, **kwargs): + """ + Initialise an instance. + :param url: The root URL to use for scraping. + :param timeout: The timeout, in seconds, to be applied to requests. + This defaults to ``None`` (no timeout specified). + :param num_workers: The number of worker threads you want to do I/O, + This defaults to 10. + :param kwargs: Passed to the superclass. + """ + super(SimpleScrapingLocator, self).__init__(**kwargs) + self.base_url = ensure_slash(url) + self.timeout = timeout + self._page_cache = {} + self._seen = set() + self._to_fetch = queue.Queue() + self._bad_hosts = set() + self.skip_externals = False + self.num_workers = num_workers + self._lock = threading.RLock() + + def _prepare_threads(self): + """ + Threads are created only when get_project is called, and terminate + before it returns. They are there primarily to parallelise I/O (i.e. + fetching web pages). + """ + self._threads = [] + for i in range(self.num_workers): + t = threading.Thread(target=self._fetch) + t.setDaemon(True) + t.start() + self._threads.append(t) + + def _wait_threads(self): + """ + Tell all the threads to terminate (by sending a sentinel value) and + wait for them to do so. + """ + # Note that you need two loops, since you can't say which + # thread will get each sentinel + for t in self._threads: + self._to_fetch.put(None) # sentinel + for t in self._threads: + t.join() + self._threads = [] + + def _get_project(self, name): + self.result = result = {} + self.project_name = name + url = urljoin(self.base_url, '%s/' % quote(name)) + self._seen.clear() + self._page_cache.clear() + self._prepare_threads() + try: + logger.debug('Queueing %s', url) + self._to_fetch.put(url) + self._to_fetch.join() + finally: + self._wait_threads() + del self.result + return result + + platform_dependent = re.compile(r'\b(linux-(i\d86|x86_64|arm\w+)|' + r'win(32|-amd64)|macosx-?\d+)\b', re.I) + + def _is_platform_dependent(self, url): + """ + Does an URL refer to a platform-specific download? + """ + return self.platform_dependent.search(url) + + def _process_download(self, url): + """ + See if an URL is a suitable download for a project. + + If it is, register information in the result dictionary (for + _get_project) about the specific version it's for. + + Note that the return value isn't actually used other than as a boolean + value. + """ + if self._is_platform_dependent(url): + info = None + else: + info = self.convert_url_to_download_info(url, self.project_name) + logger.debug('process_download: %s -> %s', url, info) + if info: + with self._lock: # needed because self.result is shared + self._update_version_data(self.result, info) + return info + + def _should_queue(self, link, referrer, rel): + """ + Determine whether a link URL from a referring page and with a + particular "rel" attribute should be queued for scraping. + """ + scheme, netloc, path, _, _, _ = urlparse(link) + if path.endswith(self.source_extensions + self.binary_extensions + + self.excluded_extensions): + result = False + elif self.skip_externals and not link.startswith(self.base_url): + result = False + elif not referrer.startswith(self.base_url): + result = False + elif rel not in ('homepage', 'download'): + result = False + elif scheme not in ('http', 'https', 'ftp'): + result = False + elif self._is_platform_dependent(link): + result = False + else: + host = netloc.split(':', 1)[0] + if host.lower() == 'localhost': + result = False + else: + result = True + logger.debug('should_queue: %s (%s) from %s -> %s', link, rel, + referrer, result) + return result + + def _fetch(self): + """ + Get a URL to fetch from the work queue, get the HTML page, examine its + links for download candidates and candidates for further scraping. + + This is a handy method to run in a thread. + """ + while True: + url = self._to_fetch.get() + try: + if url: + page = self.get_page(url) + if page is None: # e.g. after an error + continue + for link, rel in page.links: + if link not in self._seen: + self._seen.add(link) + if (not self._process_download(link) and + self._should_queue(link, url, rel)): + logger.debug('Queueing %s from %s', link, url) + self._to_fetch.put(link) + finally: + # always do this, to avoid hangs :-) + self._to_fetch.task_done() + if not url: + #logger.debug('Sentinel seen, quitting.') + break + + def get_page(self, url): + """ + Get the HTML for an URL, possibly from an in-memory cache. + + XXX TODO Note: this cache is never actually cleared. It's assumed that + the data won't get stale over the lifetime of a locator instance (not + necessarily true for the default_locator). + """ + # http://peak.telecommunity.com/DevCenter/EasyInstall#package-index-api + scheme, netloc, path, _, _, _ = urlparse(url) + if scheme == 'file' and os.path.isdir(url2pathname(path)): + url = urljoin(ensure_slash(url), 'index.html') + + if url in self._page_cache: + result = self._page_cache[url] + logger.debug('Returning %s from cache: %s', url, result) + else: + host = netloc.split(':', 1)[0] + result = None + if host in self._bad_hosts: + logger.debug('Skipping %s due to bad host %s', url, host) + else: + req = Request(url, headers={'Accept-encoding': 'identity'}) + try: + logger.debug('Fetching %s', url) + resp = self.opener.open(req, timeout=self.timeout) + logger.debug('Fetched %s', url) + headers = resp.info() + content_type = headers.get('Content-Type', '') + if HTML_CONTENT_TYPE.match(content_type): + final_url = resp.geturl() + data = resp.read() + encoding = headers.get('Content-Encoding') + if encoding: + decoder = self.decoders[encoding] # fail if not found + data = decoder(data) + encoding = 'utf-8' + m = CHARSET.search(content_type) + if m: + encoding = m.group(1) + try: + data = data.decode(encoding) + except UnicodeError: + data = data.decode('latin-1') # fallback + result = Page(data, final_url) + self._page_cache[final_url] = result + except HTTPError as e: + if e.code != 404: + logger.exception('Fetch failed: %s: %s', url, e) + except URLError as e: + logger.exception('Fetch failed: %s: %s', url, e) + with self._lock: + self._bad_hosts.add(host) + except Exception as e: + logger.exception('Fetch failed: %s: %s', url, e) + finally: + self._page_cache[url] = result # even if None (failure) + return result + + _distname_re = re.compile(']*>([^<]+)<') + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + result = set() + page = self.get_page(self.base_url) + if not page: + raise DistlibException('Unable to get %s' % self.base_url) + for match in self._distname_re.finditer(page.data): + result.add(match.group(1)) + return result + +class DirectoryLocator(Locator): + """ + This class locates distributions in a directory tree. + """ + + def __init__(self, path, **kwargs): + """ + Initialise an instance. + :param path: The root of the directory tree to search. + :param kwargs: Passed to the superclass constructor, + except for: + * recursive - if True (the default), subdirectories are + recursed into. If False, only the top-level directory + is searched, + """ + self.recursive = kwargs.pop('recursive', True) + super(DirectoryLocator, self).__init__(**kwargs) + path = os.path.abspath(path) + if not os.path.isdir(path): + raise DistlibException('Not a directory: %r' % path) + self.base_dir = path + + def should_include(self, filename, parent): + """ + Should a filename be considered as a candidate for a distribution + archive? As well as the filename, the directory which contains it + is provided, though not used by the current implementation. + """ + return filename.endswith(self.downloadable_extensions) + + def _get_project(self, name): + result = {} + for root, dirs, files in os.walk(self.base_dir): + for fn in files: + if self.should_include(fn, root): + fn = os.path.join(root, fn) + url = urlunparse(('file', '', + pathname2url(os.path.abspath(fn)), + '', '', '')) + info = self.convert_url_to_download_info(url, name) + if info: + self._update_version_data(result, info) + if not self.recursive: + break + return result + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + result = set() + for root, dirs, files in os.walk(self.base_dir): + for fn in files: + if self.should_include(fn, root): + fn = os.path.join(root, fn) + url = urlunparse(('file', '', + pathname2url(os.path.abspath(fn)), + '', '', '')) + info = self.convert_url_to_download_info(url, None) + if info: + result.add(info['name']) + if not self.recursive: + break + return result + +class JSONLocator(Locator): + """ + This locator uses special extended metadata (not available on PyPI) and is + the basis of performant dependency resolution in distlib. Other locators + require archive downloads before dependencies can be determined! As you + might imagine, that can be slow. + """ + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + raise NotImplementedError('Not available from this locator') + + def _get_project(self, name): + result = {} + data = get_project_data(name) + if data: + for info in data.get('files', []): + if info['ptype'] != 'sdist' or info['pyversion'] != 'source': + continue + # We don't store summary in project metadata as it makes + # the data bigger for no benefit during dependency + # resolution + dist = make_dist(data['name'], info['version'], + summary=data.get('summary', + 'Placeholder for summary'), + scheme=self.scheme) + md = dist.metadata + md.source_url = info['url'] + # TODO SHA256 digest + if 'digest' in info and info['digest']: + dist.digest = ('md5', info['digest']) + md.dependencies = info.get('requirements', {}) + dist.exports = info.get('exports', {}) + result[dist.version] = dist + return result + +class DistPathLocator(Locator): + """ + This locator finds installed distributions in a path. It can be useful for + adding to an :class:`AggregatingLocator`. + """ + def __init__(self, distpath, **kwargs): + """ + Initialise an instance. + + :param distpath: A :class:`DistributionPath` instance to search. + """ + super(DistPathLocator, self).__init__(**kwargs) + assert isinstance(distpath, DistributionPath) + self.distpath = distpath + + def _get_project(self, name): + dist = self.distpath.get_distribution(name) + if dist is None: + result = {} + else: + result = { dist.version: dist } + return result + + +class AggregatingLocator(Locator): + """ + This class allows you to chain and/or merge a list of locators. + """ + def __init__(self, *locators, **kwargs): + """ + Initialise an instance. + + :param locators: The list of locators to search. + :param kwargs: Passed to the superclass constructor, + except for: + * merge - if False (the default), the first successful + search from any of the locators is returned. If True, + the results from all locators are merged (this can be + slow). + """ + self.merge = kwargs.pop('merge', False) + self.locators = locators + super(AggregatingLocator, self).__init__(**kwargs) + + def clear_cache(self): + super(AggregatingLocator, self).clear_cache() + for locator in self.locators: + locator.clear_cache() + + def _set_scheme(self, value): + self._scheme = value + for locator in self.locators: + locator.scheme = value + + scheme = property(Locator.scheme.fget, _set_scheme) + + def _get_project(self, name): + result = {} + for locator in self.locators: + d = locator.get_project(name) + if d: + if self.merge: + result.update(d) + else: + # See issue #18. If any dists are found and we're looking + # for specific constraints, we only return something if + # a match is found. For example, if a DirectoryLocator + # returns just foo (1.0) while we're looking for + # foo (>= 2.0), we'll pretend there was nothing there so + # that subsequent locators can be queried. Otherwise we + # would just return foo (1.0) which would then lead to a + # failure to find foo (>= 2.0), because other locators + # weren't searched. Note that this only matters when + # merge=False. + if self.matcher is None: + found = True + else: + found = False + for k in d: + if self.matcher.match(k): + found = True + break + if found: + result = d + break + return result + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + result = set() + for locator in self.locators: + try: + result |= locator.get_distribution_names() + except NotImplementedError: + pass + return result + + +# We use a legacy scheme simply because most of the dists on PyPI use legacy +# versions which don't conform to PEP 426 / PEP 440. +default_locator = AggregatingLocator( + JSONLocator(), + SimpleScrapingLocator('https://pypi.python.org/simple/', + timeout=3.0), + scheme='legacy') + +locate = default_locator.locate + +NAME_VERSION_RE = re.compile(r'(?P[\w-]+)\s*' + r'\(\s*(==\s*)?(?P[^)]+)\)$') + +class DependencyFinder(object): + """ + Locate dependencies for distributions. + """ + + def __init__(self, locator=None): + """ + Initialise an instance, using the specified locator + to locate distributions. + """ + self.locator = locator or default_locator + self.scheme = get_scheme(self.locator.scheme) + + def add_distribution(self, dist): + """ + Add a distribution to the finder. This will update internal information + about who provides what. + :param dist: The distribution to add. + """ + logger.debug('adding distribution %s', dist) + name = dist.key + self.dists_by_name[name] = dist + self.dists[(name, dist.version)] = dist + for p in dist.provides: + name, version = parse_name_and_version(p) + logger.debug('Add to provided: %s, %s, %s', name, version, dist) + self.provided.setdefault(name, set()).add((version, dist)) + + def remove_distribution(self, dist): + """ + Remove a distribution from the finder. This will update internal + information about who provides what. + :param dist: The distribution to remove. + """ + logger.debug('removing distribution %s', dist) + name = dist.key + del self.dists_by_name[name] + del self.dists[(name, dist.version)] + for p in dist.provides: + name, version = parse_name_and_version(p) + logger.debug('Remove from provided: %s, %s, %s', name, version, dist) + s = self.provided[name] + s.remove((version, dist)) + if not s: + del self.provided[name] + + def get_matcher(self, reqt): + """ + Get a version matcher for a requirement. + :param reqt: The requirement + :type reqt: str + :return: A version matcher (an instance of + :class:`distlib.version.Matcher`). + """ + try: + matcher = self.scheme.matcher(reqt) + except UnsupportedVersionError: + # XXX compat-mode if cannot read the version + name = reqt.split()[0] + matcher = self.scheme.matcher(name) + return matcher + + def find_providers(self, reqt): + """ + Find the distributions which can fulfill a requirement. + + :param reqt: The requirement. + :type reqt: str + :return: A set of distribution which can fulfill the requirement. + """ + matcher = self.get_matcher(reqt) + name = matcher.key # case-insensitive + result = set() + provided = self.provided + if name in provided: + for version, provider in provided[name]: + try: + match = matcher.match(version) + except UnsupportedVersionError: + match = False + + if match: + result.add(provider) + break + return result + + def try_to_replace(self, provider, other, problems): + """ + Attempt to replace one provider with another. This is typically used + when resolving dependencies from multiple sources, e.g. A requires + (B >= 1.0) while C requires (B >= 1.1). + + For successful replacement, ``provider`` must meet all the requirements + which ``other`` fulfills. + + :param provider: The provider we are trying to replace with. + :param other: The provider we're trying to replace. + :param problems: If False is returned, this will contain what + problems prevented replacement. This is currently + a tuple of the literal string 'cantreplace', + ``provider``, ``other`` and the set of requirements + that ``provider`` couldn't fulfill. + :return: True if we can replace ``other`` with ``provider``, else + False. + """ + rlist = self.reqts[other] + unmatched = set() + for s in rlist: + matcher = self.get_matcher(s) + if not matcher.match(provider.version): + unmatched.add(s) + if unmatched: + # can't replace other with provider + problems.add(('cantreplace', provider, other, unmatched)) + result = False + else: + # can replace other with provider + self.remove_distribution(other) + del self.reqts[other] + for s in rlist: + self.reqts.setdefault(provider, set()).add(s) + self.add_distribution(provider) + result = True + return result + + def find(self, requirement, meta_extras=None, prereleases=False): + """ + Find a distribution and all distributions it depends on. + + :param requirement: The requirement specifying the distribution to + find, or a Distribution instance. + :param meta_extras: A list of meta extras such as :test:, :build: and + so on. + :param prereleases: If ``True``, allow pre-release versions to be + returned - otherwise, don't return prereleases + unless they're all that's available. + + Return a set of :class:`Distribution` instances and a set of + problems. + + The distributions returned should be such that they have the + :attr:`required` attribute set to ``True`` if they were + from the ``requirement`` passed to ``find()``, and they have the + :attr:`build_time_dependency` attribute set to ``True`` unless they + are post-installation dependencies of the ``requirement``. + + The problems should be a tuple consisting of the string + ``'unsatisfied'`` and the requirement which couldn't be satisfied + by any distribution known to the locator. + """ + + self.provided = {} + self.dists = {} + self.dists_by_name = {} + self.reqts = {} + + meta_extras = set(meta_extras or []) + if ':*:' in meta_extras: + meta_extras.remove(':*:') + # :meta: and :run: are implicitly included + meta_extras |= set([':test:', ':build:', ':dev:']) + + if isinstance(requirement, Distribution): + dist = odist = requirement + logger.debug('passed %s as requirement', odist) + else: + dist = odist = self.locator.locate(requirement, + prereleases=prereleases) + if dist is None: + raise DistlibException('Unable to locate %r' % requirement) + logger.debug('located %s', odist) + dist.requested = True + problems = set() + todo = set([dist]) + install_dists = set([odist]) + while todo: + dist = todo.pop() + name = dist.key # case-insensitive + if name not in self.dists_by_name: + self.add_distribution(dist) + else: + #import pdb; pdb.set_trace() + other = self.dists_by_name[name] + if other != dist: + self.try_to_replace(dist, other, problems) + + ireqts = dist.run_requires | dist.meta_requires + sreqts = dist.build_requires + ereqts = set() + if dist in install_dists: + for key in ('test', 'build', 'dev'): + e = ':%s:' % key + if e in meta_extras: + ereqts |= getattr(dist, '%s_requires' % key) + all_reqts = ireqts | sreqts | ereqts + for r in all_reqts: + providers = self.find_providers(r) + if not providers: + logger.debug('No providers found for %r', r) + provider = self.locator.locate(r, prereleases=prereleases) + # If no provider is found and we didn't consider + # prereleases, consider them now. + if provider is None and not prereleases: + provider = self.locator.locate(r, prereleases=True) + if provider is None: + logger.debug('Cannot satisfy %r', r) + problems.add(('unsatisfied', r)) + else: + n, v = provider.key, provider.version + if (n, v) not in self.dists: + todo.add(provider) + providers.add(provider) + if r in ireqts and dist in install_dists: + install_dists.add(provider) + logger.debug('Adding %s to install_dists', + provider.name_and_version) + for p in providers: + name = p.key + if name not in self.dists_by_name: + self.reqts.setdefault(p, set()).add(r) + else: + other = self.dists_by_name[name] + if other != p: + # see if other can be replaced by p + self.try_to_replace(p, other, problems) + + dists = set(self.dists.values()) + for dist in dists: + dist.build_time_dependency = dist not in install_dists + if dist.build_time_dependency: + logger.debug('%s is a build-time dependency only.', + dist.name_and_version) + logger.debug('find done for %s', odist) + return dists, problems diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/manifest.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/manifest.py new file mode 100644 index 0000000..c6b98c5 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/manifest.py @@ -0,0 +1,364 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2013 Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +""" +Class representing the list of files in a distribution. + +Equivalent to distutils.filelist, but fixes some problems. +""" +import fnmatch +import logging +import os +import re + +from . import DistlibException +from .compat import fsdecode +from .util import convert_path + + +__all__ = ['Manifest'] + +logger = logging.getLogger(__name__) + +# a \ followed by some spaces + EOL +_COLLAPSE_PATTERN = re.compile('\\\w*\n', re.M) +_COMMENTED_LINE = re.compile('#.*?(?=\n)|\n(?=$)', re.M | re.S) + + +class Manifest(object): + """A list of files built by on exploring the filesystem and filtered by + applying various patterns to what we find there. + """ + + def __init__(self, base=None): + """ + Initialise an instance. + + :param base: The base directory to explore under. + """ + self.base = os.path.abspath(os.path.normpath(base or os.getcwd())) + self.prefix = self.base + os.sep + self.allfiles = None + self.files = set() + + # + # Public API + # + + def findall(self): + """Find all files under the base and set ``allfiles`` to the absolute + pathnames of files found. + """ + from stat import S_ISREG, S_ISDIR, S_ISLNK + + self.allfiles = allfiles = [] + root = self.base + stack = [root] + pop = stack.pop + push = stack.append + + while stack: + root = pop() + names = os.listdir(root) + + for name in names: + fullname = os.path.join(root, name) + + # Avoid excess stat calls -- just one will do, thank you! + stat = os.stat(fullname) + mode = stat.st_mode + if S_ISREG(mode): + allfiles.append(fsdecode(fullname)) + elif S_ISDIR(mode) and not S_ISLNK(mode): + push(fullname) + + def add(self, item): + """ + Add a file to the manifest. + + :param item: The pathname to add. This can be relative to the base. + """ + if not item.startswith(self.prefix): + item = os.path.join(self.base, item) + self.files.add(os.path.normpath(item)) + + def add_many(self, items): + """ + Add a list of files to the manifest. + + :param items: The pathnames to add. These can be relative to the base. + """ + for item in items: + self.add(item) + + def sorted(self, wantdirs=False): + """ + Return sorted files in directory order + """ + + def add_dir(dirs, d): + dirs.add(d) + logger.debug('add_dir added %s', d) + if d != self.base: + parent, _ = os.path.split(d) + assert parent not in ('', '/') + add_dir(dirs, parent) + + result = set(self.files) # make a copy! + if wantdirs: + dirs = set() + for f in result: + add_dir(dirs, os.path.dirname(f)) + result |= dirs + return [os.path.join(*path_tuple) for path_tuple in + sorted(os.path.split(path) for path in result)] + + def clear(self): + """Clear all collected files.""" + self.files = set() + self.allfiles = [] + + def process_directive(self, directive): + """ + Process a directive which either adds some files from ``allfiles`` to + ``files``, or removes some files from ``files``. + + :param directive: The directive to process. This should be in a format + compatible with distutils ``MANIFEST.in`` files: + + http://docs.python.org/distutils/sourcedist.html#commands + """ + # Parse the line: split it up, make sure the right number of words + # is there, and return the relevant words. 'action' is always + # defined: it's the first word of the line. Which of the other + # three are defined depends on the action; it'll be either + # patterns, (dir and patterns), or (dirpattern). + action, patterns, thedir, dirpattern = self._parse_directive(directive) + + # OK, now we know that the action is valid and we have the + # right number of words on the line for that action -- so we + # can proceed with minimal error-checking. + if action == 'include': + for pattern in patterns: + if not self._include_pattern(pattern, anchor=True): + logger.warning('no files found matching %r', pattern) + + elif action == 'exclude': + for pattern in patterns: + if not self._exclude_pattern(pattern, anchor=True): + logger.warning('no previously-included files ' + 'found matching %r', pattern) + + elif action == 'global-include': + for pattern in patterns: + if not self._include_pattern(pattern, anchor=False): + logger.warning('no files found matching %r ' + 'anywhere in distribution', pattern) + + elif action == 'global-exclude': + for pattern in patterns: + if not self._exclude_pattern(pattern, anchor=False): + logger.warning('no previously-included files ' + 'matching %r found anywhere in ' + 'distribution', pattern) + + elif action == 'recursive-include': + for pattern in patterns: + if not self._include_pattern(pattern, prefix=thedir): + logger.warning('no files found matching %r ' + 'under directory %r', pattern, thedir) + + elif action == 'recursive-exclude': + for pattern in patterns: + if not self._exclude_pattern(pattern, prefix=thedir): + logger.warning('no previously-included files ' + 'matching %r found under directory %r', + pattern, thedir) + + elif action == 'graft': + if not self._include_pattern(None, prefix=dirpattern): + logger.warning('no directories found matching %r', + dirpattern) + + elif action == 'prune': + if not self._exclude_pattern(None, prefix=dirpattern): + logger.warning('no previously-included directories found ' + 'matching %r', dirpattern) + else: # pragma: no cover + # This should never happen, as it should be caught in + # _parse_template_line + raise DistlibException( + 'invalid action %r' % action) + + # + # Private API + # + + def _parse_directive(self, directive): + """ + Validate a directive. + :param directive: The directive to validate. + :return: A tuple of action, patterns, thedir, dir_patterns + """ + words = directive.split() + if len(words) == 1 and words[0] not in ('include', 'exclude', + 'global-include', + 'global-exclude', + 'recursive-include', + 'recursive-exclude', + 'graft', 'prune'): + # no action given, let's use the default 'include' + words.insert(0, 'include') + + action = words[0] + patterns = thedir = dir_pattern = None + + if action in ('include', 'exclude', + 'global-include', 'global-exclude'): + if len(words) < 2: + raise DistlibException( + '%r expects ...' % action) + + patterns = [convert_path(word) for word in words[1:]] + + elif action in ('recursive-include', 'recursive-exclude'): + if len(words) < 3: + raise DistlibException( + '%r expects ...' % action) + + thedir = convert_path(words[1]) + patterns = [convert_path(word) for word in words[2:]] + + elif action in ('graft', 'prune'): + if len(words) != 2: + raise DistlibException( + '%r expects a single ' % action) + + dir_pattern = convert_path(words[1]) + + else: + raise DistlibException('unknown action %r' % action) + + return action, patterns, thedir, dir_pattern + + def _include_pattern(self, pattern, anchor=True, prefix=None, + is_regex=False): + """Select strings (presumably filenames) from 'self.files' that + match 'pattern', a Unix-style wildcard (glob) pattern. + + Patterns are not quite the same as implemented by the 'fnmatch' + module: '*' and '?' match non-special characters, where "special" + is platform-dependent: slash on Unix; colon, slash, and backslash on + DOS/Windows; and colon on Mac OS. + + If 'anchor' is true (the default), then the pattern match is more + stringent: "*.py" will match "foo.py" but not "foo/bar.py". If + 'anchor' is false, both of these will match. + + If 'prefix' is supplied, then only filenames starting with 'prefix' + (itself a pattern) and ending with 'pattern', with anything in between + them, will match. 'anchor' is ignored in this case. + + If 'is_regex' is true, 'anchor' and 'prefix' are ignored, and + 'pattern' is assumed to be either a string containing a regex or a + regex object -- no translation is done, the regex is just compiled + and used as-is. + + Selected strings will be added to self.files. + + Return True if files are found. + """ + # XXX docstring lying about what the special chars are? + found = False + pattern_re = self._translate_pattern(pattern, anchor, prefix, is_regex) + + # delayed loading of allfiles list + if self.allfiles is None: + self.findall() + + for name in self.allfiles: + if pattern_re.search(name): + self.files.add(name) + found = True + return found + + def _exclude_pattern(self, pattern, anchor=True, prefix=None, + is_regex=False): + """Remove strings (presumably filenames) from 'files' that match + 'pattern'. + + Other parameters are the same as for 'include_pattern()', above. + The list 'self.files' is modified in place. Return True if files are + found. + + This API is public to allow e.g. exclusion of SCM subdirs, e.g. when + packaging source distributions + """ + found = False + pattern_re = self._translate_pattern(pattern, anchor, prefix, is_regex) + for f in list(self.files): + if pattern_re.search(f): + self.files.remove(f) + found = True + return found + + def _translate_pattern(self, pattern, anchor=True, prefix=None, + is_regex=False): + """Translate a shell-like wildcard pattern to a compiled regular + expression. + + Return the compiled regex. If 'is_regex' true, + then 'pattern' is directly compiled to a regex (if it's a string) + or just returned as-is (assumes it's a regex object). + """ + if is_regex: + if isinstance(pattern, str): + return re.compile(pattern) + else: + return pattern + + if pattern: + pattern_re = self._glob_to_re(pattern) + else: + pattern_re = '' + + base = re.escape(os.path.join(self.base, '')) + if prefix is not None: + # ditch end of pattern character + empty_pattern = self._glob_to_re('') + prefix_re = self._glob_to_re(prefix)[:-len(empty_pattern)] + sep = os.sep + if os.sep == '\\': + sep = r'\\' + pattern_re = '^' + base + sep.join((prefix_re, + '.*' + pattern_re)) + else: # no prefix -- respect anchor flag + if anchor: + pattern_re = '^' + base + pattern_re + + return re.compile(pattern_re) + + def _glob_to_re(self, pattern): + """Translate a shell-like glob pattern to a regular expression. + + Return a string containing the regex. Differs from + 'fnmatch.translate()' in that '*' does not match "special characters" + (which are platform-specific). + """ + pattern_re = fnmatch.translate(pattern) + + # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which + # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix, + # and by extension they shouldn't match such "special characters" under + # any OS. So change all non-escaped dots in the RE to match any + # character except the special characters (currently: just os.sep). + sep = os.sep + if os.sep == '\\': + # we're using a regex to manipulate a regex, so we need + # to escape the backslash twice + sep = r'\\\\' + escaped = r'\1[^%s]' % sep + pattern_re = re.sub(r'((? y, + 'gte': lambda x, y: x >= y, + 'in': lambda x, y: x in y, + 'lt': lambda x, y: x < y, + 'lte': lambda x, y: x <= y, + 'not': lambda x: not x, + 'noteq': lambda x, y: x != y, + 'notin': lambda x, y: x not in y, + } + + allowed_values = { + 'sys_platform': sys.platform, + 'python_version': '%s.%s' % sys.version_info[:2], + # parsing sys.platform is not reliable, but there is no other + # way to get e.g. 2.7.2+, and the PEP is defined with sys.version + 'python_full_version': sys.version.split(' ', 1)[0], + 'os_name': os.name, + 'platform_in_venv': str(in_venv()), + 'platform_release': platform.release(), + 'platform_version': platform.version(), + 'platform_machine': platform.machine(), + 'platform_python_implementation': python_implementation(), + } + + def __init__(self, context=None): + """ + Initialise an instance. + + :param context: If specified, names are looked up in this mapping. + """ + self.context = context or {} + self.source = None + + def get_fragment(self, offset): + """ + Get the part of the source which is causing a problem. + """ + fragment_len = 10 + s = '%r' % (self.source[offset:offset + fragment_len]) + if offset + fragment_len < len(self.source): + s += '...' + return s + + def get_handler(self, node_type): + """ + Get a handler for the specified AST node type. + """ + return getattr(self, 'do_%s' % node_type, None) + + def evaluate(self, node, filename=None): + """ + Evaluate a source string or node, using ``filename`` when + displaying errors. + """ + if isinstance(node, string_types): + self.source = node + kwargs = {'mode': 'eval'} + if filename: + kwargs['filename'] = filename + try: + node = ast.parse(node, **kwargs) + except SyntaxError as e: + s = self.get_fragment(e.offset) + raise SyntaxError('syntax error %s' % s) + node_type = node.__class__.__name__.lower() + handler = self.get_handler(node_type) + if handler is None: + if self.source is None: + s = '(source not available)' + else: + s = self.get_fragment(node.col_offset) + raise SyntaxError("don't know how to evaluate %r %s" % ( + node_type, s)) + return handler(node) + + def get_attr_key(self, node): + assert isinstance(node, ast.Attribute), 'attribute node expected' + return '%s.%s' % (node.value.id, node.attr) + + def do_attribute(self, node): + if not isinstance(node.value, ast.Name): + valid = False + else: + key = self.get_attr_key(node) + valid = key in self.context or key in self.allowed_values + if not valid: + raise SyntaxError('invalid expression: %s' % key) + if key in self.context: + result = self.context[key] + else: + result = self.allowed_values[key] + return result + + def do_boolop(self, node): + result = self.evaluate(node.values[0]) + is_or = node.op.__class__ is ast.Or + is_and = node.op.__class__ is ast.And + assert is_or or is_and + if (is_and and result) or (is_or and not result): + for n in node.values[1:]: + result = self.evaluate(n) + if (is_or and result) or (is_and and not result): + break + return result + + def do_compare(self, node): + def sanity_check(lhsnode, rhsnode): + valid = True + if isinstance(lhsnode, ast.Str) and isinstance(rhsnode, ast.Str): + valid = False + #elif (isinstance(lhsnode, ast.Attribute) + # and isinstance(rhsnode, ast.Attribute)): + # klhs = self.get_attr_key(lhsnode) + # krhs = self.get_attr_key(rhsnode) + # valid = klhs != krhs + if not valid: + s = self.get_fragment(node.col_offset) + raise SyntaxError('Invalid comparison: %s' % s) + + lhsnode = node.left + lhs = self.evaluate(lhsnode) + result = True + for op, rhsnode in zip(node.ops, node.comparators): + sanity_check(lhsnode, rhsnode) + op = op.__class__.__name__.lower() + if op not in self.operators: + raise SyntaxError('unsupported operation: %r' % op) + rhs = self.evaluate(rhsnode) + result = self.operators[op](lhs, rhs) + if not result: + break + lhs = rhs + lhsnode = rhsnode + return result + + def do_expression(self, node): + return self.evaluate(node.body) + + def do_name(self, node): + valid = False + if node.id in self.context: + valid = True + result = self.context[node.id] + elif node.id in self.allowed_values: + valid = True + result = self.allowed_values[node.id] + if not valid: + raise SyntaxError('invalid expression: %s' % node.id) + return result + + def do_str(self, node): + return node.s + + +def interpret(marker, execution_context=None): + """ + Interpret a marker and return a result depending on environment. + + :param marker: The marker to interpret. + :type marker: str + :param execution_context: The context used for name lookup. + :type execution_context: mapping + """ + return Evaluator(execution_context).evaluate(marker.strip()) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/metadata.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/metadata.py new file mode 100644 index 0000000..ef3b1d6 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/metadata.py @@ -0,0 +1,1015 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +"""Implementation of the Metadata for Python packages PEPs. + +Supports all metadata formats (1.0, 1.1, 1.2, and 2.0 experimental). +""" +from __future__ import unicode_literals + +import codecs +from email import message_from_file +import json +import logging +import re + + +from . import DistlibException, __version__ +from .compat import StringIO, string_types, text_type +from .markers import interpret +from .util import extract_by_key, get_extras +from .version import get_scheme, PEP426_VERSION_RE + +logger = logging.getLogger(__name__) + + +class MetadataMissingError(DistlibException): + """A required metadata is missing""" + + +class MetadataConflictError(DistlibException): + """Attempt to read or write metadata fields that are conflictual.""" + + +class MetadataUnrecognizedVersionError(DistlibException): + """Unknown metadata version number.""" + + +class MetadataInvalidError(DistlibException): + """A metadata value is invalid""" + +# public API of this module +__all__ = ['Metadata', 'PKG_INFO_ENCODING', 'PKG_INFO_PREFERRED_VERSION'] + +# Encoding used for the PKG-INFO files +PKG_INFO_ENCODING = 'utf-8' + +# preferred version. Hopefully will be changed +# to 1.2 once PEP 345 is supported everywhere +PKG_INFO_PREFERRED_VERSION = '1.1' + +_LINE_PREFIX = re.compile('\n \|') +_241_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', + 'Summary', 'Description', + 'Keywords', 'Home-page', 'Author', 'Author-email', + 'License') + +_314_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', + 'Supported-Platform', 'Summary', 'Description', + 'Keywords', 'Home-page', 'Author', 'Author-email', + 'License', 'Classifier', 'Download-URL', 'Obsoletes', + 'Provides', 'Requires') + +_314_MARKERS = ('Obsoletes', 'Provides', 'Requires', 'Classifier', + 'Download-URL') + +_345_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', + 'Supported-Platform', 'Summary', 'Description', + 'Keywords', 'Home-page', 'Author', 'Author-email', + 'Maintainer', 'Maintainer-email', 'License', + 'Classifier', 'Download-URL', 'Obsoletes-Dist', + 'Project-URL', 'Provides-Dist', 'Requires-Dist', + 'Requires-Python', 'Requires-External') + +_345_MARKERS = ('Provides-Dist', 'Requires-Dist', 'Requires-Python', + 'Obsoletes-Dist', 'Requires-External', 'Maintainer', + 'Maintainer-email', 'Project-URL') + +_426_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', + 'Supported-Platform', 'Summary', 'Description', + 'Keywords', 'Home-page', 'Author', 'Author-email', + 'Maintainer', 'Maintainer-email', 'License', + 'Classifier', 'Download-URL', 'Obsoletes-Dist', + 'Project-URL', 'Provides-Dist', 'Requires-Dist', + 'Requires-Python', 'Requires-External', 'Private-Version', + 'Obsoleted-By', 'Setup-Requires-Dist', 'Extension', + 'Provides-Extra') + +_426_MARKERS = ('Private-Version', 'Provides-Extra', 'Obsoleted-By', + 'Setup-Requires-Dist', 'Extension') + +_ALL_FIELDS = set() +_ALL_FIELDS.update(_241_FIELDS) +_ALL_FIELDS.update(_314_FIELDS) +_ALL_FIELDS.update(_345_FIELDS) +_ALL_FIELDS.update(_426_FIELDS) + +EXTRA_RE = re.compile(r'''extra\s*==\s*("([^"]+)"|'([^']+)')''') + + +def _version2fieldlist(version): + if version == '1.0': + return _241_FIELDS + elif version == '1.1': + return _314_FIELDS + elif version == '1.2': + return _345_FIELDS + elif version == '2.0': + return _426_FIELDS + raise MetadataUnrecognizedVersionError(version) + + +def _best_version(fields): + """Detect the best version depending on the fields used.""" + def _has_marker(keys, markers): + for marker in markers: + if marker in keys: + return True + return False + + keys = [] + for key, value in fields.items(): + if value in ([], 'UNKNOWN', None): + continue + keys.append(key) + + possible_versions = ['1.0', '1.1', '1.2', '2.0'] + + # first let's try to see if a field is not part of one of the version + for key in keys: + if key not in _241_FIELDS and '1.0' in possible_versions: + possible_versions.remove('1.0') + if key not in _314_FIELDS and '1.1' in possible_versions: + possible_versions.remove('1.1') + if key not in _345_FIELDS and '1.2' in possible_versions: + possible_versions.remove('1.2') + if key not in _426_FIELDS and '2.0' in possible_versions: + possible_versions.remove('2.0') + + # possible_version contains qualified versions + if len(possible_versions) == 1: + return possible_versions[0] # found ! + elif len(possible_versions) == 0: + raise MetadataConflictError('Unknown metadata set') + + # let's see if one unique marker is found + is_1_1 = '1.1' in possible_versions and _has_marker(keys, _314_MARKERS) + is_1_2 = '1.2' in possible_versions and _has_marker(keys, _345_MARKERS) + is_2_0 = '2.0' in possible_versions and _has_marker(keys, _426_MARKERS) + if int(is_1_1) + int(is_1_2) + int(is_2_0) > 1: + raise MetadataConflictError('You used incompatible 1.1/1.2/2.0 fields') + + # we have the choice, 1.0, or 1.2, or 2.0 + # - 1.0 has a broken Summary field but works with all tools + # - 1.1 is to avoid + # - 1.2 fixes Summary but has little adoption + # - 2.0 adds more features and is very new + if not is_1_1 and not is_1_2 and not is_2_0: + # we couldn't find any specific marker + if PKG_INFO_PREFERRED_VERSION in possible_versions: + return PKG_INFO_PREFERRED_VERSION + if is_1_1: + return '1.1' + if is_1_2: + return '1.2' + + return '2.0' + +_ATTR2FIELD = { + 'metadata_version': 'Metadata-Version', + 'name': 'Name', + 'version': 'Version', + 'platform': 'Platform', + 'supported_platform': 'Supported-Platform', + 'summary': 'Summary', + 'description': 'Description', + 'keywords': 'Keywords', + 'home_page': 'Home-page', + 'author': 'Author', + 'author_email': 'Author-email', + 'maintainer': 'Maintainer', + 'maintainer_email': 'Maintainer-email', + 'license': 'License', + 'classifier': 'Classifier', + 'download_url': 'Download-URL', + 'obsoletes_dist': 'Obsoletes-Dist', + 'provides_dist': 'Provides-Dist', + 'requires_dist': 'Requires-Dist', + 'setup_requires_dist': 'Setup-Requires-Dist', + 'requires_python': 'Requires-Python', + 'requires_external': 'Requires-External', + 'requires': 'Requires', + 'provides': 'Provides', + 'obsoletes': 'Obsoletes', + 'project_url': 'Project-URL', + 'private_version': 'Private-Version', + 'obsoleted_by': 'Obsoleted-By', + 'extension': 'Extension', + 'provides_extra': 'Provides-Extra', +} + +_PREDICATE_FIELDS = ('Requires-Dist', 'Obsoletes-Dist', 'Provides-Dist') +_VERSIONS_FIELDS = ('Requires-Python',) +_VERSION_FIELDS = ('Version',) +_LISTFIELDS = ('Platform', 'Classifier', 'Obsoletes', + 'Requires', 'Provides', 'Obsoletes-Dist', + 'Provides-Dist', 'Requires-Dist', 'Requires-External', + 'Project-URL', 'Supported-Platform', 'Setup-Requires-Dist', + 'Provides-Extra', 'Extension') +_LISTTUPLEFIELDS = ('Project-URL',) + +_ELEMENTSFIELD = ('Keywords',) + +_UNICODEFIELDS = ('Author', 'Maintainer', 'Summary', 'Description') + +_MISSING = object() + +_FILESAFE = re.compile('[^A-Za-z0-9.]+') + + +def _get_name_and_version(name, version, for_filename=False): + """Return the distribution name with version. + + If for_filename is true, return a filename-escaped form.""" + if for_filename: + # For both name and version any runs of non-alphanumeric or '.' + # characters are replaced with a single '-'. Additionally any + # spaces in the version string become '.' + name = _FILESAFE.sub('-', name) + version = _FILESAFE.sub('-', version.replace(' ', '.')) + return '%s-%s' % (name, version) + + +class LegacyMetadata(object): + """The legacy metadata of a release. + + Supports versions 1.0, 1.1 and 1.2 (auto-detected). You can + instantiate the class with one of these arguments (or none): + - *path*, the path to a metadata file + - *fileobj* give a file-like object with metadata as content + - *mapping* is a dict-like object + - *scheme* is a version scheme name + """ + # TODO document the mapping API and UNKNOWN default key + + def __init__(self, path=None, fileobj=None, mapping=None, + scheme='default'): + if [path, fileobj, mapping].count(None) < 2: + raise TypeError('path, fileobj and mapping are exclusive') + self._fields = {} + self.requires_files = [] + self._dependencies = None + self.scheme = scheme + if path is not None: + self.read(path) + elif fileobj is not None: + self.read_file(fileobj) + elif mapping is not None: + self.update(mapping) + self.set_metadata_version() + + def set_metadata_version(self): + self._fields['Metadata-Version'] = _best_version(self._fields) + + def _write_field(self, fileobj, name, value): + fileobj.write('%s: %s\n' % (name, value)) + + def __getitem__(self, name): + return self.get(name) + + def __setitem__(self, name, value): + return self.set(name, value) + + def __delitem__(self, name): + field_name = self._convert_name(name) + try: + del self._fields[field_name] + except KeyError: + raise KeyError(name) + + def __contains__(self, name): + return (name in self._fields or + self._convert_name(name) in self._fields) + + def _convert_name(self, name): + if name in _ALL_FIELDS: + return name + name = name.replace('-', '_').lower() + return _ATTR2FIELD.get(name, name) + + def _default_value(self, name): + if name in _LISTFIELDS or name in _ELEMENTSFIELD: + return [] + return 'UNKNOWN' + + def _remove_line_prefix(self, value): + return _LINE_PREFIX.sub('\n', value) + + def __getattr__(self, name): + if name in _ATTR2FIELD: + return self[name] + raise AttributeError(name) + + # + # Public API + # + +# dependencies = property(_get_dependencies, _set_dependencies) + + def get_fullname(self, filesafe=False): + """Return the distribution name with version. + + If filesafe is true, return a filename-escaped form.""" + return _get_name_and_version(self['Name'], self['Version'], filesafe) + + def is_field(self, name): + """return True if name is a valid metadata key""" + name = self._convert_name(name) + return name in _ALL_FIELDS + + def is_multi_field(self, name): + name = self._convert_name(name) + return name in _LISTFIELDS + + def read(self, filepath): + """Read the metadata values from a file path.""" + fp = codecs.open(filepath, 'r', encoding='utf-8') + try: + self.read_file(fp) + finally: + fp.close() + + def read_file(self, fileob): + """Read the metadata values from a file object.""" + msg = message_from_file(fileob) + self._fields['Metadata-Version'] = msg['metadata-version'] + + # When reading, get all the fields we can + for field in _ALL_FIELDS: + if field not in msg: + continue + if field in _LISTFIELDS: + # we can have multiple lines + values = msg.get_all(field) + if field in _LISTTUPLEFIELDS and values is not None: + values = [tuple(value.split(',')) for value in values] + self.set(field, values) + else: + # single line + value = msg[field] + if value is not None and value != 'UNKNOWN': + self.set(field, value) + self.set_metadata_version() + + def write(self, filepath, skip_unknown=False): + """Write the metadata fields to filepath.""" + fp = codecs.open(filepath, 'w', encoding='utf-8') + try: + self.write_file(fp, skip_unknown) + finally: + fp.close() + + def write_file(self, fileobject, skip_unknown=False): + """Write the PKG-INFO format data to a file object.""" + self.set_metadata_version() + + for field in _version2fieldlist(self['Metadata-Version']): + values = self.get(field) + if skip_unknown and values in ('UNKNOWN', [], ['UNKNOWN']): + continue + if field in _ELEMENTSFIELD: + self._write_field(fileobject, field, ','.join(values)) + continue + if field not in _LISTFIELDS: + if field == 'Description': + values = values.replace('\n', '\n |') + values = [values] + + if field in _LISTTUPLEFIELDS: + values = [','.join(value) for value in values] + + for value in values: + self._write_field(fileobject, field, value) + + def update(self, other=None, **kwargs): + """Set metadata values from the given iterable `other` and kwargs. + + Behavior is like `dict.update`: If `other` has a ``keys`` method, + they are looped over and ``self[key]`` is assigned ``other[key]``. + Else, ``other`` is an iterable of ``(key, value)`` iterables. + + Keys that don't match a metadata field or that have an empty value are + dropped. + """ + def _set(key, value): + if key in _ATTR2FIELD and value: + self.set(self._convert_name(key), value) + + if not other: + # other is None or empty container + pass + elif hasattr(other, 'keys'): + for k in other.keys(): + _set(k, other[k]) + else: + for k, v in other: + _set(k, v) + + if kwargs: + for k, v in kwargs.items(): + _set(k, v) + + def set(self, name, value): + """Control then set a metadata field.""" + name = self._convert_name(name) + + if ((name in _ELEMENTSFIELD or name == 'Platform') and + not isinstance(value, (list, tuple))): + if isinstance(value, string_types): + value = [v.strip() for v in value.split(',')] + else: + value = [] + elif (name in _LISTFIELDS and + not isinstance(value, (list, tuple))): + if isinstance(value, string_types): + value = [value] + else: + value = [] + + if logger.isEnabledFor(logging.WARNING): + project_name = self['Name'] + + scheme = get_scheme(self.scheme) + if name in _PREDICATE_FIELDS and value is not None: + for v in value: + # check that the values are valid + if not scheme.is_valid_matcher(v.split(';')[0]): + logger.warning( + '%r: %r is not valid (field %r)', + project_name, v, name) + # FIXME this rejects UNKNOWN, is that right? + elif name in _VERSIONS_FIELDS and value is not None: + if not scheme.is_valid_constraint_list(value): + logger.warning('%r: %r is not a valid version (field %r)', + project_name, value, name) + elif name in _VERSION_FIELDS and value is not None: + if not scheme.is_valid_version(value): + logger.warning('%r: %r is not a valid version (field %r)', + project_name, value, name) + + if name in _UNICODEFIELDS: + if name == 'Description': + value = self._remove_line_prefix(value) + + self._fields[name] = value + + def get(self, name, default=_MISSING): + """Get a metadata field.""" + name = self._convert_name(name) + if name not in self._fields: + if default is _MISSING: + default = self._default_value(name) + return default + if name in _UNICODEFIELDS: + value = self._fields[name] + return value + elif name in _LISTFIELDS: + value = self._fields[name] + if value is None: + return [] + res = [] + for val in value: + if name not in _LISTTUPLEFIELDS: + res.append(val) + else: + # That's for Project-URL + res.append((val[0], val[1])) + return res + + elif name in _ELEMENTSFIELD: + value = self._fields[name] + if isinstance(value, string_types): + return value.split(',') + return self._fields[name] + + def check(self, strict=False): + """Check if the metadata is compliant. If strict is True then raise if + no Name or Version are provided""" + self.set_metadata_version() + + # XXX should check the versions (if the file was loaded) + missing, warnings = [], [] + + for attr in ('Name', 'Version'): # required by PEP 345 + if attr not in self: + missing.append(attr) + + if strict and missing != []: + msg = 'missing required metadata: %s' % ', '.join(missing) + raise MetadataMissingError(msg) + + for attr in ('Home-page', 'Author'): + if attr not in self: + missing.append(attr) + + # checking metadata 1.2 (XXX needs to check 1.1, 1.0) + if self['Metadata-Version'] != '1.2': + return missing, warnings + + scheme = get_scheme(self.scheme) + + def are_valid_constraints(value): + for v in value: + if not scheme.is_valid_matcher(v.split(';')[0]): + return False + return True + + for fields, controller in ((_PREDICATE_FIELDS, are_valid_constraints), + (_VERSIONS_FIELDS, + scheme.is_valid_constraint_list), + (_VERSION_FIELDS, + scheme.is_valid_version)): + for field in fields: + value = self.get(field, None) + if value is not None and not controller(value): + warnings.append('Wrong value for %r: %s' % (field, value)) + + return missing, warnings + + def todict(self, skip_missing=False): + """Return fields as a dict. + + Field names will be converted to use the underscore-lowercase style + instead of hyphen-mixed case (i.e. home_page instead of Home-page). + """ + self.set_metadata_version() + + mapping_1_0 = ( + ('metadata_version', 'Metadata-Version'), + ('name', 'Name'), + ('version', 'Version'), + ('summary', 'Summary'), + ('home_page', 'Home-page'), + ('author', 'Author'), + ('author_email', 'Author-email'), + ('license', 'License'), + ('description', 'Description'), + ('keywords', 'Keywords'), + ('platform', 'Platform'), + ('classifier', 'Classifier'), + ('download_url', 'Download-URL'), + ) + + data = {} + for key, field_name in mapping_1_0: + if not skip_missing or field_name in self._fields: + data[key] = self[field_name] + + if self['Metadata-Version'] == '1.2': + mapping_1_2 = ( + ('requires_dist', 'Requires-Dist'), + ('requires_python', 'Requires-Python'), + ('requires_external', 'Requires-External'), + ('provides_dist', 'Provides-Dist'), + ('obsoletes_dist', 'Obsoletes-Dist'), + ('project_url', 'Project-URL'), + ('maintainer', 'Maintainer'), + ('maintainer_email', 'Maintainer-email'), + ) + for key, field_name in mapping_1_2: + if not skip_missing or field_name in self._fields: + if key != 'project_url': + data[key] = self[field_name] + else: + data[key] = [','.join(u) for u in self[field_name]] + + elif self['Metadata-Version'] == '1.1': + mapping_1_1 = ( + ('provides', 'Provides'), + ('requires', 'Requires'), + ('obsoletes', 'Obsoletes'), + ) + for key, field_name in mapping_1_1: + if not skip_missing or field_name in self._fields: + data[key] = self[field_name] + + return data + + def add_requirements(self, requirements): + if self['Metadata-Version'] == '1.1': + # we can't have 1.1 metadata *and* Setuptools requires + for field in ('Obsoletes', 'Requires', 'Provides'): + if field in self: + del self[field] + self['Requires-Dist'] += requirements + + # Mapping API + # TODO could add iter* variants + + def keys(self): + return list(_version2fieldlist(self['Metadata-Version'])) + + def __iter__(self): + for key in self.keys(): + yield key + + def values(self): + return [self[key] for key in self.keys()] + + def items(self): + return [(key, self[key]) for key in self.keys()] + + def __repr__(self): + return '<%s %s %s>' % (self.__class__.__name__, self.name, + self.version) + + +METADATA_FILENAME = 'pydist.json' + + +class Metadata(object): + """ + The metadata of a release. This implementation uses 2.0 (JSON) + metadata where possible. If not possible, it wraps a LegacyMetadata + instance which handles the key-value metadata format. + """ + + METADATA_VERSION_MATCHER = re.compile('^\d+(\.\d+)*$') + + NAME_MATCHER = re.compile('^[0-9A-Z]([0-9A-Z_.-]*[0-9A-Z])?$', re.I) + + VERSION_MATCHER = PEP426_VERSION_RE + + SUMMARY_MATCHER = re.compile('.{1,2047}') + + METADATA_VERSION = '2.0' + + GENERATOR = 'distlib (%s)' % __version__ + + MANDATORY_KEYS = { + 'name': (), + 'version': (), + 'summary': ('legacy',), + } + + INDEX_KEYS = ('name version license summary description author ' + 'author_email keywords platform home_page classifiers ' + 'download_url') + + DEPENDENCY_KEYS = ('extras run_requires test_requires build_requires ' + 'dev_requires provides meta_requires obsoleted_by ' + 'supports_environments') + + SYNTAX_VALIDATORS = { + 'metadata_version': (METADATA_VERSION_MATCHER, ()), + 'name': (NAME_MATCHER, ('legacy',)), + 'version': (VERSION_MATCHER, ('legacy',)), + 'summary': (SUMMARY_MATCHER, ('legacy',)), + } + + __slots__ = ('_legacy', '_data', 'scheme') + + def __init__(self, path=None, fileobj=None, mapping=None, + scheme='default'): + if [path, fileobj, mapping].count(None) < 2: + raise TypeError('path, fileobj and mapping are exclusive') + self._legacy = None + self._data = None + self.scheme = scheme + #import pdb; pdb.set_trace() + if mapping is not None: + try: + self._validate_mapping(mapping, scheme) + self._data = mapping + except MetadataUnrecognizedVersionError: + self._legacy = LegacyMetadata(mapping=mapping, scheme=scheme) + self.validate() + else: + data = None + if path: + with open(path, 'rb') as f: + data = f.read() + elif fileobj: + data = fileobj.read() + if data is None: + # Initialised with no args - to be added + self._data = { + 'metadata_version': self.METADATA_VERSION, + 'generator': self.GENERATOR, + } + else: + if not isinstance(data, text_type): + data = data.decode('utf-8') + try: + self._data = json.loads(data) + self._validate_mapping(self._data, scheme) + except ValueError: + # Note: MetadataUnrecognizedVersionError does not + # inherit from ValueError (it's a DistlibException, + # which should not inherit from ValueError). + # The ValueError comes from the json.load - if that + # succeeds and we get a validation error, we want + # that to propagate + self._legacy = LegacyMetadata(fileobj=StringIO(data), + scheme=scheme) + self.validate() + + common_keys = set(('name', 'version', 'license', 'keywords', 'summary')) + + none_list = (None, list) + none_dict = (None, dict) + + mapped_keys = { + 'run_requires': ('Requires-Dist', list), + 'build_requires': ('Setup-Requires-Dist', list), + 'dev_requires': none_list, + 'test_requires': none_list, + 'meta_requires': none_list, + 'extras': ('Provides-Extra', list), + 'modules': none_list, + 'namespaces': none_list, + 'exports': none_dict, + 'commands': none_dict, + 'classifiers': ('Classifier', list), + 'source_url': ('Download-URL', None), + 'metadata_version': ('Metadata-Version', None), + } + + del none_list, none_dict + + def __getattribute__(self, key): + common = object.__getattribute__(self, 'common_keys') + mapped = object.__getattribute__(self, 'mapped_keys') + if key in mapped: + lk, maker = mapped[key] + if self._legacy: + if lk is None: + result = None if maker is None else maker() + else: + result = self._legacy.get(lk) + else: + value = None if maker is None else maker() + result = self._data.get(key, value) + elif key not in common: + result = object.__getattribute__(self, key) + elif self._legacy: + result = self._legacy.get(key) + else: + result = self._data.get(key) + return result + + def _validate_value(self, key, value, scheme=None): + if key in self.SYNTAX_VALIDATORS: + pattern, exclusions = self.SYNTAX_VALIDATORS[key] + if (scheme or self.scheme) not in exclusions: + m = pattern.match(value) + if not m: + raise MetadataInvalidError('%r is an invalid value for ' + 'the %r property' % (value, + key)) + + def __setattr__(self, key, value): + self._validate_value(key, value) + common = object.__getattribute__(self, 'common_keys') + mapped = object.__getattribute__(self, 'mapped_keys') + if key in mapped: + lk, _ = mapped[key] + if self._legacy: + if lk is None: + raise NotImplementedError + self._legacy[lk] = value + else: + self._data[key] = value + elif key not in common: + object.__setattr__(self, key, value) + else: + if key == 'keywords': + if isinstance(value, string_types): + value = value.strip() + if value: + value = value.split() + else: + value = [] + if self._legacy: + self._legacy[key] = value + else: + self._data[key] = value + + @property + def name_and_version(self): + return _get_name_and_version(self.name, self.version, True) + + @property + def provides(self): + if self._legacy: + result = self._legacy['Provides-Dist'] + else: + result = self._data.setdefault('provides', []) + s = '%s (%s)' % (self.name, self.version) + if s not in result: + result.append(s) + return result + + @provides.setter + def provides(self, value): + if self._legacy: + self._legacy['Provides-Dist'] = value + else: + self._data['provides'] = value + + def get_requirements(self, reqts, extras=None, env=None): + """ + Base method to get dependencies, given a set of extras + to satisfy and an optional environment context. + :param reqts: A list of sometimes-wanted dependencies, + perhaps dependent on extras and environment. + :param extras: A list of optional components being requested. + :param env: An optional environment for marker evaluation. + """ + if self._legacy: + result = reqts + else: + result = [] + extras = get_extras(extras or [], self.extras) + for d in reqts: + if 'extra' not in d and 'environment' not in d: + # unconditional + include = True + else: + if 'extra' not in d: + # Not extra-dependent - only environment-dependent + include = True + else: + include = d.get('extra') in extras + if include: + # Not excluded because of extras, check environment + marker = d.get('environment') + if marker: + include = interpret(marker, env) + if include: + result.extend(d['requires']) + for key in ('build', 'dev', 'test'): + e = ':%s:' % key + if e in extras: + extras.remove(e) + # A recursive call, but it should terminate since 'test' + # has been removed from the extras + reqts = self._data.get('%s_requires' % key, []) + result.extend(self.get_requirements(reqts, extras=extras, + env=env)) + return result + + @property + def dictionary(self): + if self._legacy: + return self._from_legacy() + return self._data + + @property + def dependencies(self): + if self._legacy: + raise NotImplementedError + else: + return extract_by_key(self._data, self.DEPENDENCY_KEYS) + + @dependencies.setter + def dependencies(self, value): + if self._legacy: + raise NotImplementedError + else: + self._data.update(value) + + def _validate_mapping(self, mapping, scheme): + if mapping.get('metadata_version') != self.METADATA_VERSION: + raise MetadataUnrecognizedVersionError() + missing = [] + for key, exclusions in self.MANDATORY_KEYS.items(): + if key not in mapping: + if scheme not in exclusions: + missing.append(key) + if missing: + msg = 'Missing metadata items: %s' % ', '.join(missing) + raise MetadataMissingError(msg) + for k, v in mapping.items(): + self._validate_value(k, v, scheme) + + def validate(self): + if self._legacy: + missing, warnings = self._legacy.check(True) + if missing or warnings: + logger.warning('Metadata: missing: %s, warnings: %s', + missing, warnings) + else: + self._validate_mapping(self._data, self.scheme) + + def todict(self): + if self._legacy: + return self._legacy.todict(True) + else: + result = extract_by_key(self._data, self.INDEX_KEYS) + return result + + def _from_legacy(self): + assert self._legacy and not self._data + result = { + 'metadata_version': self.METADATA_VERSION, + 'generator': self.GENERATOR, + } + lmd = self._legacy.todict(True) # skip missing ones + for k in ('name', 'version', 'license', 'summary', 'description', + 'classifier'): + if k in lmd: + if k == 'classifier': + nk = 'classifiers' + else: + nk = k + result[nk] = lmd[k] + kw = lmd.get('Keywords', []) + if kw == ['']: + kw = [] + result['keywords'] = kw + keys = (('requires_dist', 'run_requires'), + ('setup_requires_dist', 'build_requires')) + for ok, nk in keys: + if ok in lmd and lmd[ok]: + result[nk] = [{'requires': lmd[ok]}] + result['provides'] = self.provides + author = {} + maintainer = {} + return result + + LEGACY_MAPPING = { + 'name': 'Name', + 'version': 'Version', + 'license': 'License', + 'summary': 'Summary', + 'description': 'Description', + 'classifiers': 'Classifier', + } + + def _to_legacy(self): + def process_entries(entries): + reqts = set() + for e in entries: + extra = e.get('extra') + env = e.get('environment') + rlist = e['requires'] + for r in rlist: + if not env and not extra: + reqts.add(r) + else: + marker = '' + if extra: + marker = 'extra == "%s"' % extra + if env: + if marker: + marker = '(%s) and %s' % (env, marker) + else: + marker = env + reqts.add(';'.join((r, marker))) + return reqts + + assert self._data and not self._legacy + result = LegacyMetadata() + nmd = self._data + for nk, ok in self.LEGACY_MAPPING.items(): + if nk in nmd: + result[ok] = nmd[nk] + r1 = process_entries(self.run_requires + self.meta_requires) + r2 = process_entries(self.build_requires + self.dev_requires) + if self.extras: + result['Provides-Extra'] = sorted(self.extras) + result['Requires-Dist'] = sorted(r1) + result['Setup-Requires-Dist'] = sorted(r2) + # TODO: other fields such as contacts + return result + + def write(self, path=None, fileobj=None, legacy=False, skip_unknown=True): + if [path, fileobj].count(None) != 1: + raise ValueError('Exactly one of path and fileobj is needed') + self.validate() + if legacy: + if self._legacy: + legacy_md = self._legacy + else: + legacy_md = self._to_legacy() + if path: + legacy_md.write(path, skip_unknown=skip_unknown) + else: + legacy_md.write_file(fileobj, skip_unknown=skip_unknown) + else: + if self._legacy: + d = self._from_legacy() + else: + d = self._data + if fileobj: + json.dump(d, fileobj, ensure_ascii=True, indent=2, + sort_keys=True) + else: + with codecs.open(path, 'w', 'utf-8') as f: + json.dump(d, f, ensure_ascii=True, indent=2, + sort_keys=True) + + def add_requirements(self, requirements): + if self._legacy: + self._legacy.add_requirements(requirements) + else: + self._data.setdefault('run_requires', []).extend(requirements) + + def __repr__(self): + name = self.name or '(no name)' + version = self.version or 'no version' + return '<%s %s %s (%s)>' % (self.__class__.__name__, + self.metadata_version, name, version) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/resources.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/resources.py new file mode 100644 index 0000000..7c26737 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/resources.py @@ -0,0 +1,351 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +from __future__ import unicode_literals + +import bisect +import io +import logging +import os +import pkgutil +import shutil +import sys +import types +import zipimport + +from . import DistlibException +from .util import cached_property, get_cache_base, path_to_cache_dir + +logger = logging.getLogger(__name__) + + +class Cache(object): + """ + A class implementing a cache for resources that need to live in the file system + e.g. shared libraries. + """ + + def __init__(self, base=None): + """ + Initialise an instance. + + :param base: The base directory where the cache should be located. If + not specified, this will be the ``resource-cache`` + directory under whatever :func:`get_cache_base` returns. + """ + if base is None: + # Use native string to avoid issues on 2.x: see Python #20140. + base = os.path.join(get_cache_base(), str('resource-cache')) + # we use 'isdir' instead of 'exists', because we want to + # fail if there's a file with that name + if not os.path.isdir(base): + os.makedirs(base) + self.base = os.path.abspath(os.path.normpath(base)) + + def prefix_to_dir(self, prefix): + """ + Converts a resource prefix to a directory name in the cache. + """ + return path_to_cache_dir(prefix) + + def is_stale(self, resource, path): + """ + Is the cache stale for the given resource? + + :param resource: The :class:`Resource` being cached. + :param path: The path of the resource in the cache. + :return: True if the cache is stale. + """ + # Cache invalidation is a hard problem :-) + return True + + def get(self, resource): + """ + Get a resource into the cache, + + :param resource: A :class:`Resource` instance. + :return: The pathname of the resource in the cache. + """ + prefix, path = resource.finder.get_cache_info(resource) + if prefix is None: + result = path + else: + result = os.path.join(self.base, self.prefix_to_dir(prefix), path) + dirname = os.path.dirname(result) + if not os.path.isdir(dirname): + os.makedirs(dirname) + if not os.path.exists(result): + stale = True + else: + stale = self.is_stale(resource, path) + if stale: + # write the bytes of the resource to the cache location + with open(result, 'wb') as f: + f.write(resource.bytes) + return result + + def clear(self): + """ + Clear the cache. + """ + not_removed = [] + for fn in os.listdir(self.base): + fn = os.path.join(self.base, fn) + try: + if os.path.islink(fn) or os.path.isfile(fn): + os.remove(fn) + elif os.path.isdir(fn): + shutil.rmtree(fn) + except Exception: + not_removed.append(fn) + return not_removed + +cache = Cache() + + +class ResourceBase(object): + def __init__(self, finder, name): + self.finder = finder + self.name = name + + +class Resource(ResourceBase): + """ + A class representing an in-package resource, such as a data file. This is + not normally instantiated by user code, but rather by a + :class:`ResourceFinder` which manages the resource. + """ + is_container = False # Backwards compatibility + + def as_stream(self): + """ + Get the resource as a stream. + + This is not a property to make it obvious that it returns a new stream + each time. + """ + return self.finder.get_stream(self) + + @cached_property + def file_path(self): + return cache.get(self) + + @cached_property + def bytes(self): + return self.finder.get_bytes(self) + + @cached_property + def size(self): + return self.finder.get_size(self) + + +class ResourceContainer(ResourceBase): + is_container = True # Backwards compatibility + + @cached_property + def resources(self): + return self.finder.get_resources(self) + + +class ResourceFinder(object): + """ + Resource finder for file system resources. + """ + def __init__(self, module): + self.module = module + self.loader = getattr(module, '__loader__', None) + self.base = os.path.dirname(getattr(module, '__file__', '')) + + def _adjust_path(self, path): + return os.path.realpath(path) + + def _make_path(self, resource_name): + parts = resource_name.split('/') + parts.insert(0, self.base) + result = os.path.join(*parts) + return self._adjust_path(result) + + def _find(self, path): + return os.path.exists(path) + + def get_cache_info(self, resource): + return None, resource.path + + def find(self, resource_name): + path = self._make_path(resource_name) + if not self._find(path): + result = None + else: + if self._is_directory(path): + result = ResourceContainer(self, resource_name) + else: + result = Resource(self, resource_name) + result.path = path + return result + + def get_stream(self, resource): + return open(resource.path, 'rb') + + def get_bytes(self, resource): + with open(resource.path, 'rb') as f: + return f.read() + + def get_size(self, resource): + return os.path.getsize(resource.path) + + def get_resources(self, resource): + def allowed(f): + return f != '__pycache__' and not f.endswith(('.pyc', '.pyo')) + return set([f for f in os.listdir(resource.path) if allowed(f)]) + + def is_container(self, resource): + return self._is_directory(resource.path) + + _is_directory = staticmethod(os.path.isdir) + + +class ZipResourceFinder(ResourceFinder): + """ + Resource finder for resources in .zip files. + """ + def __init__(self, module): + super(ZipResourceFinder, self).__init__(module) + archive = self.loader.archive + self.prefix_len = 1 + len(archive) + # PyPy doesn't have a _files attr on zipimporter, and you can't set one + if hasattr(self.loader, '_files'): + self._files = self.loader._files + else: + self._files = zipimport._zip_directory_cache[archive] + self.index = sorted(self._files) + + def _adjust_path(self, path): + return path + + def _find(self, path): + path = path[self.prefix_len:] + if path in self._files: + result = True + else: + if path and path[-1] != os.sep: + path = path + os.sep + i = bisect.bisect(self.index, path) + try: + result = self.index[i].startswith(path) + except IndexError: + result = False + if not result: + logger.debug('_find failed: %r %r', path, self.loader.prefix) + else: + logger.debug('_find worked: %r %r', path, self.loader.prefix) + return result + + def get_cache_info(self, resource): + prefix = self.loader.archive + path = resource.path[1 + len(prefix):] + return prefix, path + + def get_bytes(self, resource): + return self.loader.get_data(resource.path) + + def get_stream(self, resource): + return io.BytesIO(self.get_bytes(resource)) + + def get_size(self, resource): + path = resource.path[self.prefix_len:] + return self._files[path][3] + + def get_resources(self, resource): + path = resource.path[self.prefix_len:] + if path and path[-1] != os.sep: + path += os.sep + plen = len(path) + result = set() + i = bisect.bisect(self.index, path) + while i < len(self.index): + if not self.index[i].startswith(path): + break + s = self.index[i][plen:] + result.add(s.split(os.sep, 1)[0]) # only immediate children + i += 1 + return result + + def _is_directory(self, path): + path = path[self.prefix_len:] + if path and path[-1] != os.sep: + path += os.sep + i = bisect.bisect(self.index, path) + try: + result = self.index[i].startswith(path) + except IndexError: + result = False + return result + +_finder_registry = { + type(None): ResourceFinder, + zipimport.zipimporter: ZipResourceFinder +} + +try: + import _frozen_importlib + _finder_registry[_frozen_importlib.SourceFileLoader] = ResourceFinder + _finder_registry[_frozen_importlib.FileFinder] = ResourceFinder +except (ImportError, AttributeError): + pass + + +def register_finder(loader, finder_maker): + _finder_registry[type(loader)] = finder_maker + +_finder_cache = {} + + +def finder(package): + """ + Return a resource finder for a package. + :param package: The name of the package. + :return: A :class:`ResourceFinder` instance for the package. + """ + if package in _finder_cache: + result = _finder_cache[package] + else: + if package not in sys.modules: + __import__(package) + module = sys.modules[package] + path = getattr(module, '__path__', None) + if path is None: + raise DistlibException('You cannot get a finder for a module, ' + 'only for a package') + loader = getattr(module, '__loader__', None) + finder_maker = _finder_registry.get(type(loader)) + if finder_maker is None: + raise DistlibException('Unable to locate finder for %r' % package) + result = finder_maker(module) + _finder_cache[package] = result + return result + + +_dummy_module = types.ModuleType(str('__dummy__')) + + +def finder_for_path(path): + """ + Return a resource finder for a path, which should represent a container. + + :param path: The path. + :return: A :class:`ResourceFinder` instance for the path. + """ + result = None + # calls any path hooks, gets importer into cache + pkgutil.get_importer(path) + loader = sys.path_importer_cache.get(path) + finder = _finder_registry.get(type(loader)) + if finder: + module = _dummy_module + module.__file__ = os.path.join(path, '') + module.__loader__ = loader + result = finder(module) + return result diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/scripts.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/scripts.py new file mode 100644 index 0000000..ba0e520 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/scripts.py @@ -0,0 +1,317 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +from io import BytesIO +import logging +import os +import re +import struct +import sys + +from .compat import sysconfig, fsencode, detect_encoding, ZipFile +from .resources import finder +from .util import (FileOperator, get_export_entry, convert_path, + get_executable, in_venv) + +logger = logging.getLogger(__name__) + +_DEFAULT_MANIFEST = ''' + + + + + + + + + + + + +'''.strip() + +# check if Python is called on the first line with this expression +FIRST_LINE_RE = re.compile(b'^#!.*pythonw?[0-9.]*([ \t].*)?$') +SCRIPT_TEMPLATE = '''# -*- coding: utf-8 -*- +if __name__ == '__main__': + import sys, re + + def _resolve(module, func): + __import__(module) + mod = sys.modules[module] + parts = func.split('.') + result = getattr(mod, parts.pop(0)) + for p in parts: + result = getattr(result, p) + return result + + try: + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + + func = _resolve('%(module)s', '%(func)s') + rc = func() # None interpreted as 0 + except Exception as e: # only supporting Python >= 2.6 + sys.stderr.write('%%s\\n' %% e) + rc = 1 + sys.exit(rc) +''' + + +class ScriptMaker(object): + """ + A class to copy or create scripts from source scripts or callable + specifications. + """ + script_template = SCRIPT_TEMPLATE + + executable = None # for shebangs + + def __init__(self, source_dir, target_dir, add_launchers=True, + dry_run=False, fileop=None): + self.source_dir = source_dir + self.target_dir = target_dir + self.add_launchers = add_launchers + self.force = False + self.clobber = False + # It only makes sense to set mode bits on POSIX. + self.set_mode = (os.name == 'posix') + self.variants = set(('', 'X.Y')) + self._fileop = fileop or FileOperator(dry_run) + + def _get_alternate_executable(self, executable, options): + if options.get('gui', False) and os.name == 'nt': + dn, fn = os.path.split(executable) + fn = fn.replace('python', 'pythonw') + executable = os.path.join(dn, fn) + return executable + + def _get_shebang(self, encoding, post_interp=b'', options=None): + if self.executable: + executable = self.executable + elif not sysconfig.is_python_build(): + executable = get_executable() + elif in_venv(): + executable = os.path.join(sysconfig.get_path('scripts'), + 'python%s' % sysconfig.get_config_var('EXE')) + else: + executable = os.path.join( + sysconfig.get_config_var('BINDIR'), + 'python%s%s' % (sysconfig.get_config_var('VERSION'), + sysconfig.get_config_var('EXE'))) + if options: + executable = self._get_alternate_executable(executable, options) + + executable = fsencode(executable) + shebang = b'#!' + executable + post_interp + b'\n' + # Python parser starts to read a script using UTF-8 until + # it gets a #coding:xxx cookie. The shebang has to be the + # first line of a file, the #coding:xxx cookie cannot be + # written before. So the shebang has to be decodable from + # UTF-8. + try: + shebang.decode('utf-8') + except UnicodeDecodeError: + raise ValueError( + 'The shebang (%r) is not decodable from utf-8' % shebang) + # If the script is encoded to a custom encoding (use a + # #coding:xxx cookie), the shebang has to be decodable from + # the script encoding too. + if encoding != 'utf-8': + try: + shebang.decode(encoding) + except UnicodeDecodeError: + raise ValueError( + 'The shebang (%r) is not decodable ' + 'from the script encoding (%r)' % (shebang, encoding)) + return shebang + + def _get_script_text(self, entry): + return self.script_template % dict(module=entry.prefix, + func=entry.suffix) + + manifest = _DEFAULT_MANIFEST + + def get_manifest(self, exename): + base = os.path.basename(exename) + return self.manifest % base + + def _write_script(self, names, shebang, script_bytes, filenames, ext): + use_launcher = self.add_launchers and os.name == 'nt' + linesep = os.linesep.encode('utf-8') + if not use_launcher: + script_bytes = shebang + linesep + script_bytes + else: + if ext == 'py': + launcher = self._get_launcher('t') + else: + launcher = self._get_launcher('w') + stream = BytesIO() + with ZipFile(stream, 'w') as zf: + zf.writestr('__main__.py', script_bytes) + zip_data = stream.getvalue() + script_bytes = launcher + shebang + linesep + zip_data + for name in names: + outname = os.path.join(self.target_dir, name) + if use_launcher: + n, e = os.path.splitext(outname) + if e.startswith('.py'): + outname = n + outname = '%s.exe' % outname + try: + self._fileop.write_binary_file(outname, script_bytes) + except Exception: + # Failed writing an executable - it might be in use. + logger.warning('Failed to write executable - trying to ' + 'use .deleteme logic') + dfname = '%s.deleteme' % outname + if os.path.exists(dfname): + os.remove(dfname) # Not allowed to fail here + os.rename(outname, dfname) # nor here + self._fileop.write_binary_file(outname, script_bytes) + logger.debug('Able to replace executable using ' + '.deleteme logic') + try: + os.remove(dfname) + except Exception: + pass # still in use - ignore error + else: + if os.name == 'nt' and not outname.endswith('.' + ext): + outname = '%s.%s' % (outname, ext) + if os.path.exists(outname) and not self.clobber: + logger.warning('Skipping existing file %s', outname) + continue + self._fileop.write_binary_file(outname, script_bytes) + if self.set_mode: + self._fileop.set_executable_mode([outname]) + filenames.append(outname) + + def _make_script(self, entry, filenames, options=None): + shebang = self._get_shebang('utf-8', options=options) + script = self._get_script_text(entry).encode('utf-8') + name = entry.name + scriptnames = set() + if '' in self.variants: + scriptnames.add(name) + if 'X' in self.variants: + scriptnames.add('%s%s' % (name, sys.version[0])) + if 'X.Y' in self.variants: + scriptnames.add('%s-%s' % (name, sys.version[:3])) + if options and options.get('gui', False): + ext = 'pyw' + else: + ext = 'py' + self._write_script(scriptnames, shebang, script, filenames, ext) + + def _copy_script(self, script, filenames): + adjust = False + script = os.path.join(self.source_dir, convert_path(script)) + outname = os.path.join(self.target_dir, os.path.basename(script)) + if not self.force and not self._fileop.newer(script, outname): + logger.debug('not copying %s (up-to-date)', script) + return + + # Always open the file, but ignore failures in dry-run mode -- + # that way, we'll get accurate feedback if we can read the + # script. + try: + f = open(script, 'rb') + except IOError: + if not self.dry_run: + raise + f = None + else: + encoding, lines = detect_encoding(f.readline) + f.seek(0) + first_line = f.readline() + if not first_line: + logger.warning('%s: %s is an empty file (skipping)', + self.get_command_name(), script) + return + + match = FIRST_LINE_RE.match(first_line.replace(b'\r\n', b'\n')) + if match: + adjust = True + post_interp = match.group(1) or b'' + + if not adjust: + if f: + f.close() + self._fileop.copy_file(script, outname) + if self.set_mode: + self._fileop.set_executable_mode([outname]) + filenames.append(outname) + else: + logger.info('copying and adjusting %s -> %s', script, + self.target_dir) + if not self._fileop.dry_run: + shebang = self._get_shebang(encoding, post_interp) + if b'pythonw' in first_line: + ext = 'pyw' + else: + ext = 'py' + n = os.path.basename(outname) + self._write_script([n], shebang, f.read(), filenames, ext) + if f: + f.close() + + @property + def dry_run(self): + return self._fileop.dry_run + + @dry_run.setter + def dry_run(self, value): + self._fileop.dry_run = value + + if os.name == 'nt': + # Executable launcher support. + # Launchers are from https://bitbucket.org/vinay.sajip/simple_launcher/ + + def _get_launcher(self, kind): + if struct.calcsize('P') == 8: # 64-bit + bits = '64' + else: + bits = '32' + name = '%s%s.exe' % (kind, bits) + # Issue 31: don't hardcode an absolute package name, but + # determine it relative to the current package + distlib_package = __name__.rsplit('.', 1)[0] + result = finder(distlib_package).find(name).bytes + return result + + # Public API follows + + def make(self, specification, options=None): + """ + Make a script. + + :param specification: The specification, which is either a valid export + entry specification (to make a script from a + callable) or a filename (to make a script by + copying from a source location). + :param options: A dictionary of options controlling script generation. + :return: A list of all absolute pathnames written to. + """ + filenames = [] + entry = get_export_entry(specification) + if entry is None: + self._copy_script(specification, filenames) + else: + self._make_script(entry, filenames, options=options) + return filenames + + def make_multiple(self, specifications, options=None): + """ + Take a list of specifications and make scripts from them, + :param specifications: A list of specifications. + :return: A list of all absolute pathnames written to, + """ + filenames = [] + for specification in specifications: + filenames.extend(self.make(specification, options)) + return filenames diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/t32.exe b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/t32.exe new file mode 100644 index 0000000..43f39f3 Binary files /dev/null and b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/t32.exe differ diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/t64.exe b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/t64.exe new file mode 100644 index 0000000..73e2f40 Binary files /dev/null and b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/t64.exe differ diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/util.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/util.py new file mode 100644 index 0000000..e64d078 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/util.py @@ -0,0 +1,1532 @@ +# +# Copyright (C) 2012-2013 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +import codecs +from collections import deque +import contextlib +import csv +from glob import iglob as std_iglob +import io +import json +import logging +import os +import py_compile +import re +import shutil +import socket +import ssl +import subprocess +import sys +import tarfile +import tempfile +try: + import threading +except ImportError: + import dummy_threading as threading +import time + +from . import DistlibException +from .compat import (string_types, text_type, shutil, raw_input, StringIO, + cache_from_source, urlopen, httplib, xmlrpclib, splittype, + HTTPHandler, HTTPSHandler as BaseHTTPSHandler, + BaseConfigurator, valid_ident, Container, configparser, + URLError, match_hostname, CertificateError, ZipFile) + +logger = logging.getLogger(__name__) + +# +# Requirement parsing code for name + optional constraints + optional extras +# +# e.g. 'foo >= 1.2, < 2.0 [bar, baz]' +# +# The regex can seem a bit hairy, so we build it up out of smaller pieces +# which are manageable. +# + +COMMA = r'\s*,\s*' +COMMA_RE = re.compile(COMMA) + +IDENT = r'(\w|[.-])+' +EXTRA_IDENT = r'(\*|:(\*|\w+):|' + IDENT + ')' +VERSPEC = IDENT + r'\*?' + +RELOP = '([<>=!~]=)|[<>]' + +# +# The first relop is optional - if absent, will be taken as '~=' +# +BARE_CONSTRAINTS = ('(' + RELOP + r')?\s*(' + VERSPEC + ')(' + COMMA + '(' + + RELOP + r')\s*(' + VERSPEC + '))*') + +DIRECT_REF = '(from\s+(?P.*))' + +# +# Either the bare constraints or the bare constraints in parentheses +# +CONSTRAINTS = (r'\(\s*(?P' + BARE_CONSTRAINTS + '|' + DIRECT_REF + + r')\s*\)|(?P' + BARE_CONSTRAINTS + '\s*)') + +EXTRA_LIST = EXTRA_IDENT + '(' + COMMA + EXTRA_IDENT + ')*' +EXTRAS = r'\[\s*(?P' + EXTRA_LIST + r')?\s*\]' +REQUIREMENT = ('(?P' + IDENT + r')\s*(' + EXTRAS + r'\s*)?(\s*' + + CONSTRAINTS + ')?$') +REQUIREMENT_RE = re.compile(REQUIREMENT) + +# +# Used to scan through the constraints +# +RELOP_IDENT = '(?P' + RELOP + r')\s*(?P' + VERSPEC + ')' +RELOP_IDENT_RE = re.compile(RELOP_IDENT) + +def parse_requirement(s): + + def get_constraint(m): + d = m.groupdict() + return d['op'], d['vn'] + + result = None + m = REQUIREMENT_RE.match(s) + if m: + d = m.groupdict() + name = d['dn'] + cons = d['c1'] or d['c2'] + if not d['diref']: + url = None + else: + # direct reference + cons = None + url = d['diref'].strip() + if not cons: + cons = None + constr = '' + rs = d['dn'] + else: + if cons[0] not in '<>!=': + cons = '~=' + cons + iterator = RELOP_IDENT_RE.finditer(cons) + cons = [get_constraint(m) for m in iterator] + rs = '%s (%s)' % (name, ', '.join(['%s %s' % con for con in cons])) + if not d['ex']: + extras = None + else: + extras = COMMA_RE.split(d['ex']) + result = Container(name=name, constraints=cons, extras=extras, + requirement=rs, source=s, url=url) + return result + + +def get_resources_dests(resources_root, rules): + """Find destinations for resources files""" + + def get_rel_path(base, path): + # normalizes and returns a lstripped-/-separated path + base = base.replace(os.path.sep, '/') + path = path.replace(os.path.sep, '/') + assert path.startswith(base) + return path[len(base):].lstrip('/') + + + destinations = {} + for base, suffix, dest in rules: + prefix = os.path.join(resources_root, base) + for abs_base in iglob(prefix): + abs_glob = os.path.join(abs_base, suffix) + for abs_path in iglob(abs_glob): + resource_file = get_rel_path(resources_root, abs_path) + if dest is None: # remove the entry if it was here + destinations.pop(resource_file, None) + else: + rel_path = get_rel_path(abs_base, abs_path) + rel_dest = dest.replace(os.path.sep, '/').rstrip('/') + destinations[resource_file] = rel_dest + '/' + rel_path + return destinations + + +def in_venv(): + if hasattr(sys, 'real_prefix'): + # virtualenv venvs + result = True + else: + # PEP 405 venvs + result = sys.prefix != getattr(sys, 'base_prefix', sys.prefix) + return result + + +def get_executable(): + if sys.platform == 'darwin' and ('__VENV_LAUNCHER__' + in os.environ): + result = os.environ['__VENV_LAUNCHER__'] + else: + result = sys.executable + return result + + +def proceed(prompt, allowed_chars, error_prompt=None, default=None): + p = prompt + while True: + s = raw_input(p) + p = prompt + if not s and default: + s = default + if s: + c = s[0].lower() + if c in allowed_chars: + break + if error_prompt: + p = '%c: %s\n%s' % (c, error_prompt, prompt) + return c + + +def extract_by_key(d, keys): + if isinstance(keys, string_types): + keys = keys.split() + result = {} + for key in keys: + if key in d: + result[key] = d[key] + return result + +def read_exports(stream): + if sys.version_info[0] >= 3: + # needs to be a text stream + stream = codecs.getreader('utf-8')(stream) + # Try to load as JSON, falling back on legacy format + data = stream.read() + stream = StringIO(data) + try: + data = json.load(stream) + result = data['exports'] + for group, entries in result.items(): + for k, v in entries.items(): + s = '%s = %s' % (k, v) + entry = get_export_entry(s) + assert entry is not None + entries[k] = entry + return result + except Exception: + stream.seek(0, 0) + cp = configparser.ConfigParser() + if hasattr(cp, 'read_file'): + cp.read_file(stream) + else: + cp.readfp(stream) + result = {} + for key in cp.sections(): + result[key] = entries = {} + for name, value in cp.items(key): + s = '%s = %s' % (name, value) + entry = get_export_entry(s) + assert entry is not None + #entry.dist = self + entries[name] = entry + return result + + +def write_exports(exports, stream): + if sys.version_info[0] >= 3: + # needs to be a text stream + stream = codecs.getwriter('utf-8')(stream) + cp = configparser.ConfigParser() + for k, v in exports.items(): + # TODO check k, v for valid values + cp.add_section(k) + for entry in v.values(): + if entry.suffix is None: + s = entry.prefix + else: + s = '%s:%s' % (entry.prefix, entry.suffix) + if entry.flags: + s = '%s [%s]' % (s, ', '.join(entry.flags)) + cp.set(k, entry.name, s) + cp.write(stream) + + +@contextlib.contextmanager +def tempdir(): + td = tempfile.mkdtemp() + try: + yield td + finally: + shutil.rmtree(td) + +@contextlib.contextmanager +def chdir(d): + cwd = os.getcwd() + try: + os.chdir(d) + yield + finally: + os.chdir(cwd) + + +@contextlib.contextmanager +def socket_timeout(seconds=15): + cto = socket.getdefaulttimeout() + try: + socket.setdefaulttimeout(seconds) + yield + finally: + socket.setdefaulttimeout(cto) + + +class cached_property(object): + def __init__(self, func): + self.func = func + #for attr in ('__name__', '__module__', '__doc__'): + # setattr(self, attr, getattr(func, attr, None)) + + def __get__(self, obj, cls=None): + if obj is None: + return self + value = self.func(obj) + object.__setattr__(obj, self.func.__name__, value) + #obj.__dict__[self.func.__name__] = value = self.func(obj) + return value + +def convert_path(pathname): + """Return 'pathname' as a name that will work on the native filesystem. + + The path is split on '/' and put back together again using the current + directory separator. Needed because filenames in the setup script are + always supplied in Unix style, and have to be converted to the local + convention before we can actually use them in the filesystem. Raises + ValueError on non-Unix-ish systems if 'pathname' either starts or + ends with a slash. + """ + if os.sep == '/': + return pathname + if not pathname: + return pathname + if pathname[0] == '/': + raise ValueError("path '%s' cannot be absolute" % pathname) + if pathname[-1] == '/': + raise ValueError("path '%s' cannot end with '/'" % pathname) + + paths = pathname.split('/') + while os.curdir in paths: + paths.remove(os.curdir) + if not paths: + return os.curdir + return os.path.join(*paths) + + +class FileOperator(object): + def __init__(self, dry_run=False): + self.dry_run = dry_run + self.ensured = set() + self._init_record() + + def _init_record(self): + self.record = False + self.files_written = set() + self.dirs_created = set() + + def record_as_written(self, path): + if self.record: + self.files_written.add(path) + + def newer(self, source, target): + """Tell if the target is newer than the source. + + Returns true if 'source' exists and is more recently modified than + 'target', or if 'source' exists and 'target' doesn't. + + Returns false if both exist and 'target' is the same age or younger + than 'source'. Raise PackagingFileError if 'source' does not exist. + + Note that this test is not very accurate: files created in the same + second will have the same "age". + """ + if not os.path.exists(source): + raise DistlibException("file '%r' does not exist" % + os.path.abspath(source)) + if not os.path.exists(target): + return True + + return os.stat(source).st_mtime > os.stat(target).st_mtime + + def copy_file(self, infile, outfile, check=True): + """Copy a file respecting dry-run and force flags. + """ + self.ensure_dir(os.path.dirname(outfile)) + logger.info('Copying %s to %s', infile, outfile) + if not self.dry_run: + msg = None + if check: + if os.path.islink(outfile): + msg = '%s is a symlink' % outfile + elif os.path.exists(outfile) and not os.path.isfile(outfile): + msg = '%s is a non-regular file' % outfile + if msg: + raise ValueError(msg + ' which would be overwritten') + shutil.copyfile(infile, outfile) + self.record_as_written(outfile) + + def copy_stream(self, instream, outfile, encoding=None): + assert not os.path.isdir(outfile) + self.ensure_dir(os.path.dirname(outfile)) + logger.info('Copying stream %s to %s', instream, outfile) + if not self.dry_run: + if encoding is None: + outstream = open(outfile, 'wb') + else: + outstream = codecs.open(outfile, 'w', encoding=encoding) + try: + shutil.copyfileobj(instream, outstream) + finally: + outstream.close() + self.record_as_written(outfile) + + def write_binary_file(self, path, data): + self.ensure_dir(os.path.dirname(path)) + if not self.dry_run: + with open(path, 'wb') as f: + f.write(data) + self.record_as_written(path) + + def write_text_file(self, path, data, encoding): + self.ensure_dir(os.path.dirname(path)) + if not self.dry_run: + with open(path, 'wb') as f: + f.write(data.encode(encoding)) + self.record_as_written(path) + + def set_mode(self, bits, mask, files): + if os.name == 'posix': + # Set the executable bits (owner, group, and world) on + # all the files specified. + for f in files: + if self.dry_run: + logger.info("changing mode of %s", f) + else: + mode = (os.stat(f).st_mode | bits) & mask + logger.info("changing mode of %s to %o", f, mode) + os.chmod(f, mode) + + set_executable_mode = lambda s, f: s.set_mode(0o555, 0o7777, f) + + def ensure_dir(self, path): + path = os.path.abspath(path) + if path not in self.ensured and not os.path.exists(path): + self.ensured.add(path) + d, f = os.path.split(path) + self.ensure_dir(d) + logger.info('Creating %s' % path) + if not self.dry_run: + os.mkdir(path) + if self.record: + self.dirs_created.add(path) + + def byte_compile(self, path, optimize=False, force=False, prefix=None): + dpath = cache_from_source(path, not optimize) + logger.info('Byte-compiling %s to %s', path, dpath) + if not self.dry_run: + if force or self.newer(path, dpath): + if not prefix: + diagpath = None + else: + assert path.startswith(prefix) + diagpath = path[len(prefix):] + py_compile.compile(path, dpath, diagpath, True) # raise error + self.record_as_written(dpath) + return dpath + + def ensure_removed(self, path): + if os.path.exists(path): + if os.path.isdir(path) and not os.path.islink(path): + logger.debug('Removing directory tree at %s', path) + if not self.dry_run: + shutil.rmtree(path) + if self.record: + if path in self.dirs_created: + self.dirs_created.remove(path) + else: + if os.path.islink(path): + s = 'link' + else: + s = 'file' + logger.debug('Removing %s %s', s, path) + if not self.dry_run: + os.remove(path) + if self.record: + if path in self.files_written: + self.files_written.remove(path) + + def is_writable(self, path): + result = False + while not result: + if os.path.exists(path): + result = os.access(path, os.W_OK) + break + parent = os.path.dirname(path) + if parent == path: + break + path = parent + return result + + def commit(self): + """ + Commit recorded changes, turn off recording, return + changes. + """ + assert self.record + result = self.files_written, self.dirs_created + self._init_record() + return result + + def rollback(self): + if not self.dry_run: + for f in list(self.files_written): + if os.path.exists(f): + os.remove(f) + # dirs should all be empty now, except perhaps for + # __pycache__ subdirs + # reverse so that subdirs appear before their parents + dirs = sorted(self.dirs_created, reverse=True) + for d in dirs: + flist = os.listdir(d) + if flist: + assert flist == ['__pycache__'] + sd = os.path.join(d, flist[0]) + os.rmdir(sd) + os.rmdir(d) # should fail if non-empty + self._init_record() + +def resolve(module_name, dotted_path): + if module_name in sys.modules: + mod = sys.modules[module_name] + else: + mod = __import__(module_name) + if dotted_path is None: + result = mod + else: + parts = dotted_path.split('.') + result = getattr(mod, parts.pop(0)) + for p in parts: + result = getattr(result, p) + return result + + +class ExportEntry(object): + def __init__(self, name, prefix, suffix, flags): + self.name = name + self.prefix = prefix + self.suffix = suffix + self.flags = flags + + @cached_property + def value(self): + return resolve(self.prefix, self.suffix) + + def __repr__(self): + return '' % (self.name, self.prefix, + self.suffix, self.flags) + + def __eq__(self, other): + if not isinstance(other, ExportEntry): + result = False + else: + result = (self.name == other.name and + self.prefix == other.prefix and + self.suffix == other.suffix and + self.flags == other.flags) + return result + + __hash__ = object.__hash__ + + +ENTRY_RE = re.compile(r'''(?P(\w|[-.])+) + \s*=\s*(?P(\w+)([:\.]\w+)*) + \s*(\[\s*(?P\w+(=\w+)?(,\s*\w+(=\w+)?)*)\s*\])? + ''', re.VERBOSE) + + +def get_export_entry(specification): + m = ENTRY_RE.search(specification) + if not m: + result = None + if '[' in specification or ']' in specification: + raise DistlibException('Invalid specification ' + '%r' % specification) + else: + d = m.groupdict() + name = d['name'] + path = d['callable'] + colons = path.count(':') + if colons == 0: + prefix, suffix = path, None + else: + if colons != 1: + raise DistlibException('Invalid specification ' + '%r' % specification) + prefix, suffix = path.split(':') + flags = d['flags'] + if flags is None: + if '[' in specification or ']' in specification: + raise DistlibException('Invalid specification ' + '%r' % specification) + flags = [] + else: + flags = [f.strip() for f in flags.split(',')] + result = ExportEntry(name, prefix, suffix, flags) + return result + + +def get_cache_base(suffix=None): + """ + Return the default base location for distlib caches. If the directory does + not exist, it is created. Use the suffix provided for the base directory, + and default to '.distlib' if it isn't provided. + + On Windows, if LOCALAPPDATA is defined in the environment, then it is + assumed to be a directory, and will be the parent directory of the result. + On POSIX, and on Windows if LOCALAPPDATA is not defined, the user's home + directory - using os.expanduser('~') - will be the parent directory of + the result. + + The result is just the directory '.distlib' in the parent directory as + determined above, or with the name specified with ``suffix``. + """ + if suffix is None: + suffix = '.distlib' + if os.name == 'nt' and 'LOCALAPPDATA' in os.environ: + result = os.path.expandvars('$localappdata') + else: + # Assume posix, or old Windows + result = os.path.expanduser('~') + result = os.path.join(result, suffix) + # we use 'isdir' instead of 'exists', because we want to + # fail if there's a file with that name + if os.path.isdir(result): + usable = os.access(result, os.W_OK) + if not usable: + logger.warning('Directory exists but is not writable: %s', result) + else: + try: + os.makedirs(result) + usable = True + except OSError: + logger.warning('Unable to create %s', result, exc_info=True) + usable = False + if not usable: + result = tempfile.mkdtemp() + logger.warning('Default location unusable, using %s', result) + return result + + +def path_to_cache_dir(path): + """ + Convert an absolute path to a directory name for use in a cache. + + The algorithm used is: + + #. On Windows, any ``':'`` in the drive is replaced with ``'---'``. + #. Any occurrence of ``os.sep`` is replaced with ``'--'``. + #. ``'.cache'`` is appended. + """ + d, p = os.path.splitdrive(os.path.abspath(path)) + if d: + d = d.replace(':', '---') + p = p.replace(os.sep, '--') + return d + p + '.cache' + + +def ensure_slash(s): + if not s.endswith('/'): + return s + '/' + return s + + +def parse_credentials(netloc): + username = password = None + if '@' in netloc: + prefix, netloc = netloc.split('@', 1) + if ':' not in prefix: + username = prefix + else: + username, password = prefix.split(':', 1) + return username, password, netloc + + +def get_process_umask(): + result = os.umask(0o22) + os.umask(result) + return result + +def is_string_sequence(seq): + result = True + i = None + for i, s in enumerate(seq): + if not isinstance(s, string_types): + result = False + break + assert i is not None + return result + +PROJECT_NAME_AND_VERSION = re.compile('([a-z0-9_]+([.-][a-z_][a-z0-9_]*)*)-' + '([a-z0-9_.+-]+)', re.I) +PYTHON_VERSION = re.compile(r'-py(\d\.?\d?)') + + +def split_filename(filename, project_name=None): + """ + Extract name, version, python version from a filename (no extension) + + Return name, version, pyver or None + """ + result = None + pyver = None + m = PYTHON_VERSION.search(filename) + if m: + pyver = m.group(1) + filename = filename[:m.start()] + if project_name and len(filename) > len(project_name) + 1: + m = re.match(re.escape(project_name) + r'\b', filename) + if m: + n = m.end() + result = filename[:n], filename[n + 1:], pyver + if result is None: + m = PROJECT_NAME_AND_VERSION.match(filename) + if m: + result = m.group(1), m.group(3), pyver + return result + +# Allow spaces in name because of legacy dists like "Twisted Core" +NAME_VERSION_RE = re.compile(r'(?P[\w .-]+)\s*' + r'\(\s*(?P[^\s)]+)\)$') + +def parse_name_and_version(p): + """ + A utility method used to get name and version from a string. + + From e.g. a Provides-Dist value. + + :param p: A value in a form 'foo (1.0)' + :return: The name and version as a tuple. + """ + m = NAME_VERSION_RE.match(p) + if not m: + raise DistlibException('Ill-formed name/version string: \'%s\'' % p) + d = m.groupdict() + return d['name'].strip().lower(), d['ver'] + +def get_extras(requested, available): + result = set() + requested = set(requested or []) + available = set(available or []) + if '*' in requested: + requested.remove('*') + result |= available + for r in requested: + if r == '-': + result.add(r) + elif r.startswith('-'): + unwanted = r[1:] + if unwanted not in available: + logger.warning('undeclared extra: %s' % unwanted) + if unwanted in result: + result.remove(unwanted) + else: + if r not in available: + logger.warning('undeclared extra: %s' % r) + result.add(r) + return result +# +# Extended metadata functionality +# + +def _get_external_data(url): + result = {} + try: + # urlopen might fail if it runs into redirections, + # because of Python issue #13696. Fixed in locators + # using a custom redirect handler. + resp = urlopen(url) + headers = resp.info() + if headers.get('Content-Type') != 'application/json': + logger.debug('Unexpected response for JSON request') + else: + reader = codecs.getreader('utf-8')(resp) + #data = reader.read().decode('utf-8') + #result = json.loads(data) + result = json.load(reader) + except Exception as e: + logger.exception('Failed to get external data for %s: %s', url, e) + return result + + +def get_project_data(name): + url = ('https://www.red-dove.com/pypi/projects/' + '%s/%s/project.json' % (name[0].upper(), name)) + result = _get_external_data(url) + return result + +def get_package_data(name, version): + url = ('https://www.red-dove.com/pypi/projects/' + '%s/%s/package-%s.json' % (name[0].upper(), name, version)) + return _get_external_data(url) + + +class EventMixin(object): + """ + A very simple publish/subscribe system. + """ + def __init__(self): + self._subscribers = {} + + def add(self, event, subscriber, append=True): + """ + Add a subscriber for an event. + + :param event: The name of an event. + :param subscriber: The subscriber to be added (and called when the + event is published). + :param append: Whether to append or prepend the subscriber to an + existing subscriber list for the event. + """ + subs = self._subscribers + if event not in subs: + subs[event] = deque([subscriber]) + else: + sq = subs[event] + if append: + sq.append(subscriber) + else: + sq.appendleft(subscriber) + + def remove(self, event, subscriber): + """ + Remove a subscriber for an event. + + :param event: The name of an event. + :param subscriber: The subscriber to be removed. + """ + subs = self._subscribers + if event not in subs: + raise ValueError('No subscribers: %r' % event) + subs[event].remove(subscriber) + + def get_subscribers(self, event): + """ + Return an iterator for the subscribers for an event. + :param event: The event to return subscribers for. + """ + return iter(self._subscribers.get(event, ())) + + def publish(self, event, *args, **kwargs): + """ + Publish a event and return a list of values returned by its + subscribers. + + :param event: The event to publish. + :param args: The positional arguments to pass to the event's + subscribers. + :param kwargs: The keyword arguments to pass to the event's + subscribers. + """ + result = [] + for subscriber in self.get_subscribers(event): + try: + value = subscriber(event, *args, **kwargs) + except Exception: + logger.exception('Exception during event publication') + value = None + result.append(value) + logger.debug('publish %s: args = %s, kwargs = %s, result = %s', + event, args, kwargs, result) + return result + +# +# Simple sequencing +# +class Sequencer(object): + def __init__(self): + self._preds = {} + self._succs = {} + self._nodes = set() # nodes with no preds/succs + + def add_node(self, node): + self._nodes.add(node) + + def remove_node(self, node, edges=False): + if node in self._nodes: + self._nodes.remove(node) + if edges: + for p in set(self._preds.get(node, ())): + self.remove(p, node) + for s in set(self._succs.get(node, ())): + self.remove(node, s) + # Remove empties + for k, v in list(self._preds.items()): + if not v: + del self._preds[k] + for k, v in list(self._succs.items()): + if not v: + del self._succs[k] + + def add(self, pred, succ): + assert pred != succ + self._preds.setdefault(succ, set()).add(pred) + self._succs.setdefault(pred, set()).add(succ) + + def remove(self, pred, succ): + assert pred != succ + try: + preds = self._preds[succ] + succs = self._succs[pred] + except KeyError: + raise ValueError('%r not a successor of anything' % succ) + try: + preds.remove(pred) + succs.remove(succ) + except KeyError: + raise ValueError('%r not a successor of %r' % (succ, pred)) + + def is_step(self, step): + return (step in self._preds or step in self._succs or + step in self._nodes) + + def get_steps(self, final): + if not self.is_step(final): + raise ValueError('Unknown: %r' % final) + result = [] + todo = [] + seen = set() + todo.append(final) + while todo: + step = todo.pop(0) + if step in seen: + # if a step was already seen, + # move it to the end (so it will appear earlier + # when reversed on return) ... but not for the + # final step, as that would be confusing for + # users + if step != final: + result.remove(step) + result.append(step) + else: + seen.add(step) + result.append(step) + preds = self._preds.get(step, ()) + todo.extend(preds) + return reversed(result) + + @property + def strong_connections(self): + #http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm + index_counter = [0] + stack = [] + lowlinks = {} + index = {} + result = [] + + graph = self._succs + + def strongconnect(node): + # set the depth index for this node to the smallest unused index + index[node] = index_counter[0] + lowlinks[node] = index_counter[0] + index_counter[0] += 1 + stack.append(node) + + # Consider successors + try: + successors = graph[node] + except Exception: + successors = [] + for successor in successors: + if successor not in lowlinks: + # Successor has not yet been visited + strongconnect(successor) + lowlinks[node] = min(lowlinks[node],lowlinks[successor]) + elif successor in stack: + # the successor is in the stack and hence in the current + # strongly connected component (SCC) + lowlinks[node] = min(lowlinks[node],index[successor]) + + # If `node` is a root node, pop the stack and generate an SCC + if lowlinks[node] == index[node]: + connected_component = [] + + while True: + successor = stack.pop() + connected_component.append(successor) + if successor == node: break + component = tuple(connected_component) + # storing the result + result.append(component) + + for node in graph: + if node not in lowlinks: + strongconnect(node) + + return result + + @property + def dot(self): + result = ['digraph G {'] + for succ in self._preds: + preds = self._preds[succ] + for pred in preds: + result.append(' %s -> %s;' % (pred, succ)) + for node in self._nodes: + result.append(' %s;' % node) + result.append('}') + return '\n'.join(result) + +# +# Unarchiving functionality for zip, tar, tgz, tbz, whl +# + +ARCHIVE_EXTENSIONS = ('.tar.gz', '.tar.bz2', '.tar', '.zip', + '.tgz', '.tbz', '.whl') + +def unarchive(archive_filename, dest_dir, format=None, check=True): + + def check_path(path): + if not isinstance(path, text_type): + path = path.decode('utf-8') + p = os.path.abspath(os.path.join(dest_dir, path)) + if not p.startswith(dest_dir) or p[plen] != os.sep: + raise ValueError('path outside destination: %r' % p) + + dest_dir = os.path.abspath(dest_dir) + plen = len(dest_dir) + archive = None + if format is None: + if archive_filename.endswith(('.zip', '.whl')): + format = 'zip' + elif archive_filename.endswith(('.tar.gz', '.tgz')): + format = 'tgz' + mode = 'r:gz' + elif archive_filename.endswith(('.tar.bz2', '.tbz')): + format = 'tbz' + mode = 'r:bz2' + elif archive_filename.endswith('.tar'): + format = 'tar' + mode = 'r' + else: + raise ValueError('Unknown format for %r' % archive_filename) + try: + if format == 'zip': + archive = ZipFile(archive_filename, 'r') + if check: + names = archive.namelist() + for name in names: + check_path(name) + else: + archive = tarfile.open(archive_filename, mode) + if check: + names = archive.getnames() + for name in names: + check_path(name) + if format != 'zip' and sys.version_info[0] < 3: + # See Python issue 17153. If the dest path contains Unicode, + # tarfile extraction fails on Python 2.x if a member path name + # contains non-ASCII characters - it leads to an implicit + # bytes -> unicode conversion using ASCII to decode. + for tarinfo in archive.getmembers(): + if not isinstance(tarinfo.name, text_type): + tarinfo.name = tarinfo.name.decode('utf-8') + archive.extractall(dest_dir) + + finally: + if archive: + archive.close() + + +def zip_dir(directory): + """zip a directory tree into a BytesIO object""" + result = io.BytesIO() + dlen = len(directory) + with ZipFile(result, "w") as zf: + for root, dirs, files in os.walk(directory): + for name in files: + full = os.path.join(root, name) + rel = root[dlen:] + dest = os.path.join(rel, name) + zf.write(full, dest) + return result + +# +# Simple progress bar +# + +UNITS = ('', 'K', 'M', 'G','T','P') + + +class Progress(object): + unknown = 'UNKNOWN' + + def __init__(self, minval=0, maxval=100): + assert maxval is None or maxval >= minval + self.min = self.cur = minval + self.max = maxval + self.started = None + self.elapsed = 0 + self.done = False + + def update(self, curval): + assert self.min <= curval + assert self.max is None or curval <= self.max + self.cur = curval + now = time.time() + if self.started is None: + self.started = now + else: + self.elapsed = now - self.started + + def increment(self, incr): + assert incr >= 0 + self.update(self.cur + incr) + + def start(self): + self.update(self.min) + return self + + def stop(self): + if self.max is not None: + self.update(self.max) + self.done = True + + @property + def maximum(self): + return self.unknown if self.max is None else self.max + + @property + def percentage(self): + if self.done: + result = '100 %' + elif self.max is None: + result = ' ?? %' + else: + v = 100.0 * (self.cur - self.min) / (self.max - self.min) + result = '%3d %%' % v + return result + + def format_duration(self, duration): + if (duration <= 0) and self.max is None or self.cur == self.min: + result = '??:??:??' + #elif duration < 1: + # result = '--:--:--' + else: + result = time.strftime('%H:%M:%S', time.gmtime(duration)) + return result + + @property + def ETA(self): + if self.done: + prefix = 'Done' + t = self.elapsed + #import pdb; pdb.set_trace() + else: + prefix = 'ETA ' + if self.max is None: + t = -1 + elif self.elapsed == 0 or (self.cur == self.min): + t = 0 + else: + #import pdb; pdb.set_trace() + t = float(self.max - self.min) + t /= self.cur - self.min + t = (t - 1) * self.elapsed + return '%s: %s' % (prefix, self.format_duration(t)) + + @property + def speed(self): + if self.elapsed == 0: + result = 0.0 + else: + result = (self.cur - self.min) / self.elapsed + for unit in UNITS: + if result < 1000: + break + result /= 1000.0 + return '%d %sB/s' % (result, unit) + +# +# Glob functionality +# + +RICH_GLOB = re.compile(r'\{([^}]*)\}') +_CHECK_RECURSIVE_GLOB = re.compile(r'[^/\\,{]\*\*|\*\*[^/\\,}]') +_CHECK_MISMATCH_SET = re.compile(r'^[^{]*\}|\{[^}]*$') + + +def iglob(path_glob): + """Extended globbing function that supports ** and {opt1,opt2,opt3}.""" + if _CHECK_RECURSIVE_GLOB.search(path_glob): + msg = """invalid glob %r: recursive glob "**" must be used alone""" + raise ValueError(msg % path_glob) + if _CHECK_MISMATCH_SET.search(path_glob): + msg = """invalid glob %r: mismatching set marker '{' or '}'""" + raise ValueError(msg % path_glob) + return _iglob(path_glob) + + +def _iglob(path_glob): + rich_path_glob = RICH_GLOB.split(path_glob, 1) + if len(rich_path_glob) > 1: + assert len(rich_path_glob) == 3, rich_path_glob + prefix, set, suffix = rich_path_glob + for item in set.split(','): + for path in _iglob(''.join((prefix, item, suffix))): + yield path + else: + if '**' not in path_glob: + for item in std_iglob(path_glob): + yield item + else: + prefix, radical = path_glob.split('**', 1) + if prefix == '': + prefix = '.' + if radical == '': + radical = '*' + else: + # we support both + radical = radical.lstrip('/') + radical = radical.lstrip('\\') + for path, dir, files in os.walk(prefix): + path = os.path.normpath(path) + for fn in _iglob(os.path.join(path, radical)): + yield fn + + + +# +# HTTPSConnection which verifies certificates/matches domains +# + +class HTTPSConnection(httplib.HTTPSConnection): + ca_certs = None # set this to the path to the certs file (.pem) + check_domain = True # only used if ca_certs is not None + + # noinspection PyPropertyAccess + def connect(self): + sock = socket.create_connection((self.host, self.port), self.timeout) + if getattr(self, '_tunnel_host', False): + self.sock = sock + self._tunnel() + + if not hasattr(ssl, 'SSLContext'): + # For 2.x + if self.ca_certs: + cert_reqs = ssl.CERT_REQUIRED + else: + cert_reqs = ssl.CERT_NONE + self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, + cert_reqs=cert_reqs, + ssl_version=ssl.PROTOCOL_SSLv23, + ca_certs=self.ca_certs) + else: + context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + context.options |= ssl.OP_NO_SSLv2 + if self.cert_file: + context.load_cert_chain(self.cert_file, self.key_file) + kwargs = {} + if self.ca_certs: + context.verify_mode = ssl.CERT_REQUIRED + context.load_verify_locations(cafile=self.ca_certs) + if getattr(ssl, 'HAS_SNI', False): + kwargs['server_hostname'] = self.host + self.sock = context.wrap_socket(sock, **kwargs) + if self.ca_certs and self.check_domain: + try: + match_hostname(self.sock.getpeercert(), self.host) + logger.debug('Host verified: %s', self.host) + except CertificateError: + self.sock.shutdown(socket.SHUT_RDWR) + self.sock.close() + raise + +class HTTPSHandler(BaseHTTPSHandler): + def __init__(self, ca_certs, check_domain=True): + BaseHTTPSHandler.__init__(self) + self.ca_certs = ca_certs + self.check_domain = check_domain + + def _conn_maker(self, *args, **kwargs): + """ + This is called to create a connection instance. Normally you'd + pass a connection class to do_open, but it doesn't actually check for + a class, and just expects a callable. As long as we behave just as a + constructor would have, we should be OK. If it ever changes so that + we *must* pass a class, we'll create an UnsafeHTTPSConnection class + which just sets check_domain to False in the class definition, and + choose which one to pass to do_open. + """ + result = HTTPSConnection(*args, **kwargs) + if self.ca_certs: + result.ca_certs = self.ca_certs + result.check_domain = self.check_domain + return result + + def https_open(self, req): + try: + return self.do_open(self._conn_maker, req) + except URLError as e: + if 'certificate verify failed' in str(e.reason): + raise CertificateError('Unable to verify server certificate ' + 'for %s' % req.host) + else: + raise + +# +# To prevent against mixing HTTP traffic with HTTPS (examples: A Man-In-The- +# Middle proxy using HTTP listens on port 443, or an index mistakenly serves +# HTML containing a http://xyz link when it should be https://xyz), +# you can use the following handler class, which does not allow HTTP traffic. +# +# It works by inheriting from HTTPHandler - so build_opener won't add a +# handler for HTTP itself. +# +class HTTPSOnlyHandler(HTTPSHandler, HTTPHandler): + def http_open(self, req): + raise URLError('Unexpected HTTP request on what should be a secure ' + 'connection: %s' % req) + +# +# XML-RPC with timeouts +# + +_ver_info = sys.version_info[:2] + +if _ver_info == (2, 6): + class HTTP(httplib.HTTP): + def __init__(self, host='', port=None, **kwargs): + if port == 0: # 0 means use port 0, not the default port + port = None + self._setup(self._connection_class(host, port, **kwargs)) + + + class HTTPS(httplib.HTTPS): + def __init__(self, host='', port=None, **kwargs): + if port == 0: # 0 means use port 0, not the default port + port = None + self._setup(self._connection_class(host, port, **kwargs)) + + +class Transport(xmlrpclib.Transport): + def __init__(self, timeout, use_datetime=0): + self.timeout = timeout + xmlrpclib.Transport.__init__(self, use_datetime) + + def make_connection(self, host): + h, eh, x509 = self.get_host_info(host) + if _ver_info == (2, 6): + result = HTTP(h, timeout=self.timeout) + else: + if not self._connection or host != self._connection[0]: + self._extra_headers = eh + self._connection = host, httplib.HTTPConnection(h) + result = self._connection[1] + return result + +class SafeTransport(xmlrpclib.SafeTransport): + def __init__(self, timeout, use_datetime=0): + self.timeout = timeout + xmlrpclib.SafeTransport.__init__(self, use_datetime) + + def make_connection(self, host): + h, eh, kwargs = self.get_host_info(host) + if not kwargs: + kwargs = {} + kwargs['timeout'] = self.timeout + if _ver_info == (2, 6): + result = HTTPS(host, None, **kwargs) + else: + if not self._connection or host != self._connection[0]: + self._extra_headers = eh + self._connection = host, httplib.HTTPSConnection(h, None, + **kwargs) + result = self._connection[1] + return result + + +class ServerProxy(xmlrpclib.ServerProxy): + def __init__(self, uri, **kwargs): + self.timeout = timeout = kwargs.pop('timeout', None) + # The above classes only come into play if a timeout + # is specified + if timeout is not None: + scheme, _ = splittype(uri) + use_datetime = kwargs.get('use_datetime', 0) + if scheme == 'https': + tcls = SafeTransport + else: + tcls = Transport + kwargs['transport'] = t = tcls(timeout, use_datetime=use_datetime) + self.transport = t + xmlrpclib.ServerProxy.__init__(self, uri, **kwargs) + +# +# CSV functionality. This is provided because on 2.x, the csv module can't +# handle Unicode. However, we need to deal with Unicode in e.g. RECORD files. +# + +def _csv_open(fn, mode, **kwargs): + if sys.version_info[0] < 3: + mode += 'b' + else: + kwargs['newline'] = '' + return open(fn, mode, **kwargs) + + +class CSVBase(object): + defaults = { + 'delimiter': str(','), # The strs are used because we need native + 'quotechar': str('"'), # str in the csv API (2.x won't take + 'lineterminator': str('\n') # Unicode) + } + + def __enter__(self): + return self + + def __exit__(self, *exc_info): + self.stream.close() + + +class CSVReader(CSVBase): + def __init__(self, **kwargs): + if 'stream' in kwargs: + stream = kwargs['stream'] + if sys.version_info[0] >= 3: + # needs to be a text stream + stream = codecs.getreader('utf-8')(stream) + self.stream = stream + else: + self.stream = _csv_open(kwargs['path'], 'r') + self.reader = csv.reader(self.stream, **self.defaults) + + def __iter__(self): + return self + + def next(self): + result = next(self.reader) + if sys.version_info[0] < 3: + for i, item in enumerate(result): + if not isinstance(item, text_type): + result[i] = item.decode('utf-8') + return result + + __next__ = next + +class CSVWriter(CSVBase): + def __init__(self, fn, **kwargs): + self.stream = _csv_open(fn, 'w') + self.writer = csv.writer(self.stream, **self.defaults) + + def writerow(self, row): + if sys.version_info[0] < 3: + r = [] + for item in row: + if isinstance(item, text_type): + item = item.encode('utf-8') + r.append(item) + row = r + self.writer.writerow(row) + +# +# Configurator functionality +# + +class Configurator(BaseConfigurator): + + value_converters = dict(BaseConfigurator.value_converters) + value_converters['inc'] = 'inc_convert' + + def __init__(self, config, base=None): + super(Configurator, self).__init__(config) + self.base = base or os.getcwd() + + def configure_custom(self, config): + def convert(o): + if isinstance(o, (list, tuple)): + result = type(o)([convert(i) for i in o]) + elif isinstance(o, dict): + if '()' in o: + result = self.configure_custom(o) + else: + result = {} + for k in o: + result[k] = convert(o[k]) + else: + result = self.convert(o) + return result + + c = config.pop('()') + if not callable(c): + c = self.resolve(c) + props = config.pop('.', None) + # Check for valid identifiers + args = config.pop('[]', ()) + if args: + args = tuple([convert(o) for o in args]) + items = [(k, convert(config[k])) for k in config if valid_ident(k)] + kwargs = dict(items) + result = c(*args, **kwargs) + if props: + for n, v in props.items(): + setattr(result, n, convert(v)) + return result + + def __getitem__(self, key): + result = self.config[key] + if isinstance(result, dict) and '()' in result: + self.config[key] = result = self.configure_custom(result) + return result + + def inc_convert(self, value): + """Default converter for the inc:// protocol.""" + if not os.path.isabs(value): + value = os.path.join(self.base, value) + with codecs.open(value, 'r', encoding='utf-8') as f: + result = json.load(f) + return result + +# +# Mixin for running subprocesses and capturing their output +# + +class SubprocessMixin(object): + def __init__(self, verbose=False, progress=None): + self.verbose = verbose + self.progress = progress + + def reader(self, stream, context): + """ + Read lines from a subprocess' output stream and either pass to a progress + callable (if specified) or write progress information to sys.stderr. + """ + progress = self.progress + verbose = self.verbose + while True: + s = stream.readline() + if not s: + break + if progress is not None: + progress(s, context) + else: + if not verbose: + sys.stderr.write('.') + else: + sys.stderr.write(s.decode('utf-8')) + sys.stderr.flush() + stream.close() + + def run_command(self, cmd, **kwargs): + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, **kwargs) + t1 = threading.Thread(target=self.reader, args=(p.stdout, 'stdout')) + t1.start() + t2 = threading.Thread(target=self.reader, args=(p.stderr, 'stderr')) + t2.start() + p.wait() + t1.join() + t2.join() + if self.progress is not None: + self.progress('done.', 'main') + elif self.verbose: + sys.stderr.write('done.\n') + return p diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/version.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/version.py new file mode 100644 index 0000000..a219227 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/version.py @@ -0,0 +1,698 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2013 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +""" +Implementation of a flexible versioning scheme providing support for PEP-386, +distribute-compatible and semantic versioning. +""" + +import logging +import re + +from .compat import string_types + +__all__ = ['NormalizedVersion', 'NormalizedMatcher', + 'LegacyVersion', 'LegacyMatcher', + 'SemanticVersion', 'SemanticMatcher', + 'UnsupportedVersionError', 'get_scheme'] + +logger = logging.getLogger(__name__) + + +class UnsupportedVersionError(ValueError): + """This is an unsupported version.""" + pass + + +class Version(object): + def __init__(self, s): + self._string = s = s.strip() + self._parts = parts = self.parse(s) + assert isinstance(parts, tuple) + assert len(parts) > 0 + + def parse(self, s): + raise NotImplementedError('please implement in a subclass') + + def _check_compatible(self, other): + if type(self) != type(other): + raise TypeError('cannot compare %r and %r' % (self, other)) + + def __eq__(self, other): + self._check_compatible(other) + return self._parts == other._parts + + def __ne__(self, other): + return not self.__eq__(other) + + def __lt__(self, other): + self._check_compatible(other) + return self._parts < other._parts + + def __gt__(self, other): + return not (self.__lt__(other) or self.__eq__(other)) + + def __le__(self, other): + return self.__lt__(other) or self.__eq__(other) + + def __ge__(self, other): + return self.__gt__(other) or self.__eq__(other) + + # See http://docs.python.org/reference/datamodel#object.__hash__ + def __hash__(self): + return hash(self._parts) + + def __repr__(self): + return "%s('%s')" % (self.__class__.__name__, self._string) + + def __str__(self): + return self._string + + @property + def is_prerelease(self): + raise NotImplementedError('Please implement in subclasses.') + + +class Matcher(object): + version_class = None + + dist_re = re.compile(r"^(\w[\s\w'.-]*)(\((.*)\))?") + comp_re = re.compile(r'^(<=|>=|<|>|!=|==|~=)?\s*([^\s,]+)$') + num_re = re.compile(r'^\d+(\.\d+)*$') + + # value is either a callable or the name of a method + _operators = { + '<': lambda v, c, p: v < c, + '>': lambda v, c, p: v > c, + '<=': lambda v, c, p: v == c or v < c, + '>=': lambda v, c, p: v == c or v > c, + '==': lambda v, c, p: v == c, + # by default, compatible => >=. + '~=': lambda v, c, p: v == c or v > c, + '!=': lambda v, c, p: v != c, + } + + def __init__(self, s): + if self.version_class is None: + raise ValueError('Please specify a version class') + self._string = s = s.strip() + m = self.dist_re.match(s) + if not m: + raise ValueError('Not valid: %r' % s) + groups = m.groups('') + self.name = groups[0].strip() + self.key = self.name.lower() # for case-insensitive comparisons + clist = [] + if groups[2]: + constraints = [c.strip() for c in groups[2].split(',')] + for c in constraints: + m = self.comp_re.match(c) + if not m: + raise ValueError('Invalid %r in %r' % (c, s)) + groups = m.groups() + op = groups[0] or '~=' + s = groups[1] + if s.endswith('.*'): + if op not in ('==', '!='): + raise ValueError('\'.*\' not allowed for ' + '%r constraints' % op) + # Could be a partial version (e.g. for '2.*') which + # won't parse as a version, so keep it as a string + vn, prefix = s[:-2], True + if not self.num_re.match(vn): + # Just to check that vn is a valid version + self.version_class(vn) + else: + # Should parse as a version, so we can create an + # instance for the comparison + vn, prefix = self.version_class(s), False + clist.append((op, vn, prefix)) + self._parts = tuple(clist) + + def match(self, version): + """ + Check if the provided version matches the constraints. + + :param version: The version to match against this instance. + :type version: Strring or :class:`Version` instance. + """ + if isinstance(version, string_types): + version = self.version_class(version) + for operator, constraint, prefix in self._parts: + f = self._operators.get(operator) + if isinstance(f, string_types): + f = getattr(self, f) + if not f: + msg = ('%r not implemented ' + 'for %s' % (operator, self.__class__.__name__)) + raise NotImplementedError(msg) + if not f(version, constraint, prefix): + return False + return True + + @property + def exact_version(self): + result = None + if len(self._parts) == 1 and self._parts[0][0] == '==': + result = self._parts[0][1] + return result + + def _check_compatible(self, other): + if type(self) != type(other) or self.name != other.name: + raise TypeError('cannot compare %s and %s' % (self, other)) + + def __eq__(self, other): + self._check_compatible(other) + return self.key == other.key and self._parts == other._parts + + def __ne__(self, other): + return not self.__eq__(other) + + # See http://docs.python.org/reference/datamodel#object.__hash__ + def __hash__(self): + return hash(self.key) + hash(self._parts) + + def __repr__(self): + return "%s(%r)" % (self.__class__.__name__, self._string) + + def __str__(self): + return self._string + + +PEP426_VERSION_RE = re.compile(r'^(\d+\.\d+(\.\d+)*)((a|b|c|rc)(\d+))?' + r'(\.(post)(\d+))?(\.(dev)(\d+))?' + r'(-(\d+(\.\d+)?))?$') + + +def _pep426_key(s): + s = s.strip() + m = PEP426_VERSION_RE.match(s) + if not m: + raise UnsupportedVersionError('Not a valid version: %s' % s) + groups = m.groups() + nums = tuple(int(v) for v in groups[0].split('.')) + while len(nums) > 1 and nums[-1] == 0: + nums = nums[:-1] + + pre = groups[3:5] + post = groups[6:8] + dev = groups[9:11] + local = groups[12] + if pre == (None, None): + pre = () + else: + pre = pre[0], int(pre[1]) + if post == (None, None): + post = () + else: + post = post[0], int(post[1]) + if dev == (None, None): + dev = () + else: + dev = dev[0], int(dev[1]) + if local is None: + local = () + else: + local = tuple([int(s) for s in local.split('.')]) + if not pre: + # either before pre-release, or final release and after + if not post and dev: + # before pre-release + pre = ('a', -1) # to sort before a0 + else: + pre = ('z',) # to sort after all pre-releases + # now look at the state of post and dev. + if not post: + post = ('_',) # sort before 'a' + if not dev: + dev = ('final',) + + #print('%s -> %s' % (s, m.groups())) + return nums, pre, post, dev, local + + +_normalized_key = _pep426_key + + +class NormalizedVersion(Version): + """A rational version. + + Good: + 1.2 # equivalent to "1.2.0" + 1.2.0 + 1.2a1 + 1.2.3a2 + 1.2.3b1 + 1.2.3c1 + 1.2.3.4 + TODO: fill this out + + Bad: + 1 # mininum two numbers + 1.2a # release level must have a release serial + 1.2.3b + """ + def parse(self, s): + result = _normalized_key(s) + # _normalized_key loses trailing zeroes in the release + # clause, since that's needed to ensure that X.Y == X.Y.0 == X.Y.0.0 + # However, PEP 440 prefix matching needs it: for example, + # (~= 1.4.5.0) matches differently to (~= 1.4.5.0.0). + m = PEP426_VERSION_RE.match(s) # must succeed + groups = m.groups() + self._release_clause = tuple(int(v) for v in groups[0].split('.')) + return result + + PREREL_TAGS = set(['a', 'b', 'c', 'rc', 'dev']) + + @property + def is_prerelease(self): + return any(t[0] in self.PREREL_TAGS for t in self._parts if t) + + +def _match_prefix(x, y): + x = str(x) + y = str(y) + if x == y: + return True + if not x.startswith(y): + return False + n = len(y) + return x[n] == '.' + + +class NormalizedMatcher(Matcher): + version_class = NormalizedVersion + + # value is either a callable or the name of a method + _operators = { + '~=': '_match_compatible', + '<': '_match_lt', + '>': '_match_gt', + '<=': '_match_le', + '>=': '_match_ge', + '==': '_match_eq', + '!=': '_match_ne', + } + + def _match_lt(self, version, constraint, prefix): + if version >= constraint: + return False + release_clause = constraint._release_clause + pfx = '.'.join([str(i) for i in release_clause]) + return not _match_prefix(version, pfx) + + def _match_gt(self, version, constraint, prefix): + if version <= constraint: + return False + release_clause = constraint._release_clause + pfx = '.'.join([str(i) for i in release_clause]) + return not _match_prefix(version, pfx) + + def _match_le(self, version, constraint, prefix): + return version <= constraint + + def _match_ge(self, version, constraint, prefix): + return version >= constraint + + def _match_eq(self, version, constraint, prefix): + if not prefix: + result = (version == constraint) + else: + result = _match_prefix(version, constraint) + return result + + def _match_ne(self, version, constraint, prefix): + if not prefix: + result = (version != constraint) + else: + result = not _match_prefix(version, constraint) + return result + + def _match_compatible(self, version, constraint, prefix): + if version == constraint: + return True + if version < constraint: + return False + release_clause = constraint._release_clause + if len(release_clause) > 1: + release_clause = release_clause[:-1] + pfx = '.'.join([str(i) for i in release_clause]) + return _match_prefix(version, pfx) + +_REPLACEMENTS = ( + (re.compile('[.+-]$'), ''), # remove trailing puncts + (re.compile(r'^[.](\d)'), r'0.\1'), # .N -> 0.N at start + (re.compile('^[.-]'), ''), # remove leading puncts + (re.compile(r'^\((.*)\)$'), r'\1'), # remove parentheses + (re.compile(r'^v(ersion)?\s*(\d+)'), r'\2'), # remove leading v(ersion) + (re.compile(r'^r(ev)?\s*(\d+)'), r'\2'), # remove leading v(ersion) + (re.compile('[.]{2,}'), '.'), # multiple runs of '.' + (re.compile(r'\b(alfa|apha)\b'), 'alpha'), # misspelt alpha + (re.compile(r'\b(pre-alpha|prealpha)\b'), + 'pre.alpha'), # standardise + (re.compile(r'\(beta\)$'), 'beta'), # remove parentheses +) + +_SUFFIX_REPLACEMENTS = ( + (re.compile('^[:~._+-]+'), ''), # remove leading puncts + (re.compile('[,*")([\]]'), ''), # remove unwanted chars + (re.compile('[~:+_ -]'), '.'), # replace illegal chars + (re.compile('[.]{2,}'), '.'), # multiple runs of '.' + (re.compile(r'\.$'), ''), # trailing '.' +) + +_NUMERIC_PREFIX = re.compile(r'(\d+(\.\d+)*)') + + +def _suggest_semantic_version(s): + """ + Try to suggest a semantic form for a version for which + _suggest_normalized_version couldn't come up with anything. + """ + result = s.strip().lower() + for pat, repl in _REPLACEMENTS: + result = pat.sub(repl, result) + if not result: + result = '0.0.0' + + # Now look for numeric prefix, and separate it out from + # the rest. + #import pdb; pdb.set_trace() + m = _NUMERIC_PREFIX.match(result) + if not m: + prefix = '0.0.0' + suffix = result + else: + prefix = m.groups()[0].split('.') + prefix = [int(i) for i in prefix] + while len(prefix) < 3: + prefix.append(0) + if len(prefix) == 3: + suffix = result[m.end():] + else: + suffix = '.'.join([str(i) for i in prefix[3:]]) + result[m.end():] + prefix = prefix[:3] + prefix = '.'.join([str(i) for i in prefix]) + suffix = suffix.strip() + if suffix: + #import pdb; pdb.set_trace() + # massage the suffix. + for pat, repl in _SUFFIX_REPLACEMENTS: + suffix = pat.sub(repl, suffix) + + if not suffix: + result = prefix + else: + sep = '-' if 'dev' in suffix else '+' + result = prefix + sep + suffix + if not is_semver(result): + result = None + return result + + +def _suggest_normalized_version(s): + """Suggest a normalized version close to the given version string. + + If you have a version string that isn't rational (i.e. NormalizedVersion + doesn't like it) then you might be able to get an equivalent (or close) + rational version from this function. + + This does a number of simple normalizations to the given string, based + on observation of versions currently in use on PyPI. Given a dump of + those version during PyCon 2009, 4287 of them: + - 2312 (53.93%) match NormalizedVersion without change + with the automatic suggestion + - 3474 (81.04%) match when using this suggestion method + + @param s {str} An irrational version string. + @returns A rational version string, or None, if couldn't determine one. + """ + try: + _normalized_key(s) + return s # already rational + except UnsupportedVersionError: + pass + + rs = s.lower() + + # part of this could use maketrans + for orig, repl in (('-alpha', 'a'), ('-beta', 'b'), ('alpha', 'a'), + ('beta', 'b'), ('rc', 'c'), ('-final', ''), + ('-pre', 'c'), + ('-release', ''), ('.release', ''), ('-stable', ''), + ('+', '.'), ('_', '.'), (' ', ''), ('.final', ''), + ('final', '')): + rs = rs.replace(orig, repl) + + # if something ends with dev or pre, we add a 0 + rs = re.sub(r"pre$", r"pre0", rs) + rs = re.sub(r"dev$", r"dev0", rs) + + # if we have something like "b-2" or "a.2" at the end of the + # version, that is pobably beta, alpha, etc + # let's remove the dash or dot + rs = re.sub(r"([abc]|rc)[\-\.](\d+)$", r"\1\2", rs) + + # 1.0-dev-r371 -> 1.0.dev371 + # 0.1-dev-r79 -> 0.1.dev79 + rs = re.sub(r"[\-\.](dev)[\-\.]?r?(\d+)$", r".\1\2", rs) + + # Clean: 2.0.a.3, 2.0.b1, 0.9.0~c1 + rs = re.sub(r"[.~]?([abc])\.?", r"\1", rs) + + # Clean: v0.3, v1.0 + if rs.startswith('v'): + rs = rs[1:] + + # Clean leading '0's on numbers. + #TODO: unintended side-effect on, e.g., "2003.05.09" + # PyPI stats: 77 (~2%) better + rs = re.sub(r"\b0+(\d+)(?!\d)", r"\1", rs) + + # Clean a/b/c with no version. E.g. "1.0a" -> "1.0a0". Setuptools infers + # zero. + # PyPI stats: 245 (7.56%) better + rs = re.sub(r"(\d+[abc])$", r"\g<1>0", rs) + + # the 'dev-rNNN' tag is a dev tag + rs = re.sub(r"\.?(dev-r|dev\.r)\.?(\d+)$", r".dev\2", rs) + + # clean the - when used as a pre delimiter + rs = re.sub(r"-(a|b|c)(\d+)$", r"\1\2", rs) + + # a terminal "dev" or "devel" can be changed into ".dev0" + rs = re.sub(r"[\.\-](dev|devel)$", r".dev0", rs) + + # a terminal "dev" can be changed into ".dev0" + rs = re.sub(r"(?![\.\-])dev$", r".dev0", rs) + + # a terminal "final" or "stable" can be removed + rs = re.sub(r"(final|stable)$", "", rs) + + # The 'r' and the '-' tags are post release tags + # 0.4a1.r10 -> 0.4a1.post10 + # 0.9.33-17222 -> 0.9.33.post17222 + # 0.9.33-r17222 -> 0.9.33.post17222 + rs = re.sub(r"\.?(r|-|-r)\.?(\d+)$", r".post\2", rs) + + # Clean 'r' instead of 'dev' usage: + # 0.9.33+r17222 -> 0.9.33.dev17222 + # 1.0dev123 -> 1.0.dev123 + # 1.0.git123 -> 1.0.dev123 + # 1.0.bzr123 -> 1.0.dev123 + # 0.1a0dev.123 -> 0.1a0.dev123 + # PyPI stats: ~150 (~4%) better + rs = re.sub(r"\.?(dev|git|bzr)\.?(\d+)$", r".dev\2", rs) + + # Clean '.pre' (normalized from '-pre' above) instead of 'c' usage: + # 0.2.pre1 -> 0.2c1 + # 0.2-c1 -> 0.2c1 + # 1.0preview123 -> 1.0c123 + # PyPI stats: ~21 (0.62%) better + rs = re.sub(r"\.?(pre|preview|-c)(\d+)$", r"c\g<2>", rs) + + # Tcl/Tk uses "px" for their post release markers + rs = re.sub(r"p(\d+)$", r".post\1", rs) + + try: + _normalized_key(rs) + except UnsupportedVersionError: + rs = None + return rs + +# +# Legacy version processing (distribute-compatible) +# + +_VERSION_PART = re.compile(r'([a-z]+|\d+|[\.-])', re.I) +_VERSION_REPLACE = { + 'pre': 'c', + 'preview': 'c', + '-': 'final-', + 'rc': 'c', + 'dev': '@', + '': None, + '.': None, +} + + +def _legacy_key(s): + def get_parts(s): + result = [] + for p in _VERSION_PART.split(s.lower()): + p = _VERSION_REPLACE.get(p, p) + if p: + if '0' <= p[:1] <= '9': + p = p.zfill(8) + else: + p = '*' + p + result.append(p) + result.append('*final') + return result + + result = [] + for p in get_parts(s): + if p.startswith('*'): + if p < '*final': + while result and result[-1] == '*final-': + result.pop() + while result and result[-1] == '00000000': + result.pop() + result.append(p) + return tuple(result) + + +class LegacyVersion(Version): + def parse(self, s): + return _legacy_key(s) + + PREREL_TAGS = set( + ['*a', '*alpha', '*b', '*beta', '*c', '*rc', '*r', '*@', '*pre'] + ) + + @property + def is_prerelease(self): + return any(x in self.PREREL_TAGS for x in self._parts) + + +class LegacyMatcher(Matcher): + version_class = LegacyVersion + + _operators = dict(Matcher._operators) + _operators['~='] = '_match_compatible' + + numeric_re = re.compile('^(\d+(\.\d+)*)') + + def _match_compatible(self, version, constraint, prefix): + if version < constraint: + return False + m = self.numeric_re.match(str(constraint)) + if not m: + logger.warning('Cannot compute compatible match for version %s ' + ' and constraint %s', version, constraint) + return True + s = m.groups()[0] + if '.' in s: + s = s.rsplit('.', 1)[0] + return _match_prefix(version, s) + +# +# Semantic versioning +# + +_SEMVER_RE = re.compile(r'^(\d+)\.(\d+)\.(\d+)' + r'(-[a-z0-9]+(\.[a-z0-9-]+)*)?' + r'(\+[a-z0-9]+(\.[a-z0-9-]+)*)?$', re.I) + + +def is_semver(s): + return _SEMVER_RE.match(s) + + +def _semantic_key(s): + def make_tuple(s, absent): + if s is None: + result = (absent,) + else: + parts = s[1:].split('.') + # We can't compare ints and strings on Python 3, so fudge it + # by zero-filling numeric values so simulate a numeric comparison + result = tuple([p.zfill(8) if p.isdigit() else p for p in parts]) + return result + + m = is_semver(s) + if not m: + raise UnsupportedVersionError(s) + groups = m.groups() + major, minor, patch = [int(i) for i in groups[:3]] + # choose the '|' and '*' so that versions sort correctly + pre, build = make_tuple(groups[3], '|'), make_tuple(groups[5], '*') + return (major, minor, patch), pre, build + + +class SemanticVersion(Version): + def parse(self, s): + return _semantic_key(s) + + @property + def is_prerelease(self): + return self._parts[1][0] != '|' + + +class SemanticMatcher(Matcher): + version_class = SemanticVersion + + +class VersionScheme(object): + def __init__(self, key, matcher, suggester=None): + self.key = key + self.matcher = matcher + self.suggester = suggester + + def is_valid_version(self, s): + try: + self.matcher.version_class(s) + result = True + except UnsupportedVersionError: + result = False + return result + + def is_valid_matcher(self, s): + try: + self.matcher(s) + result = True + except UnsupportedVersionError: + result = False + return result + + def is_valid_constraint_list(self, s): + """ + Used for processing some metadata fields + """ + return self.is_valid_matcher('dummy_name (%s)' % s) + + def suggest(self, s): + if self.suggester is None: + result = None + else: + result = self.suggester(s) + return result + +_SCHEMES = { + 'normalized': VersionScheme(_normalized_key, NormalizedMatcher, + _suggest_normalized_version), + 'legacy': VersionScheme(_legacy_key, LegacyMatcher, lambda self, s: s), + 'semantic': VersionScheme(_semantic_key, SemanticMatcher, + _suggest_semantic_version), +} + +_SCHEMES['default'] = _SCHEMES['normalized'] + + +def get_scheme(name): + if name not in _SCHEMES: + raise ValueError('unknown scheme name: %r' % name) + return _SCHEMES[name] diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/w32.exe b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/w32.exe new file mode 100644 index 0000000..09e7635 Binary files /dev/null and b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/w32.exe differ diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/w64.exe b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/w64.exe new file mode 100644 index 0000000..29e44e1 Binary files /dev/null and b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/w64.exe differ diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/wheel.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/wheel.py new file mode 100644 index 0000000..6dbca81 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/distlib/wheel.py @@ -0,0 +1,723 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +from __future__ import unicode_literals + +import base64 +import codecs +import datetime +import distutils.util +from email import message_from_file +import hashlib +import imp +import json +import logging +import os +import posixpath +import re +import shutil +import sys +import tempfile +import zipfile + +from . import __version__, DistlibException +from .compat import sysconfig, ZipFile, fsdecode, text_type, filter +from .database import InstalledDistribution +from .metadata import Metadata, METADATA_FILENAME +from .util import (FileOperator, convert_path, CSVReader, CSVWriter, + cached_property, get_cache_base, read_exports) + + +logger = logging.getLogger(__name__) + + +if hasattr(sys, 'pypy_version_info'): + IMP_PREFIX = 'pp' +elif sys.platform.startswith('java'): + IMP_PREFIX = 'jy' +elif sys.platform == 'cli': + IMP_PREFIX = 'ip' +else: + IMP_PREFIX = 'cp' + +VER_SUFFIX = sysconfig.get_config_var('py_version_nodot') +if not VER_SUFFIX: # pragma: no cover + VER_SUFFIX = '%s%s' % sys.version_info[:2] +PYVER = 'py' + VER_SUFFIX +IMPVER = IMP_PREFIX + VER_SUFFIX + +ARCH = distutils.util.get_platform().replace('-', '_').replace('.', '_') + +ABI = sysconfig.get_config_var('SOABI') +if ABI and ABI.startswith('cpython-'): + ABI = ABI.replace('cpython-', 'cp') +else: + ABI = 'none' + +FILENAME_RE = re.compile(r''' +(?P[^-]+) +-(?P\d+[^-]*) +(-(?P\d+[^-]*))? +-(?P\w+\d+(\.\w+\d+)*) +-(?P\w+) +-(?P\w+) +\.whl$ +''', re.IGNORECASE | re.VERBOSE) + +NAME_VERSION_RE = re.compile(r''' +(?P[^-]+) +-(?P\d+[^-]*) +(-(?P\d+[^-]*))?$ +''', re.IGNORECASE | re.VERBOSE) + +SHEBANG_RE = re.compile(br'\s*#![^\r\n]*') + +if os.sep == '/': + to_posix = lambda o: o +else: + to_posix = lambda o: o.replace(os.sep, '/') + + +class Mounter(object): + def __init__(self): + self.impure_wheels = {} + self.libs = {} + + def add(self, pathname, extensions): + self.impure_wheels[pathname] = extensions + self.libs.update(extensions) + + def remove(self, pathname): + extensions = self.impure_wheels.pop(pathname) + for k, v in extensions: + if k in self.libs: + del self.libs[k] + + def find_module(self, fullname, path=None): + if fullname in self.libs: + result = self + else: + result = None + return result + + def load_module(self, fullname): + if fullname in sys.modules: + result = sys.modules[fullname] + else: + if fullname not in self.libs: + raise ImportError('unable to find extension for %s' % fullname) + result = imp.load_dynamic(fullname, self.libs[fullname]) + result.__loader__ = self + parts = fullname.rsplit('.', 1) + if len(parts) > 1: + result.__package__ = parts[0] + return result + +_hook = Mounter() + + +class Wheel(object): + """ + Class to build and install from Wheel files (PEP 427). + """ + + wheel_version = (1, 1) + hash_kind = 'sha256' + + def __init__(self, filename=None, sign=False, verify=False): + """ + Initialise an instance using a (valid) filename. + """ + self.sign = sign + self.verify = verify + self.buildver = '' + self.pyver = [PYVER] + self.abi = ['none'] + self.arch = ['any'] + self.dirname = os.getcwd() + if filename is None: + self.name = 'dummy' + self.version = '0.1' + self._filename = self.filename + else: + m = NAME_VERSION_RE.match(filename) + if m: + info = m.groupdict('') + self.name = info['nm'] + self.version = info['vn'] + self.buildver = info['bn'] + self._filename = self.filename + else: + dirname, filename = os.path.split(filename) + m = FILENAME_RE.match(filename) + if not m: + raise DistlibException('Invalid name or ' + 'filename: %r' % filename) + if dirname: + self.dirname = os.path.abspath(dirname) + self._filename = filename + info = m.groupdict('') + self.name = info['nm'] + self.version = info['vn'] + self.buildver = info['bn'] + self.pyver = info['py'].split('.') + self.abi = info['bi'].split('.') + self.arch = info['ar'].split('.') + + @property + def filename(self): + """ + Build and return a filename from the various components. + """ + if self.buildver: + buildver = '-' + self.buildver + else: + buildver = '' + pyver = '.'.join(self.pyver) + abi = '.'.join(self.abi) + arch = '.'.join(self.arch) + return '%s-%s%s-%s-%s-%s.whl' % (self.name, self.version, buildver, + pyver, abi, arch) + + @property + def tags(self): + for pyver in self.pyver: + for abi in self.abi: + for arch in self.arch: + yield pyver, abi, arch + + @cached_property + def metadata(self): + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + info_dir = '%s.dist-info' % name_ver + wrapper = codecs.getreader('utf-8') + metadata_filename = posixpath.join(info_dir, METADATA_FILENAME) + with ZipFile(pathname, 'r') as zf: + try: + with zf.open(metadata_filename) as bf: + wf = wrapper(bf) + result = Metadata(fileobj=wf) + except KeyError: + raise ValueError('Invalid wheel, because %s is ' + 'missing' % METADATA_FILENAME) + return result + + @cached_property + def info(self): + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + info_dir = '%s.dist-info' % name_ver + metadata_filename = posixpath.join(info_dir, 'WHEEL') + wrapper = codecs.getreader('utf-8') + with ZipFile(pathname, 'r') as zf: + with zf.open(metadata_filename) as bf: + wf = wrapper(bf) + message = message_from_file(wf) + result = dict(message) + return result + + def process_shebang(self, data): + m = SHEBANG_RE.match(data) + if m: + data = b'#!python' + data[m.end():] + else: + cr = data.find(b'\r') + lf = data.find(b'\n') + if cr < 0 or cr > lf: + term = b'\n' + else: + if data[cr:cr + 2] == b'\r\n': + term = b'\r\n' + else: + term = b'\r' + data = b'#!python' + term + data + return data + + def get_hash(self, data, hash_kind=None): + if hash_kind is None: + hash_kind = self.hash_kind + try: + hasher = getattr(hashlib, hash_kind) + except AttributeError: + raise DistlibException('Unsupported hash algorithm: %r' % hash_kind) + result = hasher(data).digest() + result = base64.urlsafe_b64encode(result).rstrip(b'=').decode('ascii') + return hash_kind, result + + def write_record(self, records, record_path, base): + with CSVWriter(record_path) as writer: + for row in records: + writer.writerow(row) + p = to_posix(os.path.relpath(record_path, base)) + writer.writerow((p, '', '')) + + def build(self, paths, tags=None, wheel_version=None): + """ + Build a wheel from files in specified paths, and use any specified tags + when determining the name of the wheel. + """ + if tags is None: + tags = {} + + libkey = list(filter(lambda o: o in paths, ('purelib', 'platlib')))[0] + if libkey == 'platlib': + is_pure = 'false' + default_pyver = [IMPVER] + default_abi = [ABI] + default_arch = [ARCH] + else: + is_pure = 'true' + default_pyver = [PYVER] + default_abi = ['none'] + default_arch = ['any'] + + self.pyver = tags.get('pyver', default_pyver) + self.abi = tags.get('abi', default_abi) + self.arch = tags.get('arch', default_arch) + + libdir = paths[libkey] + + name_ver = '%s-%s' % (self.name, self.version) + data_dir = '%s.data' % name_ver + info_dir = '%s.dist-info' % name_ver + + archive_paths = [] + + # First, stuff which is not in site-packages + for key in ('data', 'headers', 'scripts'): + if key not in paths: + continue + path = paths[key] + if os.path.isdir(path): + for root, dirs, files in os.walk(path): + for fn in files: + p = fsdecode(os.path.join(root, fn)) + rp = os.path.relpath(p, path) + ap = to_posix(os.path.join(data_dir, key, rp)) + archive_paths.append((ap, p)) + if key == 'scripts' and not p.endswith('.exe'): + with open(p, 'rb') as f: + data = f.read() + data = self.process_shebang(data) + with open(p, 'wb') as f: + f.write(data) + + # Now, stuff which is in site-packages, other than the + # distinfo stuff. + path = libdir + distinfo = None + for root, dirs, files in os.walk(path): + if root == path: + # At the top level only, save distinfo for later + # and skip it for now + for i, dn in enumerate(dirs): + dn = fsdecode(dn) + if dn.endswith('.dist-info'): + distinfo = os.path.join(root, dn) + del dirs[i] + break + assert distinfo, '.dist-info directory expected, not found' + + for fn in files: + # comment out next suite to leave .pyc files in + if fsdecode(fn).endswith(('.pyc', '.pyo')): + continue + p = os.path.join(root, fn) + rp = to_posix(os.path.relpath(p, path)) + archive_paths.append((rp, p)) + + # Now distinfo. Assumed to be flat, i.e. os.listdir is enough. + files = os.listdir(distinfo) + for fn in files: + if fn not in ('RECORD', 'INSTALLER', 'SHARED'): + p = fsdecode(os.path.join(distinfo, fn)) + ap = to_posix(os.path.join(info_dir, fn)) + archive_paths.append((ap, p)) + + wheel_metadata = [ + 'Wheel-Version: %d.%d' % (wheel_version or self.wheel_version), + 'Generator: distlib %s' % __version__, + 'Root-Is-Purelib: %s' % is_pure, + ] + for pyver, abi, arch in self.tags: + wheel_metadata.append('Tag: %s-%s-%s' % (pyver, abi, arch)) + p = os.path.join(distinfo, 'WHEEL') + with open(p, 'w') as f: + f.write('\n'.join(wheel_metadata)) + ap = to_posix(os.path.join(info_dir, 'WHEEL')) + archive_paths.append((ap, p)) + + # Now, at last, RECORD. + # Paths in here are archive paths - nothing else makes sense. + records = [] + hasher = getattr(hashlib, self.hash_kind) + for ap, p in archive_paths: + with open(p, 'rb') as f: + data = f.read() + digest = '%s=%s' % self.get_hash(data) + size = os.path.getsize(p) + records.append((ap, digest, size)) + + p = os.path.join(distinfo, 'RECORD') + self.write_record(records, p, libdir) + ap = to_posix(os.path.join(info_dir, 'RECORD')) + archive_paths.append((ap, p)) + # Now, ready to build the zip file + pathname = os.path.join(self.dirname, self.filename) + with ZipFile(pathname, 'w', zipfile.ZIP_DEFLATED) as zf: + for ap, p in archive_paths: + logger.debug('Wrote %s to %s in wheel', p, ap) + zf.write(p, ap) + return pathname + + def install(self, paths, maker, **kwargs): + """ + Install a wheel to the specified paths. If kwarg ``warner`` is + specified, it should be a callable, which will be called with two + tuples indicating the wheel version of this software and the wheel + version in the file, if there is a discrepancy in the versions. + This can be used to issue any warnings to raise any exceptions. + If kwarg ``lib_only`` is True, only the purelib/platlib files are + installed, and the headers, scripts, data and dist-info metadata are + not written. + + The return value is a :class:`InstalledDistribution` instance unless + ``options.lib_only`` is True, in which case the return value is ``None``. + """ + + dry_run = maker.dry_run + warner = kwargs.get('warner') + lib_only = kwargs.get('lib_only', False) + + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + data_dir = '%s.data' % name_ver + info_dir = '%s.dist-info' % name_ver + + metadata_name = posixpath.join(info_dir, METADATA_FILENAME) + wheel_metadata_name = posixpath.join(info_dir, 'WHEEL') + record_name = posixpath.join(info_dir, 'RECORD') + + wrapper = codecs.getreader('utf-8') + + with ZipFile(pathname, 'r') as zf: + with zf.open(wheel_metadata_name) as bwf: + wf = wrapper(bwf) + message = message_from_file(wf) + wv = message['Wheel-Version'].split('.', 1) + file_version = tuple([int(i) for i in wv]) + if (file_version != self.wheel_version) and warner: + warner(self.wheel_version, file_version) + + if message['Root-Is-Purelib'] == 'true': + libdir = paths['purelib'] + else: + libdir = paths['platlib'] + + records = {} + with zf.open(record_name) as bf: + with CSVReader(stream=bf) as reader: + for row in reader: + p = row[0] + records[p] = row + + data_pfx = posixpath.join(data_dir, '') + info_pfx = posixpath.join(info_dir, '') + script_pfx = posixpath.join(data_dir, 'scripts', '') + + # make a new instance rather than a copy of maker's, + # as we mutate it + fileop = FileOperator(dry_run=dry_run) + fileop.record = True # so we can rollback if needed + + bc = not sys.dont_write_bytecode # Double negatives. Lovely! + + outfiles = [] # for RECORD writing + + # for script copying/shebang processing + workdir = tempfile.mkdtemp() + # set target dir later + # we default add_launchers to False, as the + # Python Launcher should be used instead + maker.source_dir = workdir + maker.target_dir = None + try: + for zinfo in zf.infolist(): + arcname = zinfo.filename + if isinstance(arcname, text_type): + u_arcname = arcname + else: + u_arcname = arcname.decode('utf-8') + # The signature file won't be in RECORD, + # and we don't currently don't do anything with it + if u_arcname.endswith('/RECORD.jws'): + continue + row = records[u_arcname] + if row[2] and str(zinfo.file_size) != row[2]: + raise DistlibException('size mismatch for ' + '%s' % u_arcname) + if row[1]: + kind, value = row[1].split('=', 1) + with zf.open(arcname) as bf: + data = bf.read() + _, digest = self.get_hash(data, kind) + if digest != value: + raise DistlibException('digest mismatch for ' + '%s' % arcname) + + if lib_only and u_arcname.startswith((info_pfx, data_pfx)): + logger.debug('lib_only: skipping %s', u_arcname) + continue + is_script = (u_arcname.startswith(script_pfx) + and not u_arcname.endswith('.exe')) + + if u_arcname.startswith(data_pfx): + _, where, rp = u_arcname.split('/', 2) + outfile = os.path.join(paths[where], convert_path(rp)) + else: + # meant for site-packages. + if u_arcname in (wheel_metadata_name, record_name): + continue + outfile = os.path.join(libdir, convert_path(u_arcname)) + if not is_script: + with zf.open(arcname) as bf: + fileop.copy_stream(bf, outfile) + outfiles.append(outfile) + # Double check the digest of the written file + if not dry_run and row[1]: + with open(outfile, 'rb') as bf: + data = bf.read() + _, newdigest = self.get_hash(data, kind) + if newdigest != digest: + raise DistlibException('digest mismatch ' + 'on write for ' + '%s' % outfile) + if bc and outfile.endswith('.py'): + try: + pyc = fileop.byte_compile(outfile) + outfiles.append(pyc) + except Exception: + # Don't give up if byte-compilation fails, + # but log it and perhaps warn the user + logger.warning('Byte-compilation failed', + exc_info=True) + else: + fn = os.path.basename(convert_path(arcname)) + workname = os.path.join(workdir, fn) + with zf.open(arcname) as bf: + fileop.copy_stream(bf, workname) + + dn, fn = os.path.split(outfile) + maker.target_dir = dn + filenames = maker.make(fn) + fileop.set_executable_mode(filenames) + outfiles.extend(filenames) + + if lib_only: + logger.debug('lib_only: returning None') + dist = None + else: + # Generate scripts + + # Try to get pydist.json so we can see if there are + # any commands to generate. If this fails (e.g. because + # of a legacy wheel), log a warning but don't give up. + commands = None + file_version = self.info['Wheel-Version'] + if file_version == '1.0': + # Use legacy info + ep = posixpath.join(info_dir, 'entry_points.txt') + try: + with zf.open(ep) as bwf: + epdata = read_exports(bwf) + commands = {} + for key in ('console', 'gui'): + k = '%s_scripts' % key + if k in epdata: + commands['wrap_%s' % key] = d = {} + for v in epdata[k].values(): + s = '%s:%s' % (v.prefix, v.suffix) + if v.flags: + s += ' %s' % v.flags + d[v.name] = s + except Exception: + logger.warning('Unable to read legacy script ' + 'metadata, so cannot generate ' + 'scripts') + else: + try: + with zf.open(metadata_name) as bwf: + wf = wrapper(bwf) + commands = json.load(wf).get('commands') + except Exception: + logger.warning('Unable to read JSON metadata, so ' + 'cannot generate scripts') + if commands: + console_scripts = commands.get('wrap_console', {}) + gui_scripts = commands.get('wrap_gui', {}) + if console_scripts or gui_scripts: + script_dir = paths.get('scripts', '') + if not os.path.isdir(script_dir): + raise ValueError('Valid script path not ' + 'specified') + maker.target_dir = script_dir + for k, v in console_scripts.items(): + script = '%s = %s' % (k, v) + filenames = maker.make(script) + fileop.set_executable_mode(filenames) + + if gui_scripts: + options = {'gui': True } + for k, v in gui_scripts.items(): + script = '%s = %s' % (k, v) + filenames = maker.make(script, options) + fileop.set_executable_mode(filenames) + + p = os.path.join(libdir, info_dir) + dist = InstalledDistribution(p) + + # Write SHARED + paths = dict(paths) # don't change passed in dict + del paths['purelib'] + del paths['platlib'] + paths['lib'] = libdir + p = dist.write_shared_locations(paths, dry_run) + if p: + outfiles.append(p) + + # Write RECORD + dist.write_installed_files(outfiles, paths['prefix'], + dry_run) + return dist + except Exception: # pragma: no cover + logger.exception('installation failed.') + fileop.rollback() + raise + finally: + shutil.rmtree(workdir) + + def _get_dylib_cache(self): + # Use native string to avoid issues on 2.x: see Python #20140. + result = os.path.join(get_cache_base(), str('dylib-cache'), sys.version[:3]) + if not os.path.isdir(result): + os.makedirs(result) + return result + + def _get_extensions(self): + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + info_dir = '%s.dist-info' % name_ver + arcname = posixpath.join(info_dir, 'EXTENSIONS') + wrapper = codecs.getreader('utf-8') + result = [] + with ZipFile(pathname, 'r') as zf: + try: + with zf.open(arcname) as bf: + wf = wrapper(bf) + extensions = json.load(wf) + cache_base = self._get_dylib_cache() + for name, relpath in extensions.items(): + dest = os.path.join(cache_base, convert_path(relpath)) + if not os.path.exists(dest): + extract = True + else: + file_time = os.stat(dest).st_mtime + file_time = datetime.datetime.fromtimestamp(file_time) + info = zf.getinfo(relpath) + wheel_time = datetime.datetime(*info.date_time) + extract = wheel_time > file_time + if extract: + zf.extract(relpath, cache_base) + result.append((name, dest)) + except KeyError: + pass + return result + + def mount(self, append=False): + pathname = os.path.abspath(os.path.join(self.dirname, self.filename)) + if not is_compatible(self): + msg = 'Wheel %s not mountable in this Python.' % pathname + raise DistlibException(msg) + if pathname in sys.path: + logger.debug('%s already in path', pathname) + else: + if append: + sys.path.append(pathname) + else: + sys.path.insert(0, pathname) + extensions = self._get_extensions() + if extensions: + if _hook not in sys.meta_path: + sys.meta_path.append(_hook) + _hook.add(pathname, extensions) + + def unmount(self): + pathname = os.path.abspath(os.path.join(self.dirname, self.filename)) + if pathname not in sys.path: + logger.debug('%s not in path', pathname) + else: + sys.path.remove(pathname) + if pathname in _hook.impure_wheels: + _hook.remove(pathname) + if not _hook.impure_wheels: + if _hook in sys.meta_path: + sys.meta_path.remove(_hook) + + +def compatible_tags(): + """ + Return (pyver, abi, arch) tuples compatible with this Python. + """ + versions = [VER_SUFFIX] + major = VER_SUFFIX[0] + for minor in range(sys.version_info[1] - 1, - 1, -1): + versions.append(''.join([major, str(minor)])) + + abis = [] + for suffix, _, _ in imp.get_suffixes(): + if suffix.startswith('.abi'): + abis.append(suffix.split('.', 2)[1]) + abis.sort() + if ABI != 'none': + abis.insert(0, ABI) + abis.append('none') + result = [] + + # Most specific - our Python version, ABI and arch + for abi in abis: + result.append((''.join((IMP_PREFIX, versions[0])), abi, ARCH)) + + # where no ABI / arch dependency, but IMP_PREFIX dependency + for i, version in enumerate(versions): + result.append((''.join((IMP_PREFIX, version)), 'none', 'any')) + if i == 0: + result.append((''.join((IMP_PREFIX, version[0])), 'none', 'any')) + + # no IMP_PREFIX, ABI or arch dependency + for i, version in enumerate(versions): + result.append((''.join(('py', version)), 'none', 'any')) + if i == 0: + result.append((''.join(('py', version[0])), 'none', 'any')) + return result + + +COMPATIBLE_TAGS = compatible_tags() + +del compatible_tags + + +def is_compatible(wheel, tags=None): + if not isinstance(wheel, Wheel): + wheel = Wheel(wheel) # assume it's a filename + result = False + if tags is None: + tags = COMPATIBLE_TAGS + for ver, abi, arch in tags: + if ver in wheel.pyver and abi in wheel.abi and arch in wheel.arch: + result = True + break + return result diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/__init__.py new file mode 100644 index 0000000..10e2b74 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/__init__.py @@ -0,0 +1,23 @@ +""" +HTML parsing library based on the WHATWG "HTML5" +specification. The parser is designed to be compatible with existing +HTML found in the wild and implements well-defined error recovery that +is largely compatible with modern desktop web browsers. + +Example usage: + +import html5lib +f = open("my_document.html") +tree = html5lib.parse(f) +""" + +from __future__ import absolute_import, division, unicode_literals + +from .html5parser import HTMLParser, parse, parseFragment +from .treebuilders import getTreeBuilder +from .treewalkers import getTreeWalker +from .serializer import serialize + +__all__ = ["HTMLParser", "parse", "parseFragment", "getTreeBuilder", + "getTreeWalker", "serialize"] +__version__ = "1.0b1" diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/constants.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/constants.py new file mode 100644 index 0000000..1866dd7 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/constants.py @@ -0,0 +1,3086 @@ +from __future__ import absolute_import, division, unicode_literals + +import string +import gettext +_ = gettext.gettext + +EOF = None + +E = { + "null-character": + _("Null character in input stream, replaced with U+FFFD."), + "invalid-codepoint": + _("Invalid codepoint in stream."), + "incorrectly-placed-solidus": + _("Solidus (/) incorrectly placed in tag."), + "incorrect-cr-newline-entity": + _("Incorrect CR newline entity, replaced with LF."), + "illegal-windows-1252-entity": + _("Entity used with illegal number (windows-1252 reference)."), + "cant-convert-numeric-entity": + _("Numeric entity couldn't be converted to character " + "(codepoint U+%(charAsInt)08x)."), + "illegal-codepoint-for-numeric-entity": + _("Numeric entity represents an illegal codepoint: " + "U+%(charAsInt)08x."), + "numeric-entity-without-semicolon": + _("Numeric entity didn't end with ';'."), + "expected-numeric-entity-but-got-eof": + _("Numeric entity expected. Got end of file instead."), + "expected-numeric-entity": + _("Numeric entity expected but none found."), + "named-entity-without-semicolon": + _("Named entity didn't end with ';'."), + "expected-named-entity": + _("Named entity expected. Got none."), + "attributes-in-end-tag": + _("End tag contains unexpected attributes."), + 'self-closing-flag-on-end-tag': + _("End tag contains unexpected self-closing flag."), + "expected-tag-name-but-got-right-bracket": + _("Expected tag name. Got '>' instead."), + "expected-tag-name-but-got-question-mark": + _("Expected tag name. Got '?' instead. (HTML doesn't " + "support processing instructions.)"), + "expected-tag-name": + _("Expected tag name. Got something else instead"), + "expected-closing-tag-but-got-right-bracket": + _("Expected closing tag. Got '>' instead. Ignoring ''."), + "expected-closing-tag-but-got-eof": + _("Expected closing tag. Unexpected end of file."), + "expected-closing-tag-but-got-char": + _("Expected closing tag. Unexpected character '%(data)s' found."), + "eof-in-tag-name": + _("Unexpected end of file in the tag name."), + "expected-attribute-name-but-got-eof": + _("Unexpected end of file. Expected attribute name instead."), + "eof-in-attribute-name": + _("Unexpected end of file in attribute name."), + "invalid-character-in-attribute-name": + _("Invalid character in attribute name"), + "duplicate-attribute": + _("Dropped duplicate attribute on tag."), + "expected-end-of-tag-name-but-got-eof": + _("Unexpected end of file. Expected = or end of tag."), + "expected-attribute-value-but-got-eof": + _("Unexpected end of file. Expected attribute value."), + "expected-attribute-value-but-got-right-bracket": + _("Expected attribute value. Got '>' instead."), + 'equals-in-unquoted-attribute-value': + _("Unexpected = in unquoted attribute"), + 'unexpected-character-in-unquoted-attribute-value': + _("Unexpected character in unquoted attribute"), + "invalid-character-after-attribute-name": + _("Unexpected character after attribute name."), + "unexpected-character-after-attribute-value": + _("Unexpected character after attribute value."), + "eof-in-attribute-value-double-quote": + _("Unexpected end of file in attribute value (\")."), + "eof-in-attribute-value-single-quote": + _("Unexpected end of file in attribute value (')."), + "eof-in-attribute-value-no-quotes": + _("Unexpected end of file in attribute value."), + "unexpected-EOF-after-solidus-in-tag": + _("Unexpected end of file in tag. Expected >"), + "unexpected-character-after-solidus-in-tag": + _("Unexpected character after / in tag. Expected >"), + "expected-dashes-or-doctype": + _("Expected '--' or 'DOCTYPE'. Not found."), + "unexpected-bang-after-double-dash-in-comment": + _("Unexpected ! after -- in comment"), + "unexpected-space-after-double-dash-in-comment": + _("Unexpected space after -- in comment"), + "incorrect-comment": + _("Incorrect comment."), + "eof-in-comment": + _("Unexpected end of file in comment."), + "eof-in-comment-end-dash": + _("Unexpected end of file in comment (-)"), + "unexpected-dash-after-double-dash-in-comment": + _("Unexpected '-' after '--' found in comment."), + "eof-in-comment-double-dash": + _("Unexpected end of file in comment (--)."), + "eof-in-comment-end-space-state": + _("Unexpected end of file in comment."), + "eof-in-comment-end-bang-state": + _("Unexpected end of file in comment."), + "unexpected-char-in-comment": + _("Unexpected character in comment found."), + "need-space-after-doctype": + _("No space after literal string 'DOCTYPE'."), + "expected-doctype-name-but-got-right-bracket": + _("Unexpected > character. Expected DOCTYPE name."), + "expected-doctype-name-but-got-eof": + _("Unexpected end of file. Expected DOCTYPE name."), + "eof-in-doctype-name": + _("Unexpected end of file in DOCTYPE name."), + "eof-in-doctype": + _("Unexpected end of file in DOCTYPE."), + "expected-space-or-right-bracket-in-doctype": + _("Expected space or '>'. Got '%(data)s'"), + "unexpected-end-of-doctype": + _("Unexpected end of DOCTYPE."), + "unexpected-char-in-doctype": + _("Unexpected character in DOCTYPE."), + "eof-in-innerhtml": + _("XXX innerHTML EOF"), + "unexpected-doctype": + _("Unexpected DOCTYPE. Ignored."), + "non-html-root": + _("html needs to be the first start tag."), + "expected-doctype-but-got-eof": + _("Unexpected End of file. Expected DOCTYPE."), + "unknown-doctype": + _("Erroneous DOCTYPE."), + "expected-doctype-but-got-chars": + _("Unexpected non-space characters. Expected DOCTYPE."), + "expected-doctype-but-got-start-tag": + _("Unexpected start tag (%(name)s). Expected DOCTYPE."), + "expected-doctype-but-got-end-tag": + _("Unexpected end tag (%(name)s). Expected DOCTYPE."), + "end-tag-after-implied-root": + _("Unexpected end tag (%(name)s) after the (implied) root element."), + "expected-named-closing-tag-but-got-eof": + _("Unexpected end of file. Expected end tag (%(name)s)."), + "two-heads-are-not-better-than-one": + _("Unexpected start tag head in existing head. Ignored."), + "unexpected-end-tag": + _("Unexpected end tag (%(name)s). Ignored."), + "unexpected-start-tag-out-of-my-head": + _("Unexpected start tag (%(name)s) that can be in head. Moved."), + "unexpected-start-tag": + _("Unexpected start tag (%(name)s)."), + "missing-end-tag": + _("Missing end tag (%(name)s)."), + "missing-end-tags": + _("Missing end tags (%(name)s)."), + "unexpected-start-tag-implies-end-tag": + _("Unexpected start tag (%(startName)s) " + "implies end tag (%(endName)s)."), + "unexpected-start-tag-treated-as": + _("Unexpected start tag (%(originalName)s). Treated as %(newName)s."), + "deprecated-tag": + _("Unexpected start tag %(name)s. Don't use it!"), + "unexpected-start-tag-ignored": + _("Unexpected start tag %(name)s. Ignored."), + "expected-one-end-tag-but-got-another": + _("Unexpected end tag (%(gotName)s). " + "Missing end tag (%(expectedName)s)."), + "end-tag-too-early": + _("End tag (%(name)s) seen too early. Expected other end tag."), + "end-tag-too-early-named": + _("Unexpected end tag (%(gotName)s). Expected end tag (%(expectedName)s)."), + "end-tag-too-early-ignored": + _("End tag (%(name)s) seen too early. Ignored."), + "adoption-agency-1.1": + _("End tag (%(name)s) violates step 1, " + "paragraph 1 of the adoption agency algorithm."), + "adoption-agency-1.2": + _("End tag (%(name)s) violates step 1, " + "paragraph 2 of the adoption agency algorithm."), + "adoption-agency-1.3": + _("End tag (%(name)s) violates step 1, " + "paragraph 3 of the adoption agency algorithm."), + "adoption-agency-4.4": + _("End tag (%(name)s) violates step 4, " + "paragraph 4 of the adoption agency algorithm."), + "unexpected-end-tag-treated-as": + _("Unexpected end tag (%(originalName)s). Treated as %(newName)s."), + "no-end-tag": + _("This element (%(name)s) has no end tag."), + "unexpected-implied-end-tag-in-table": + _("Unexpected implied end tag (%(name)s) in the table phase."), + "unexpected-implied-end-tag-in-table-body": + _("Unexpected implied end tag (%(name)s) in the table body phase."), + "unexpected-char-implies-table-voodoo": + _("Unexpected non-space characters in " + "table context caused voodoo mode."), + "unexpected-hidden-input-in-table": + _("Unexpected input with type hidden in table context."), + "unexpected-form-in-table": + _("Unexpected form in table context."), + "unexpected-start-tag-implies-table-voodoo": + _("Unexpected start tag (%(name)s) in " + "table context caused voodoo mode."), + "unexpected-end-tag-implies-table-voodoo": + _("Unexpected end tag (%(name)s) in " + "table context caused voodoo mode."), + "unexpected-cell-in-table-body": + _("Unexpected table cell start tag (%(name)s) " + "in the table body phase."), + "unexpected-cell-end-tag": + _("Got table cell end tag (%(name)s) " + "while required end tags are missing."), + "unexpected-end-tag-in-table-body": + _("Unexpected end tag (%(name)s) in the table body phase. Ignored."), + "unexpected-implied-end-tag-in-table-row": + _("Unexpected implied end tag (%(name)s) in the table row phase."), + "unexpected-end-tag-in-table-row": + _("Unexpected end tag (%(name)s) in the table row phase. Ignored."), + "unexpected-select-in-select": + _("Unexpected select start tag in the select phase " + "treated as select end tag."), + "unexpected-input-in-select": + _("Unexpected input start tag in the select phase."), + "unexpected-start-tag-in-select": + _("Unexpected start tag token (%(name)s in the select phase. " + "Ignored."), + "unexpected-end-tag-in-select": + _("Unexpected end tag (%(name)s) in the select phase. Ignored."), + "unexpected-table-element-start-tag-in-select-in-table": + _("Unexpected table element start tag (%(name)s) in the select in table phase."), + "unexpected-table-element-end-tag-in-select-in-table": + _("Unexpected table element end tag (%(name)s) in the select in table phase."), + "unexpected-char-after-body": + _("Unexpected non-space characters in the after body phase."), + "unexpected-start-tag-after-body": + _("Unexpected start tag token (%(name)s)" + " in the after body phase."), + "unexpected-end-tag-after-body": + _("Unexpected end tag token (%(name)s)" + " in the after body phase."), + "unexpected-char-in-frameset": + _("Unexpected characters in the frameset phase. Characters ignored."), + "unexpected-start-tag-in-frameset": + _("Unexpected start tag token (%(name)s)" + " in the frameset phase. Ignored."), + "unexpected-frameset-in-frameset-innerhtml": + _("Unexpected end tag token (frameset) " + "in the frameset phase (innerHTML)."), + "unexpected-end-tag-in-frameset": + _("Unexpected end tag token (%(name)s)" + " in the frameset phase. Ignored."), + "unexpected-char-after-frameset": + _("Unexpected non-space characters in the " + "after frameset phase. Ignored."), + "unexpected-start-tag-after-frameset": + _("Unexpected start tag (%(name)s)" + " in the after frameset phase. Ignored."), + "unexpected-end-tag-after-frameset": + _("Unexpected end tag (%(name)s)" + " in the after frameset phase. Ignored."), + "unexpected-end-tag-after-body-innerhtml": + _("Unexpected end tag after body(innerHtml)"), + "expected-eof-but-got-char": + _("Unexpected non-space characters. Expected end of file."), + "expected-eof-but-got-start-tag": + _("Unexpected start tag (%(name)s)" + ". Expected end of file."), + "expected-eof-but-got-end-tag": + _("Unexpected end tag (%(name)s)" + ". Expected end of file."), + "eof-in-table": + _("Unexpected end of file. Expected table content."), + "eof-in-select": + _("Unexpected end of file. Expected select content."), + "eof-in-frameset": + _("Unexpected end of file. Expected frameset content."), + "eof-in-script-in-script": + _("Unexpected end of file. Expected script content."), + "eof-in-foreign-lands": + _("Unexpected end of file. Expected foreign content"), + "non-void-element-with-trailing-solidus": + _("Trailing solidus not allowed on element %(name)s"), + "unexpected-html-element-in-foreign-content": + _("Element %(name)s not allowed in a non-html context"), + "unexpected-end-tag-before-html": + _("Unexpected end tag (%(name)s) before html."), + "XXX-undefined-error": + _("Undefined error (this sucks and should be fixed)"), +} + +namespaces = { + "html": "http://www.w3.org/1999/xhtml", + "mathml": "http://www.w3.org/1998/Math/MathML", + "svg": "http://www.w3.org/2000/svg", + "xlink": "http://www.w3.org/1999/xlink", + "xml": "http://www.w3.org/XML/1998/namespace", + "xmlns": "http://www.w3.org/2000/xmlns/" +} + +scopingElements = frozenset(( + (namespaces["html"], "applet"), + (namespaces["html"], "caption"), + (namespaces["html"], "html"), + (namespaces["html"], "marquee"), + (namespaces["html"], "object"), + (namespaces["html"], "table"), + (namespaces["html"], "td"), + (namespaces["html"], "th"), + (namespaces["mathml"], "mi"), + (namespaces["mathml"], "mo"), + (namespaces["mathml"], "mn"), + (namespaces["mathml"], "ms"), + (namespaces["mathml"], "mtext"), + (namespaces["mathml"], "annotation-xml"), + (namespaces["svg"], "foreignObject"), + (namespaces["svg"], "desc"), + (namespaces["svg"], "title"), +)) + +formattingElements = frozenset(( + (namespaces["html"], "a"), + (namespaces["html"], "b"), + (namespaces["html"], "big"), + (namespaces["html"], "code"), + (namespaces["html"], "em"), + (namespaces["html"], "font"), + (namespaces["html"], "i"), + (namespaces["html"], "nobr"), + (namespaces["html"], "s"), + (namespaces["html"], "small"), + (namespaces["html"], "strike"), + (namespaces["html"], "strong"), + (namespaces["html"], "tt"), + (namespaces["html"], "u") +)) + +specialElements = frozenset(( + (namespaces["html"], "address"), + (namespaces["html"], "applet"), + (namespaces["html"], "area"), + (namespaces["html"], "article"), + (namespaces["html"], "aside"), + (namespaces["html"], "base"), + (namespaces["html"], "basefont"), + (namespaces["html"], "bgsound"), + (namespaces["html"], "blockquote"), + (namespaces["html"], "body"), + (namespaces["html"], "br"), + (namespaces["html"], "button"), + (namespaces["html"], "caption"), + (namespaces["html"], "center"), + (namespaces["html"], "col"), + (namespaces["html"], "colgroup"), + (namespaces["html"], "command"), + (namespaces["html"], "dd"), + (namespaces["html"], "details"), + (namespaces["html"], "dir"), + (namespaces["html"], "div"), + (namespaces["html"], "dl"), + (namespaces["html"], "dt"), + (namespaces["html"], "embed"), + (namespaces["html"], "fieldset"), + (namespaces["html"], "figure"), + (namespaces["html"], "footer"), + (namespaces["html"], "form"), + (namespaces["html"], "frame"), + (namespaces["html"], "frameset"), + (namespaces["html"], "h1"), + (namespaces["html"], "h2"), + (namespaces["html"], "h3"), + (namespaces["html"], "h4"), + (namespaces["html"], "h5"), + (namespaces["html"], "h6"), + (namespaces["html"], "head"), + (namespaces["html"], "header"), + (namespaces["html"], "hr"), + (namespaces["html"], "html"), + (namespaces["html"], "iframe"), + # Note that image is commented out in the spec as "this isn't an + # element that can end up on the stack, so it doesn't matter," + (namespaces["html"], "image"), + (namespaces["html"], "img"), + (namespaces["html"], "input"), + (namespaces["html"], "isindex"), + (namespaces["html"], "li"), + (namespaces["html"], "link"), + (namespaces["html"], "listing"), + (namespaces["html"], "marquee"), + (namespaces["html"], "menu"), + (namespaces["html"], "meta"), + (namespaces["html"], "nav"), + (namespaces["html"], "noembed"), + (namespaces["html"], "noframes"), + (namespaces["html"], "noscript"), + (namespaces["html"], "object"), + (namespaces["html"], "ol"), + (namespaces["html"], "p"), + (namespaces["html"], "param"), + (namespaces["html"], "plaintext"), + (namespaces["html"], "pre"), + (namespaces["html"], "script"), + (namespaces["html"], "section"), + (namespaces["html"], "select"), + (namespaces["html"], "style"), + (namespaces["html"], "table"), + (namespaces["html"], "tbody"), + (namespaces["html"], "td"), + (namespaces["html"], "textarea"), + (namespaces["html"], "tfoot"), + (namespaces["html"], "th"), + (namespaces["html"], "thead"), + (namespaces["html"], "title"), + (namespaces["html"], "tr"), + (namespaces["html"], "ul"), + (namespaces["html"], "wbr"), + (namespaces["html"], "xmp"), + (namespaces["svg"], "foreignObject") +)) + +htmlIntegrationPointElements = frozenset(( + (namespaces["mathml"], "annotaion-xml"), + (namespaces["svg"], "foreignObject"), + (namespaces["svg"], "desc"), + (namespaces["svg"], "title") +)) + +mathmlTextIntegrationPointElements = frozenset(( + (namespaces["mathml"], "mi"), + (namespaces["mathml"], "mo"), + (namespaces["mathml"], "mn"), + (namespaces["mathml"], "ms"), + (namespaces["mathml"], "mtext") +)) + +spaceCharacters = frozenset(( + "\t", + "\n", + "\u000C", + " ", + "\r" +)) + +tableInsertModeElements = frozenset(( + "table", + "tbody", + "tfoot", + "thead", + "tr" +)) + +asciiLowercase = frozenset(string.ascii_lowercase) +asciiUppercase = frozenset(string.ascii_uppercase) +asciiLetters = frozenset(string.ascii_letters) +digits = frozenset(string.digits) +hexDigits = frozenset(string.hexdigits) + +asciiUpper2Lower = dict([(ord(c), ord(c.lower())) + for c in string.ascii_uppercase]) + +# Heading elements need to be ordered +headingElements = ( + "h1", + "h2", + "h3", + "h4", + "h5", + "h6" +) + +voidElements = frozenset(( + "base", + "command", + "event-source", + "link", + "meta", + "hr", + "br", + "img", + "embed", + "param", + "area", + "col", + "input", + "source", + "track" +)) + +cdataElements = frozenset(('title', 'textarea')) + +rcdataElements = frozenset(( + 'style', + 'script', + 'xmp', + 'iframe', + 'noembed', + 'noframes', + 'noscript' +)) + +booleanAttributes = { + "": frozenset(("irrelevant",)), + "style": frozenset(("scoped",)), + "img": frozenset(("ismap",)), + "audio": frozenset(("autoplay", "controls")), + "video": frozenset(("autoplay", "controls")), + "script": frozenset(("defer", "async")), + "details": frozenset(("open",)), + "datagrid": frozenset(("multiple", "disabled")), + "command": frozenset(("hidden", "disabled", "checked", "default")), + "hr": frozenset(("noshade")), + "menu": frozenset(("autosubmit",)), + "fieldset": frozenset(("disabled", "readonly")), + "option": frozenset(("disabled", "readonly", "selected")), + "optgroup": frozenset(("disabled", "readonly")), + "button": frozenset(("disabled", "autofocus")), + "input": frozenset(("disabled", "readonly", "required", "autofocus", "checked", "ismap")), + "select": frozenset(("disabled", "readonly", "autofocus", "multiple")), + "output": frozenset(("disabled", "readonly")), +} + +# entitiesWindows1252 has to be _ordered_ and needs to have an index. It +# therefore can't be a frozenset. +entitiesWindows1252 = ( + 8364, # 0x80 0x20AC EURO SIGN + 65533, # 0x81 UNDEFINED + 8218, # 0x82 0x201A SINGLE LOW-9 QUOTATION MARK + 402, # 0x83 0x0192 LATIN SMALL LETTER F WITH HOOK + 8222, # 0x84 0x201E DOUBLE LOW-9 QUOTATION MARK + 8230, # 0x85 0x2026 HORIZONTAL ELLIPSIS + 8224, # 0x86 0x2020 DAGGER + 8225, # 0x87 0x2021 DOUBLE DAGGER + 710, # 0x88 0x02C6 MODIFIER LETTER CIRCUMFLEX ACCENT + 8240, # 0x89 0x2030 PER MILLE SIGN + 352, # 0x8A 0x0160 LATIN CAPITAL LETTER S WITH CARON + 8249, # 0x8B 0x2039 SINGLE LEFT-POINTING ANGLE QUOTATION MARK + 338, # 0x8C 0x0152 LATIN CAPITAL LIGATURE OE + 65533, # 0x8D UNDEFINED + 381, # 0x8E 0x017D LATIN CAPITAL LETTER Z WITH CARON + 65533, # 0x8F UNDEFINED + 65533, # 0x90 UNDEFINED + 8216, # 0x91 0x2018 LEFT SINGLE QUOTATION MARK + 8217, # 0x92 0x2019 RIGHT SINGLE QUOTATION MARK + 8220, # 0x93 0x201C LEFT DOUBLE QUOTATION MARK + 8221, # 0x94 0x201D RIGHT DOUBLE QUOTATION MARK + 8226, # 0x95 0x2022 BULLET + 8211, # 0x96 0x2013 EN DASH + 8212, # 0x97 0x2014 EM DASH + 732, # 0x98 0x02DC SMALL TILDE + 8482, # 0x99 0x2122 TRADE MARK SIGN + 353, # 0x9A 0x0161 LATIN SMALL LETTER S WITH CARON + 8250, # 0x9B 0x203A SINGLE RIGHT-POINTING ANGLE QUOTATION MARK + 339, # 0x9C 0x0153 LATIN SMALL LIGATURE OE + 65533, # 0x9D UNDEFINED + 382, # 0x9E 0x017E LATIN SMALL LETTER Z WITH CARON + 376 # 0x9F 0x0178 LATIN CAPITAL LETTER Y WITH DIAERESIS +) + +xmlEntities = frozenset(('lt;', 'gt;', 'amp;', 'apos;', 'quot;')) + +entities = { + "AElig": "\xc6", + "AElig;": "\xc6", + "AMP": "&", + "AMP;": "&", + "Aacute": "\xc1", + "Aacute;": "\xc1", + "Abreve;": "\u0102", + "Acirc": "\xc2", + "Acirc;": "\xc2", + "Acy;": "\u0410", + "Afr;": "\U0001d504", + "Agrave": "\xc0", + "Agrave;": "\xc0", + "Alpha;": "\u0391", + "Amacr;": "\u0100", + "And;": "\u2a53", + "Aogon;": "\u0104", + "Aopf;": "\U0001d538", + "ApplyFunction;": "\u2061", + "Aring": "\xc5", + "Aring;": "\xc5", + "Ascr;": "\U0001d49c", + "Assign;": "\u2254", + "Atilde": "\xc3", + "Atilde;": "\xc3", + "Auml": "\xc4", + "Auml;": "\xc4", + "Backslash;": "\u2216", + "Barv;": "\u2ae7", + "Barwed;": "\u2306", + "Bcy;": "\u0411", + "Because;": "\u2235", + "Bernoullis;": "\u212c", + "Beta;": "\u0392", + "Bfr;": "\U0001d505", + "Bopf;": "\U0001d539", + "Breve;": "\u02d8", + "Bscr;": "\u212c", + "Bumpeq;": "\u224e", + "CHcy;": "\u0427", + "COPY": "\xa9", + "COPY;": "\xa9", + "Cacute;": "\u0106", + "Cap;": "\u22d2", + "CapitalDifferentialD;": "\u2145", + "Cayleys;": "\u212d", + "Ccaron;": "\u010c", + "Ccedil": "\xc7", + "Ccedil;": "\xc7", + "Ccirc;": "\u0108", + "Cconint;": "\u2230", + "Cdot;": "\u010a", + "Cedilla;": "\xb8", + "CenterDot;": "\xb7", + "Cfr;": "\u212d", + "Chi;": "\u03a7", + "CircleDot;": "\u2299", + "CircleMinus;": "\u2296", + "CirclePlus;": "\u2295", + "CircleTimes;": "\u2297", + "ClockwiseContourIntegral;": "\u2232", + "CloseCurlyDoubleQuote;": "\u201d", + "CloseCurlyQuote;": "\u2019", + "Colon;": "\u2237", + "Colone;": "\u2a74", + "Congruent;": "\u2261", + "Conint;": "\u222f", + "ContourIntegral;": "\u222e", + "Copf;": "\u2102", + "Coproduct;": "\u2210", + "CounterClockwiseContourIntegral;": "\u2233", + "Cross;": "\u2a2f", + "Cscr;": "\U0001d49e", + "Cup;": "\u22d3", + "CupCap;": "\u224d", + "DD;": "\u2145", + "DDotrahd;": "\u2911", + "DJcy;": "\u0402", + "DScy;": "\u0405", + "DZcy;": "\u040f", + "Dagger;": "\u2021", + "Darr;": "\u21a1", + "Dashv;": "\u2ae4", + "Dcaron;": "\u010e", + "Dcy;": "\u0414", + "Del;": "\u2207", + "Delta;": "\u0394", + "Dfr;": "\U0001d507", + "DiacriticalAcute;": "\xb4", + "DiacriticalDot;": "\u02d9", + "DiacriticalDoubleAcute;": "\u02dd", + "DiacriticalGrave;": "`", + "DiacriticalTilde;": "\u02dc", + "Diamond;": "\u22c4", + "DifferentialD;": "\u2146", + "Dopf;": "\U0001d53b", + "Dot;": "\xa8", + "DotDot;": "\u20dc", + "DotEqual;": "\u2250", + "DoubleContourIntegral;": "\u222f", + "DoubleDot;": "\xa8", + "DoubleDownArrow;": "\u21d3", + "DoubleLeftArrow;": "\u21d0", + "DoubleLeftRightArrow;": "\u21d4", + "DoubleLeftTee;": "\u2ae4", + "DoubleLongLeftArrow;": "\u27f8", + "DoubleLongLeftRightArrow;": "\u27fa", + "DoubleLongRightArrow;": "\u27f9", + "DoubleRightArrow;": "\u21d2", + "DoubleRightTee;": "\u22a8", + "DoubleUpArrow;": "\u21d1", + "DoubleUpDownArrow;": "\u21d5", + "DoubleVerticalBar;": "\u2225", + "DownArrow;": "\u2193", + "DownArrowBar;": "\u2913", + "DownArrowUpArrow;": "\u21f5", + "DownBreve;": "\u0311", + "DownLeftRightVector;": "\u2950", + "DownLeftTeeVector;": "\u295e", + "DownLeftVector;": "\u21bd", + "DownLeftVectorBar;": "\u2956", + "DownRightTeeVector;": "\u295f", + "DownRightVector;": "\u21c1", + "DownRightVectorBar;": "\u2957", + "DownTee;": "\u22a4", + "DownTeeArrow;": "\u21a7", + "Downarrow;": "\u21d3", + "Dscr;": "\U0001d49f", + "Dstrok;": "\u0110", + "ENG;": "\u014a", + "ETH": "\xd0", + "ETH;": "\xd0", + "Eacute": "\xc9", + "Eacute;": "\xc9", + "Ecaron;": "\u011a", + "Ecirc": "\xca", + "Ecirc;": "\xca", + "Ecy;": "\u042d", + "Edot;": "\u0116", + "Efr;": "\U0001d508", + "Egrave": "\xc8", + "Egrave;": "\xc8", + "Element;": "\u2208", + "Emacr;": "\u0112", + "EmptySmallSquare;": "\u25fb", + "EmptyVerySmallSquare;": "\u25ab", + "Eogon;": "\u0118", + "Eopf;": "\U0001d53c", + "Epsilon;": "\u0395", + "Equal;": "\u2a75", + "EqualTilde;": "\u2242", + "Equilibrium;": "\u21cc", + "Escr;": "\u2130", + "Esim;": "\u2a73", + "Eta;": "\u0397", + "Euml": "\xcb", + "Euml;": "\xcb", + "Exists;": "\u2203", + "ExponentialE;": "\u2147", + "Fcy;": "\u0424", + "Ffr;": "\U0001d509", + "FilledSmallSquare;": "\u25fc", + "FilledVerySmallSquare;": "\u25aa", + "Fopf;": "\U0001d53d", + "ForAll;": "\u2200", + "Fouriertrf;": "\u2131", + "Fscr;": "\u2131", + "GJcy;": "\u0403", + "GT": ">", + "GT;": ">", + "Gamma;": "\u0393", + "Gammad;": "\u03dc", + "Gbreve;": "\u011e", + "Gcedil;": "\u0122", + "Gcirc;": "\u011c", + "Gcy;": "\u0413", + "Gdot;": "\u0120", + "Gfr;": "\U0001d50a", + "Gg;": "\u22d9", + "Gopf;": "\U0001d53e", + "GreaterEqual;": "\u2265", + "GreaterEqualLess;": "\u22db", + "GreaterFullEqual;": "\u2267", + "GreaterGreater;": "\u2aa2", + "GreaterLess;": "\u2277", + "GreaterSlantEqual;": "\u2a7e", + "GreaterTilde;": "\u2273", + "Gscr;": "\U0001d4a2", + "Gt;": "\u226b", + "HARDcy;": "\u042a", + "Hacek;": "\u02c7", + "Hat;": "^", + "Hcirc;": "\u0124", + "Hfr;": "\u210c", + "HilbertSpace;": "\u210b", + "Hopf;": "\u210d", + "HorizontalLine;": "\u2500", + "Hscr;": "\u210b", + "Hstrok;": "\u0126", + "HumpDownHump;": "\u224e", + "HumpEqual;": "\u224f", + "IEcy;": "\u0415", + "IJlig;": "\u0132", + "IOcy;": "\u0401", + "Iacute": "\xcd", + "Iacute;": "\xcd", + "Icirc": "\xce", + "Icirc;": "\xce", + "Icy;": "\u0418", + "Idot;": "\u0130", + "Ifr;": "\u2111", + "Igrave": "\xcc", + "Igrave;": "\xcc", + "Im;": "\u2111", + "Imacr;": "\u012a", + "ImaginaryI;": "\u2148", + "Implies;": "\u21d2", + "Int;": "\u222c", + "Integral;": "\u222b", + "Intersection;": "\u22c2", + "InvisibleComma;": "\u2063", + "InvisibleTimes;": "\u2062", + "Iogon;": "\u012e", + "Iopf;": "\U0001d540", + "Iota;": "\u0399", + "Iscr;": "\u2110", + "Itilde;": "\u0128", + "Iukcy;": "\u0406", + "Iuml": "\xcf", + "Iuml;": "\xcf", + "Jcirc;": "\u0134", + "Jcy;": "\u0419", + "Jfr;": "\U0001d50d", + "Jopf;": "\U0001d541", + "Jscr;": "\U0001d4a5", + "Jsercy;": "\u0408", + "Jukcy;": "\u0404", + "KHcy;": "\u0425", + "KJcy;": "\u040c", + "Kappa;": "\u039a", + "Kcedil;": "\u0136", + "Kcy;": "\u041a", + "Kfr;": "\U0001d50e", + "Kopf;": "\U0001d542", + "Kscr;": "\U0001d4a6", + "LJcy;": "\u0409", + "LT": "<", + "LT;": "<", + "Lacute;": "\u0139", + "Lambda;": "\u039b", + "Lang;": "\u27ea", + "Laplacetrf;": "\u2112", + "Larr;": "\u219e", + "Lcaron;": "\u013d", + "Lcedil;": "\u013b", + "Lcy;": "\u041b", + "LeftAngleBracket;": "\u27e8", + "LeftArrow;": "\u2190", + "LeftArrowBar;": "\u21e4", + "LeftArrowRightArrow;": "\u21c6", + "LeftCeiling;": "\u2308", + "LeftDoubleBracket;": "\u27e6", + "LeftDownTeeVector;": "\u2961", + "LeftDownVector;": "\u21c3", + "LeftDownVectorBar;": "\u2959", + "LeftFloor;": "\u230a", + "LeftRightArrow;": "\u2194", + "LeftRightVector;": "\u294e", + "LeftTee;": "\u22a3", + "LeftTeeArrow;": "\u21a4", + "LeftTeeVector;": "\u295a", + "LeftTriangle;": "\u22b2", + "LeftTriangleBar;": "\u29cf", + "LeftTriangleEqual;": "\u22b4", + "LeftUpDownVector;": "\u2951", + "LeftUpTeeVector;": "\u2960", + "LeftUpVector;": "\u21bf", + "LeftUpVectorBar;": "\u2958", + "LeftVector;": "\u21bc", + "LeftVectorBar;": "\u2952", + "Leftarrow;": "\u21d0", + "Leftrightarrow;": "\u21d4", + "LessEqualGreater;": "\u22da", + "LessFullEqual;": "\u2266", + "LessGreater;": "\u2276", + "LessLess;": "\u2aa1", + "LessSlantEqual;": "\u2a7d", + "LessTilde;": "\u2272", + "Lfr;": "\U0001d50f", + "Ll;": "\u22d8", + "Lleftarrow;": "\u21da", + "Lmidot;": "\u013f", + "LongLeftArrow;": "\u27f5", + "LongLeftRightArrow;": "\u27f7", + "LongRightArrow;": "\u27f6", + "Longleftarrow;": "\u27f8", + "Longleftrightarrow;": "\u27fa", + "Longrightarrow;": "\u27f9", + "Lopf;": "\U0001d543", + "LowerLeftArrow;": "\u2199", + "LowerRightArrow;": "\u2198", + "Lscr;": "\u2112", + "Lsh;": "\u21b0", + "Lstrok;": "\u0141", + "Lt;": "\u226a", + "Map;": "\u2905", + "Mcy;": "\u041c", + "MediumSpace;": "\u205f", + "Mellintrf;": "\u2133", + "Mfr;": "\U0001d510", + "MinusPlus;": "\u2213", + "Mopf;": "\U0001d544", + "Mscr;": "\u2133", + "Mu;": "\u039c", + "NJcy;": "\u040a", + "Nacute;": "\u0143", + "Ncaron;": "\u0147", + "Ncedil;": "\u0145", + "Ncy;": "\u041d", + "NegativeMediumSpace;": "\u200b", + "NegativeThickSpace;": "\u200b", + "NegativeThinSpace;": "\u200b", + "NegativeVeryThinSpace;": "\u200b", + "NestedGreaterGreater;": "\u226b", + "NestedLessLess;": "\u226a", + "NewLine;": "\n", + "Nfr;": "\U0001d511", + "NoBreak;": "\u2060", + "NonBreakingSpace;": "\xa0", + "Nopf;": "\u2115", + "Not;": "\u2aec", + "NotCongruent;": "\u2262", + "NotCupCap;": "\u226d", + "NotDoubleVerticalBar;": "\u2226", + "NotElement;": "\u2209", + "NotEqual;": "\u2260", + "NotEqualTilde;": "\u2242\u0338", + "NotExists;": "\u2204", + "NotGreater;": "\u226f", + "NotGreaterEqual;": "\u2271", + "NotGreaterFullEqual;": "\u2267\u0338", + "NotGreaterGreater;": "\u226b\u0338", + "NotGreaterLess;": "\u2279", + "NotGreaterSlantEqual;": "\u2a7e\u0338", + "NotGreaterTilde;": "\u2275", + "NotHumpDownHump;": "\u224e\u0338", + "NotHumpEqual;": "\u224f\u0338", + "NotLeftTriangle;": "\u22ea", + "NotLeftTriangleBar;": "\u29cf\u0338", + "NotLeftTriangleEqual;": "\u22ec", + "NotLess;": "\u226e", + "NotLessEqual;": "\u2270", + "NotLessGreater;": "\u2278", + "NotLessLess;": "\u226a\u0338", + "NotLessSlantEqual;": "\u2a7d\u0338", + "NotLessTilde;": "\u2274", + "NotNestedGreaterGreater;": "\u2aa2\u0338", + "NotNestedLessLess;": "\u2aa1\u0338", + "NotPrecedes;": "\u2280", + "NotPrecedesEqual;": "\u2aaf\u0338", + "NotPrecedesSlantEqual;": "\u22e0", + "NotReverseElement;": "\u220c", + "NotRightTriangle;": "\u22eb", + "NotRightTriangleBar;": "\u29d0\u0338", + "NotRightTriangleEqual;": "\u22ed", + "NotSquareSubset;": "\u228f\u0338", + "NotSquareSubsetEqual;": "\u22e2", + "NotSquareSuperset;": "\u2290\u0338", + "NotSquareSupersetEqual;": "\u22e3", + "NotSubset;": "\u2282\u20d2", + "NotSubsetEqual;": "\u2288", + "NotSucceeds;": "\u2281", + "NotSucceedsEqual;": "\u2ab0\u0338", + "NotSucceedsSlantEqual;": "\u22e1", + "NotSucceedsTilde;": "\u227f\u0338", + "NotSuperset;": "\u2283\u20d2", + "NotSupersetEqual;": "\u2289", + "NotTilde;": "\u2241", + "NotTildeEqual;": "\u2244", + "NotTildeFullEqual;": "\u2247", + "NotTildeTilde;": "\u2249", + "NotVerticalBar;": "\u2224", + "Nscr;": "\U0001d4a9", + "Ntilde": "\xd1", + "Ntilde;": "\xd1", + "Nu;": "\u039d", + "OElig;": "\u0152", + "Oacute": "\xd3", + "Oacute;": "\xd3", + "Ocirc": "\xd4", + "Ocirc;": "\xd4", + "Ocy;": "\u041e", + "Odblac;": "\u0150", + "Ofr;": "\U0001d512", + "Ograve": "\xd2", + "Ograve;": "\xd2", + "Omacr;": "\u014c", + "Omega;": "\u03a9", + "Omicron;": "\u039f", + "Oopf;": "\U0001d546", + "OpenCurlyDoubleQuote;": "\u201c", + "OpenCurlyQuote;": "\u2018", + "Or;": "\u2a54", + "Oscr;": "\U0001d4aa", + "Oslash": "\xd8", + "Oslash;": "\xd8", + "Otilde": "\xd5", + "Otilde;": "\xd5", + "Otimes;": "\u2a37", + "Ouml": "\xd6", + "Ouml;": "\xd6", + "OverBar;": "\u203e", + "OverBrace;": "\u23de", + "OverBracket;": "\u23b4", + "OverParenthesis;": "\u23dc", + "PartialD;": "\u2202", + "Pcy;": "\u041f", + "Pfr;": "\U0001d513", + "Phi;": "\u03a6", + "Pi;": "\u03a0", + "PlusMinus;": "\xb1", + "Poincareplane;": "\u210c", + "Popf;": "\u2119", + "Pr;": "\u2abb", + "Precedes;": "\u227a", + "PrecedesEqual;": "\u2aaf", + "PrecedesSlantEqual;": "\u227c", + "PrecedesTilde;": "\u227e", + "Prime;": "\u2033", + "Product;": "\u220f", + "Proportion;": "\u2237", + "Proportional;": "\u221d", + "Pscr;": "\U0001d4ab", + "Psi;": "\u03a8", + "QUOT": "\"", + "QUOT;": "\"", + "Qfr;": "\U0001d514", + "Qopf;": "\u211a", + "Qscr;": "\U0001d4ac", + "RBarr;": "\u2910", + "REG": "\xae", + "REG;": "\xae", + "Racute;": "\u0154", + "Rang;": "\u27eb", + "Rarr;": "\u21a0", + "Rarrtl;": "\u2916", + "Rcaron;": "\u0158", + "Rcedil;": "\u0156", + "Rcy;": "\u0420", + "Re;": "\u211c", + "ReverseElement;": "\u220b", + "ReverseEquilibrium;": "\u21cb", + "ReverseUpEquilibrium;": "\u296f", + "Rfr;": "\u211c", + "Rho;": "\u03a1", + "RightAngleBracket;": "\u27e9", + "RightArrow;": "\u2192", + "RightArrowBar;": "\u21e5", + "RightArrowLeftArrow;": "\u21c4", + "RightCeiling;": "\u2309", + "RightDoubleBracket;": "\u27e7", + "RightDownTeeVector;": "\u295d", + "RightDownVector;": "\u21c2", + "RightDownVectorBar;": "\u2955", + "RightFloor;": "\u230b", + "RightTee;": "\u22a2", + "RightTeeArrow;": "\u21a6", + "RightTeeVector;": "\u295b", + "RightTriangle;": "\u22b3", + "RightTriangleBar;": "\u29d0", + "RightTriangleEqual;": "\u22b5", + "RightUpDownVector;": "\u294f", + "RightUpTeeVector;": "\u295c", + "RightUpVector;": "\u21be", + "RightUpVectorBar;": "\u2954", + "RightVector;": "\u21c0", + "RightVectorBar;": "\u2953", + "Rightarrow;": "\u21d2", + "Ropf;": "\u211d", + "RoundImplies;": "\u2970", + "Rrightarrow;": "\u21db", + "Rscr;": "\u211b", + "Rsh;": "\u21b1", + "RuleDelayed;": "\u29f4", + "SHCHcy;": "\u0429", + "SHcy;": "\u0428", + "SOFTcy;": "\u042c", + "Sacute;": "\u015a", + "Sc;": "\u2abc", + "Scaron;": "\u0160", + "Scedil;": "\u015e", + "Scirc;": "\u015c", + "Scy;": "\u0421", + "Sfr;": "\U0001d516", + "ShortDownArrow;": "\u2193", + "ShortLeftArrow;": "\u2190", + "ShortRightArrow;": "\u2192", + "ShortUpArrow;": "\u2191", + "Sigma;": "\u03a3", + "SmallCircle;": "\u2218", + "Sopf;": "\U0001d54a", + "Sqrt;": "\u221a", + "Square;": "\u25a1", + "SquareIntersection;": "\u2293", + "SquareSubset;": "\u228f", + "SquareSubsetEqual;": "\u2291", + "SquareSuperset;": "\u2290", + "SquareSupersetEqual;": "\u2292", + "SquareUnion;": "\u2294", + "Sscr;": "\U0001d4ae", + "Star;": "\u22c6", + "Sub;": "\u22d0", + "Subset;": "\u22d0", + "SubsetEqual;": "\u2286", + "Succeeds;": "\u227b", + "SucceedsEqual;": "\u2ab0", + "SucceedsSlantEqual;": "\u227d", + "SucceedsTilde;": "\u227f", + "SuchThat;": "\u220b", + "Sum;": "\u2211", + "Sup;": "\u22d1", + "Superset;": "\u2283", + "SupersetEqual;": "\u2287", + "Supset;": "\u22d1", + "THORN": "\xde", + "THORN;": "\xde", + "TRADE;": "\u2122", + "TSHcy;": "\u040b", + "TScy;": "\u0426", + "Tab;": "\t", + "Tau;": "\u03a4", + "Tcaron;": "\u0164", + "Tcedil;": "\u0162", + "Tcy;": "\u0422", + "Tfr;": "\U0001d517", + "Therefore;": "\u2234", + "Theta;": "\u0398", + "ThickSpace;": "\u205f\u200a", + "ThinSpace;": "\u2009", + "Tilde;": "\u223c", + "TildeEqual;": "\u2243", + "TildeFullEqual;": "\u2245", + "TildeTilde;": "\u2248", + "Topf;": "\U0001d54b", + "TripleDot;": "\u20db", + "Tscr;": "\U0001d4af", + "Tstrok;": "\u0166", + "Uacute": "\xda", + "Uacute;": "\xda", + "Uarr;": "\u219f", + "Uarrocir;": "\u2949", + "Ubrcy;": "\u040e", + "Ubreve;": "\u016c", + "Ucirc": "\xdb", + "Ucirc;": "\xdb", + "Ucy;": "\u0423", + "Udblac;": "\u0170", + "Ufr;": "\U0001d518", + "Ugrave": "\xd9", + "Ugrave;": "\xd9", + "Umacr;": "\u016a", + "UnderBar;": "_", + "UnderBrace;": "\u23df", + "UnderBracket;": "\u23b5", + "UnderParenthesis;": "\u23dd", + "Union;": "\u22c3", + "UnionPlus;": "\u228e", + "Uogon;": "\u0172", + "Uopf;": "\U0001d54c", + "UpArrow;": "\u2191", + "UpArrowBar;": "\u2912", + "UpArrowDownArrow;": "\u21c5", + "UpDownArrow;": "\u2195", + "UpEquilibrium;": "\u296e", + "UpTee;": "\u22a5", + "UpTeeArrow;": "\u21a5", + "Uparrow;": "\u21d1", + "Updownarrow;": "\u21d5", + "UpperLeftArrow;": "\u2196", + "UpperRightArrow;": "\u2197", + "Upsi;": "\u03d2", + "Upsilon;": "\u03a5", + "Uring;": "\u016e", + "Uscr;": "\U0001d4b0", + "Utilde;": "\u0168", + "Uuml": "\xdc", + "Uuml;": "\xdc", + "VDash;": "\u22ab", + "Vbar;": "\u2aeb", + "Vcy;": "\u0412", + "Vdash;": "\u22a9", + "Vdashl;": "\u2ae6", + "Vee;": "\u22c1", + "Verbar;": "\u2016", + "Vert;": "\u2016", + "VerticalBar;": "\u2223", + "VerticalLine;": "|", + "VerticalSeparator;": "\u2758", + "VerticalTilde;": "\u2240", + "VeryThinSpace;": "\u200a", + "Vfr;": "\U0001d519", + "Vopf;": "\U0001d54d", + "Vscr;": "\U0001d4b1", + "Vvdash;": "\u22aa", + "Wcirc;": "\u0174", + "Wedge;": "\u22c0", + "Wfr;": "\U0001d51a", + "Wopf;": "\U0001d54e", + "Wscr;": "\U0001d4b2", + "Xfr;": "\U0001d51b", + "Xi;": "\u039e", + "Xopf;": "\U0001d54f", + "Xscr;": "\U0001d4b3", + "YAcy;": "\u042f", + "YIcy;": "\u0407", + "YUcy;": "\u042e", + "Yacute": "\xdd", + "Yacute;": "\xdd", + "Ycirc;": "\u0176", + "Ycy;": "\u042b", + "Yfr;": "\U0001d51c", + "Yopf;": "\U0001d550", + "Yscr;": "\U0001d4b4", + "Yuml;": "\u0178", + "ZHcy;": "\u0416", + "Zacute;": "\u0179", + "Zcaron;": "\u017d", + "Zcy;": "\u0417", + "Zdot;": "\u017b", + "ZeroWidthSpace;": "\u200b", + "Zeta;": "\u0396", + "Zfr;": "\u2128", + "Zopf;": "\u2124", + "Zscr;": "\U0001d4b5", + "aacute": "\xe1", + "aacute;": "\xe1", + "abreve;": "\u0103", + "ac;": "\u223e", + "acE;": "\u223e\u0333", + "acd;": "\u223f", + "acirc": "\xe2", + "acirc;": "\xe2", + "acute": "\xb4", + "acute;": "\xb4", + "acy;": "\u0430", + "aelig": "\xe6", + "aelig;": "\xe6", + "af;": "\u2061", + "afr;": "\U0001d51e", + "agrave": "\xe0", + "agrave;": "\xe0", + "alefsym;": "\u2135", + "aleph;": "\u2135", + "alpha;": "\u03b1", + "amacr;": "\u0101", + "amalg;": "\u2a3f", + "amp": "&", + "amp;": "&", + "and;": "\u2227", + "andand;": "\u2a55", + "andd;": "\u2a5c", + "andslope;": "\u2a58", + "andv;": "\u2a5a", + "ang;": "\u2220", + "ange;": "\u29a4", + "angle;": "\u2220", + "angmsd;": "\u2221", + "angmsdaa;": "\u29a8", + "angmsdab;": "\u29a9", + "angmsdac;": "\u29aa", + "angmsdad;": "\u29ab", + "angmsdae;": "\u29ac", + "angmsdaf;": "\u29ad", + "angmsdag;": "\u29ae", + "angmsdah;": "\u29af", + "angrt;": "\u221f", + "angrtvb;": "\u22be", + "angrtvbd;": "\u299d", + "angsph;": "\u2222", + "angst;": "\xc5", + "angzarr;": "\u237c", + "aogon;": "\u0105", + "aopf;": "\U0001d552", + "ap;": "\u2248", + "apE;": "\u2a70", + "apacir;": "\u2a6f", + "ape;": "\u224a", + "apid;": "\u224b", + "apos;": "'", + "approx;": "\u2248", + "approxeq;": "\u224a", + "aring": "\xe5", + "aring;": "\xe5", + "ascr;": "\U0001d4b6", + "ast;": "*", + "asymp;": "\u2248", + "asympeq;": "\u224d", + "atilde": "\xe3", + "atilde;": "\xe3", + "auml": "\xe4", + "auml;": "\xe4", + "awconint;": "\u2233", + "awint;": "\u2a11", + "bNot;": "\u2aed", + "backcong;": "\u224c", + "backepsilon;": "\u03f6", + "backprime;": "\u2035", + "backsim;": "\u223d", + "backsimeq;": "\u22cd", + "barvee;": "\u22bd", + "barwed;": "\u2305", + "barwedge;": "\u2305", + "bbrk;": "\u23b5", + "bbrktbrk;": "\u23b6", + "bcong;": "\u224c", + "bcy;": "\u0431", + "bdquo;": "\u201e", + "becaus;": "\u2235", + "because;": "\u2235", + "bemptyv;": "\u29b0", + "bepsi;": "\u03f6", + "bernou;": "\u212c", + "beta;": "\u03b2", + "beth;": "\u2136", + "between;": "\u226c", + "bfr;": "\U0001d51f", + "bigcap;": "\u22c2", + "bigcirc;": "\u25ef", + "bigcup;": "\u22c3", + "bigodot;": "\u2a00", + "bigoplus;": "\u2a01", + "bigotimes;": "\u2a02", + "bigsqcup;": "\u2a06", + "bigstar;": "\u2605", + "bigtriangledown;": "\u25bd", + "bigtriangleup;": "\u25b3", + "biguplus;": "\u2a04", + "bigvee;": "\u22c1", + "bigwedge;": "\u22c0", + "bkarow;": "\u290d", + "blacklozenge;": "\u29eb", + "blacksquare;": "\u25aa", + "blacktriangle;": "\u25b4", + "blacktriangledown;": "\u25be", + "blacktriangleleft;": "\u25c2", + "blacktriangleright;": "\u25b8", + "blank;": "\u2423", + "blk12;": "\u2592", + "blk14;": "\u2591", + "blk34;": "\u2593", + "block;": "\u2588", + "bne;": "=\u20e5", + "bnequiv;": "\u2261\u20e5", + "bnot;": "\u2310", + "bopf;": "\U0001d553", + "bot;": "\u22a5", + "bottom;": "\u22a5", + "bowtie;": "\u22c8", + "boxDL;": "\u2557", + "boxDR;": "\u2554", + "boxDl;": "\u2556", + "boxDr;": "\u2553", + "boxH;": "\u2550", + "boxHD;": "\u2566", + "boxHU;": "\u2569", + "boxHd;": "\u2564", + "boxHu;": "\u2567", + "boxUL;": "\u255d", + "boxUR;": "\u255a", + "boxUl;": "\u255c", + "boxUr;": "\u2559", + "boxV;": "\u2551", + "boxVH;": "\u256c", + "boxVL;": "\u2563", + "boxVR;": "\u2560", + "boxVh;": "\u256b", + "boxVl;": "\u2562", + "boxVr;": "\u255f", + "boxbox;": "\u29c9", + "boxdL;": "\u2555", + "boxdR;": "\u2552", + "boxdl;": "\u2510", + "boxdr;": "\u250c", + "boxh;": "\u2500", + "boxhD;": "\u2565", + "boxhU;": "\u2568", + "boxhd;": "\u252c", + "boxhu;": "\u2534", + "boxminus;": "\u229f", + "boxplus;": "\u229e", + "boxtimes;": "\u22a0", + "boxuL;": "\u255b", + "boxuR;": "\u2558", + "boxul;": "\u2518", + "boxur;": "\u2514", + "boxv;": "\u2502", + "boxvH;": "\u256a", + "boxvL;": "\u2561", + "boxvR;": "\u255e", + "boxvh;": "\u253c", + "boxvl;": "\u2524", + "boxvr;": "\u251c", + "bprime;": "\u2035", + "breve;": "\u02d8", + "brvbar": "\xa6", + "brvbar;": "\xa6", + "bscr;": "\U0001d4b7", + "bsemi;": "\u204f", + "bsim;": "\u223d", + "bsime;": "\u22cd", + "bsol;": "\\", + "bsolb;": "\u29c5", + "bsolhsub;": "\u27c8", + "bull;": "\u2022", + "bullet;": "\u2022", + "bump;": "\u224e", + "bumpE;": "\u2aae", + "bumpe;": "\u224f", + "bumpeq;": "\u224f", + "cacute;": "\u0107", + "cap;": "\u2229", + "capand;": "\u2a44", + "capbrcup;": "\u2a49", + "capcap;": "\u2a4b", + "capcup;": "\u2a47", + "capdot;": "\u2a40", + "caps;": "\u2229\ufe00", + "caret;": "\u2041", + "caron;": "\u02c7", + "ccaps;": "\u2a4d", + "ccaron;": "\u010d", + "ccedil": "\xe7", + "ccedil;": "\xe7", + "ccirc;": "\u0109", + "ccups;": "\u2a4c", + "ccupssm;": "\u2a50", + "cdot;": "\u010b", + "cedil": "\xb8", + "cedil;": "\xb8", + "cemptyv;": "\u29b2", + "cent": "\xa2", + "cent;": "\xa2", + "centerdot;": "\xb7", + "cfr;": "\U0001d520", + "chcy;": "\u0447", + "check;": "\u2713", + "checkmark;": "\u2713", + "chi;": "\u03c7", + "cir;": "\u25cb", + "cirE;": "\u29c3", + "circ;": "\u02c6", + "circeq;": "\u2257", + "circlearrowleft;": "\u21ba", + "circlearrowright;": "\u21bb", + "circledR;": "\xae", + "circledS;": "\u24c8", + "circledast;": "\u229b", + "circledcirc;": "\u229a", + "circleddash;": "\u229d", + "cire;": "\u2257", + "cirfnint;": "\u2a10", + "cirmid;": "\u2aef", + "cirscir;": "\u29c2", + "clubs;": "\u2663", + "clubsuit;": "\u2663", + "colon;": ":", + "colone;": "\u2254", + "coloneq;": "\u2254", + "comma;": ",", + "commat;": "@", + "comp;": "\u2201", + "compfn;": "\u2218", + "complement;": "\u2201", + "complexes;": "\u2102", + "cong;": "\u2245", + "congdot;": "\u2a6d", + "conint;": "\u222e", + "copf;": "\U0001d554", + "coprod;": "\u2210", + "copy": "\xa9", + "copy;": "\xa9", + "copysr;": "\u2117", + "crarr;": "\u21b5", + "cross;": "\u2717", + "cscr;": "\U0001d4b8", + "csub;": "\u2acf", + "csube;": "\u2ad1", + "csup;": "\u2ad0", + "csupe;": "\u2ad2", + "ctdot;": "\u22ef", + "cudarrl;": "\u2938", + "cudarrr;": "\u2935", + "cuepr;": "\u22de", + "cuesc;": "\u22df", + "cularr;": "\u21b6", + "cularrp;": "\u293d", + "cup;": "\u222a", + "cupbrcap;": "\u2a48", + "cupcap;": "\u2a46", + "cupcup;": "\u2a4a", + "cupdot;": "\u228d", + "cupor;": "\u2a45", + "cups;": "\u222a\ufe00", + "curarr;": "\u21b7", + "curarrm;": "\u293c", + "curlyeqprec;": "\u22de", + "curlyeqsucc;": "\u22df", + "curlyvee;": "\u22ce", + "curlywedge;": "\u22cf", + "curren": "\xa4", + "curren;": "\xa4", + "curvearrowleft;": "\u21b6", + "curvearrowright;": "\u21b7", + "cuvee;": "\u22ce", + "cuwed;": "\u22cf", + "cwconint;": "\u2232", + "cwint;": "\u2231", + "cylcty;": "\u232d", + "dArr;": "\u21d3", + "dHar;": "\u2965", + "dagger;": "\u2020", + "daleth;": "\u2138", + "darr;": "\u2193", + "dash;": "\u2010", + "dashv;": "\u22a3", + "dbkarow;": "\u290f", + "dblac;": "\u02dd", + "dcaron;": "\u010f", + "dcy;": "\u0434", + "dd;": "\u2146", + "ddagger;": "\u2021", + "ddarr;": "\u21ca", + "ddotseq;": "\u2a77", + "deg": "\xb0", + "deg;": "\xb0", + "delta;": "\u03b4", + "demptyv;": "\u29b1", + "dfisht;": "\u297f", + "dfr;": "\U0001d521", + "dharl;": "\u21c3", + "dharr;": "\u21c2", + "diam;": "\u22c4", + "diamond;": "\u22c4", + "diamondsuit;": "\u2666", + "diams;": "\u2666", + "die;": "\xa8", + "digamma;": "\u03dd", + "disin;": "\u22f2", + "div;": "\xf7", + "divide": "\xf7", + "divide;": "\xf7", + "divideontimes;": "\u22c7", + "divonx;": "\u22c7", + "djcy;": "\u0452", + "dlcorn;": "\u231e", + "dlcrop;": "\u230d", + "dollar;": "$", + "dopf;": "\U0001d555", + "dot;": "\u02d9", + "doteq;": "\u2250", + "doteqdot;": "\u2251", + "dotminus;": "\u2238", + "dotplus;": "\u2214", + "dotsquare;": "\u22a1", + "doublebarwedge;": "\u2306", + "downarrow;": "\u2193", + "downdownarrows;": "\u21ca", + "downharpoonleft;": "\u21c3", + "downharpoonright;": "\u21c2", + "drbkarow;": "\u2910", + "drcorn;": "\u231f", + "drcrop;": "\u230c", + "dscr;": "\U0001d4b9", + "dscy;": "\u0455", + "dsol;": "\u29f6", + "dstrok;": "\u0111", + "dtdot;": "\u22f1", + "dtri;": "\u25bf", + "dtrif;": "\u25be", + "duarr;": "\u21f5", + "duhar;": "\u296f", + "dwangle;": "\u29a6", + "dzcy;": "\u045f", + "dzigrarr;": "\u27ff", + "eDDot;": "\u2a77", + "eDot;": "\u2251", + "eacute": "\xe9", + "eacute;": "\xe9", + "easter;": "\u2a6e", + "ecaron;": "\u011b", + "ecir;": "\u2256", + "ecirc": "\xea", + "ecirc;": "\xea", + "ecolon;": "\u2255", + "ecy;": "\u044d", + "edot;": "\u0117", + "ee;": "\u2147", + "efDot;": "\u2252", + "efr;": "\U0001d522", + "eg;": "\u2a9a", + "egrave": "\xe8", + "egrave;": "\xe8", + "egs;": "\u2a96", + "egsdot;": "\u2a98", + "el;": "\u2a99", + "elinters;": "\u23e7", + "ell;": "\u2113", + "els;": "\u2a95", + "elsdot;": "\u2a97", + "emacr;": "\u0113", + "empty;": "\u2205", + "emptyset;": "\u2205", + "emptyv;": "\u2205", + "emsp13;": "\u2004", + "emsp14;": "\u2005", + "emsp;": "\u2003", + "eng;": "\u014b", + "ensp;": "\u2002", + "eogon;": "\u0119", + "eopf;": "\U0001d556", + "epar;": "\u22d5", + "eparsl;": "\u29e3", + "eplus;": "\u2a71", + "epsi;": "\u03b5", + "epsilon;": "\u03b5", + "epsiv;": "\u03f5", + "eqcirc;": "\u2256", + "eqcolon;": "\u2255", + "eqsim;": "\u2242", + "eqslantgtr;": "\u2a96", + "eqslantless;": "\u2a95", + "equals;": "=", + "equest;": "\u225f", + "equiv;": "\u2261", + "equivDD;": "\u2a78", + "eqvparsl;": "\u29e5", + "erDot;": "\u2253", + "erarr;": "\u2971", + "escr;": "\u212f", + "esdot;": "\u2250", + "esim;": "\u2242", + "eta;": "\u03b7", + "eth": "\xf0", + "eth;": "\xf0", + "euml": "\xeb", + "euml;": "\xeb", + "euro;": "\u20ac", + "excl;": "!", + "exist;": "\u2203", + "expectation;": "\u2130", + "exponentiale;": "\u2147", + "fallingdotseq;": "\u2252", + "fcy;": "\u0444", + "female;": "\u2640", + "ffilig;": "\ufb03", + "fflig;": "\ufb00", + "ffllig;": "\ufb04", + "ffr;": "\U0001d523", + "filig;": "\ufb01", + "fjlig;": "fj", + "flat;": "\u266d", + "fllig;": "\ufb02", + "fltns;": "\u25b1", + "fnof;": "\u0192", + "fopf;": "\U0001d557", + "forall;": "\u2200", + "fork;": "\u22d4", + "forkv;": "\u2ad9", + "fpartint;": "\u2a0d", + "frac12": "\xbd", + "frac12;": "\xbd", + "frac13;": "\u2153", + "frac14": "\xbc", + "frac14;": "\xbc", + "frac15;": "\u2155", + "frac16;": "\u2159", + "frac18;": "\u215b", + "frac23;": "\u2154", + "frac25;": "\u2156", + "frac34": "\xbe", + "frac34;": "\xbe", + "frac35;": "\u2157", + "frac38;": "\u215c", + "frac45;": "\u2158", + "frac56;": "\u215a", + "frac58;": "\u215d", + "frac78;": "\u215e", + "frasl;": "\u2044", + "frown;": "\u2322", + "fscr;": "\U0001d4bb", + "gE;": "\u2267", + "gEl;": "\u2a8c", + "gacute;": "\u01f5", + "gamma;": "\u03b3", + "gammad;": "\u03dd", + "gap;": "\u2a86", + "gbreve;": "\u011f", + "gcirc;": "\u011d", + "gcy;": "\u0433", + "gdot;": "\u0121", + "ge;": "\u2265", + "gel;": "\u22db", + "geq;": "\u2265", + "geqq;": "\u2267", + "geqslant;": "\u2a7e", + "ges;": "\u2a7e", + "gescc;": "\u2aa9", + "gesdot;": "\u2a80", + "gesdoto;": "\u2a82", + "gesdotol;": "\u2a84", + "gesl;": "\u22db\ufe00", + "gesles;": "\u2a94", + "gfr;": "\U0001d524", + "gg;": "\u226b", + "ggg;": "\u22d9", + "gimel;": "\u2137", + "gjcy;": "\u0453", + "gl;": "\u2277", + "glE;": "\u2a92", + "gla;": "\u2aa5", + "glj;": "\u2aa4", + "gnE;": "\u2269", + "gnap;": "\u2a8a", + "gnapprox;": "\u2a8a", + "gne;": "\u2a88", + "gneq;": "\u2a88", + "gneqq;": "\u2269", + "gnsim;": "\u22e7", + "gopf;": "\U0001d558", + "grave;": "`", + "gscr;": "\u210a", + "gsim;": "\u2273", + "gsime;": "\u2a8e", + "gsiml;": "\u2a90", + "gt": ">", + "gt;": ">", + "gtcc;": "\u2aa7", + "gtcir;": "\u2a7a", + "gtdot;": "\u22d7", + "gtlPar;": "\u2995", + "gtquest;": "\u2a7c", + "gtrapprox;": "\u2a86", + "gtrarr;": "\u2978", + "gtrdot;": "\u22d7", + "gtreqless;": "\u22db", + "gtreqqless;": "\u2a8c", + "gtrless;": "\u2277", + "gtrsim;": "\u2273", + "gvertneqq;": "\u2269\ufe00", + "gvnE;": "\u2269\ufe00", + "hArr;": "\u21d4", + "hairsp;": "\u200a", + "half;": "\xbd", + "hamilt;": "\u210b", + "hardcy;": "\u044a", + "harr;": "\u2194", + "harrcir;": "\u2948", + "harrw;": "\u21ad", + "hbar;": "\u210f", + "hcirc;": "\u0125", + "hearts;": "\u2665", + "heartsuit;": "\u2665", + "hellip;": "\u2026", + "hercon;": "\u22b9", + "hfr;": "\U0001d525", + "hksearow;": "\u2925", + "hkswarow;": "\u2926", + "hoarr;": "\u21ff", + "homtht;": "\u223b", + "hookleftarrow;": "\u21a9", + "hookrightarrow;": "\u21aa", + "hopf;": "\U0001d559", + "horbar;": "\u2015", + "hscr;": "\U0001d4bd", + "hslash;": "\u210f", + "hstrok;": "\u0127", + "hybull;": "\u2043", + "hyphen;": "\u2010", + "iacute": "\xed", + "iacute;": "\xed", + "ic;": "\u2063", + "icirc": "\xee", + "icirc;": "\xee", + "icy;": "\u0438", + "iecy;": "\u0435", + "iexcl": "\xa1", + "iexcl;": "\xa1", + "iff;": "\u21d4", + "ifr;": "\U0001d526", + "igrave": "\xec", + "igrave;": "\xec", + "ii;": "\u2148", + "iiiint;": "\u2a0c", + "iiint;": "\u222d", + "iinfin;": "\u29dc", + "iiota;": "\u2129", + "ijlig;": "\u0133", + "imacr;": "\u012b", + "image;": "\u2111", + "imagline;": "\u2110", + "imagpart;": "\u2111", + "imath;": "\u0131", + "imof;": "\u22b7", + "imped;": "\u01b5", + "in;": "\u2208", + "incare;": "\u2105", + "infin;": "\u221e", + "infintie;": "\u29dd", + "inodot;": "\u0131", + "int;": "\u222b", + "intcal;": "\u22ba", + "integers;": "\u2124", + "intercal;": "\u22ba", + "intlarhk;": "\u2a17", + "intprod;": "\u2a3c", + "iocy;": "\u0451", + "iogon;": "\u012f", + "iopf;": "\U0001d55a", + "iota;": "\u03b9", + "iprod;": "\u2a3c", + "iquest": "\xbf", + "iquest;": "\xbf", + "iscr;": "\U0001d4be", + "isin;": "\u2208", + "isinE;": "\u22f9", + "isindot;": "\u22f5", + "isins;": "\u22f4", + "isinsv;": "\u22f3", + "isinv;": "\u2208", + "it;": "\u2062", + "itilde;": "\u0129", + "iukcy;": "\u0456", + "iuml": "\xef", + "iuml;": "\xef", + "jcirc;": "\u0135", + "jcy;": "\u0439", + "jfr;": "\U0001d527", + "jmath;": "\u0237", + "jopf;": "\U0001d55b", + "jscr;": "\U0001d4bf", + "jsercy;": "\u0458", + "jukcy;": "\u0454", + "kappa;": "\u03ba", + "kappav;": "\u03f0", + "kcedil;": "\u0137", + "kcy;": "\u043a", + "kfr;": "\U0001d528", + "kgreen;": "\u0138", + "khcy;": "\u0445", + "kjcy;": "\u045c", + "kopf;": "\U0001d55c", + "kscr;": "\U0001d4c0", + "lAarr;": "\u21da", + "lArr;": "\u21d0", + "lAtail;": "\u291b", + "lBarr;": "\u290e", + "lE;": "\u2266", + "lEg;": "\u2a8b", + "lHar;": "\u2962", + "lacute;": "\u013a", + "laemptyv;": "\u29b4", + "lagran;": "\u2112", + "lambda;": "\u03bb", + "lang;": "\u27e8", + "langd;": "\u2991", + "langle;": "\u27e8", + "lap;": "\u2a85", + "laquo": "\xab", + "laquo;": "\xab", + "larr;": "\u2190", + "larrb;": "\u21e4", + "larrbfs;": "\u291f", + "larrfs;": "\u291d", + "larrhk;": "\u21a9", + "larrlp;": "\u21ab", + "larrpl;": "\u2939", + "larrsim;": "\u2973", + "larrtl;": "\u21a2", + "lat;": "\u2aab", + "latail;": "\u2919", + "late;": "\u2aad", + "lates;": "\u2aad\ufe00", + "lbarr;": "\u290c", + "lbbrk;": "\u2772", + "lbrace;": "{", + "lbrack;": "[", + "lbrke;": "\u298b", + "lbrksld;": "\u298f", + "lbrkslu;": "\u298d", + "lcaron;": "\u013e", + "lcedil;": "\u013c", + "lceil;": "\u2308", + "lcub;": "{", + "lcy;": "\u043b", + "ldca;": "\u2936", + "ldquo;": "\u201c", + "ldquor;": "\u201e", + "ldrdhar;": "\u2967", + "ldrushar;": "\u294b", + "ldsh;": "\u21b2", + "le;": "\u2264", + "leftarrow;": "\u2190", + "leftarrowtail;": "\u21a2", + "leftharpoondown;": "\u21bd", + "leftharpoonup;": "\u21bc", + "leftleftarrows;": "\u21c7", + "leftrightarrow;": "\u2194", + "leftrightarrows;": "\u21c6", + "leftrightharpoons;": "\u21cb", + "leftrightsquigarrow;": "\u21ad", + "leftthreetimes;": "\u22cb", + "leg;": "\u22da", + "leq;": "\u2264", + "leqq;": "\u2266", + "leqslant;": "\u2a7d", + "les;": "\u2a7d", + "lescc;": "\u2aa8", + "lesdot;": "\u2a7f", + "lesdoto;": "\u2a81", + "lesdotor;": "\u2a83", + "lesg;": "\u22da\ufe00", + "lesges;": "\u2a93", + "lessapprox;": "\u2a85", + "lessdot;": "\u22d6", + "lesseqgtr;": "\u22da", + "lesseqqgtr;": "\u2a8b", + "lessgtr;": "\u2276", + "lesssim;": "\u2272", + "lfisht;": "\u297c", + "lfloor;": "\u230a", + "lfr;": "\U0001d529", + "lg;": "\u2276", + "lgE;": "\u2a91", + "lhard;": "\u21bd", + "lharu;": "\u21bc", + "lharul;": "\u296a", + "lhblk;": "\u2584", + "ljcy;": "\u0459", + "ll;": "\u226a", + "llarr;": "\u21c7", + "llcorner;": "\u231e", + "llhard;": "\u296b", + "lltri;": "\u25fa", + "lmidot;": "\u0140", + "lmoust;": "\u23b0", + "lmoustache;": "\u23b0", + "lnE;": "\u2268", + "lnap;": "\u2a89", + "lnapprox;": "\u2a89", + "lne;": "\u2a87", + "lneq;": "\u2a87", + "lneqq;": "\u2268", + "lnsim;": "\u22e6", + "loang;": "\u27ec", + "loarr;": "\u21fd", + "lobrk;": "\u27e6", + "longleftarrow;": "\u27f5", + "longleftrightarrow;": "\u27f7", + "longmapsto;": "\u27fc", + "longrightarrow;": "\u27f6", + "looparrowleft;": "\u21ab", + "looparrowright;": "\u21ac", + "lopar;": "\u2985", + "lopf;": "\U0001d55d", + "loplus;": "\u2a2d", + "lotimes;": "\u2a34", + "lowast;": "\u2217", + "lowbar;": "_", + "loz;": "\u25ca", + "lozenge;": "\u25ca", + "lozf;": "\u29eb", + "lpar;": "(", + "lparlt;": "\u2993", + "lrarr;": "\u21c6", + "lrcorner;": "\u231f", + "lrhar;": "\u21cb", + "lrhard;": "\u296d", + "lrm;": "\u200e", + "lrtri;": "\u22bf", + "lsaquo;": "\u2039", + "lscr;": "\U0001d4c1", + "lsh;": "\u21b0", + "lsim;": "\u2272", + "lsime;": "\u2a8d", + "lsimg;": "\u2a8f", + "lsqb;": "[", + "lsquo;": "\u2018", + "lsquor;": "\u201a", + "lstrok;": "\u0142", + "lt": "<", + "lt;": "<", + "ltcc;": "\u2aa6", + "ltcir;": "\u2a79", + "ltdot;": "\u22d6", + "lthree;": "\u22cb", + "ltimes;": "\u22c9", + "ltlarr;": "\u2976", + "ltquest;": "\u2a7b", + "ltrPar;": "\u2996", + "ltri;": "\u25c3", + "ltrie;": "\u22b4", + "ltrif;": "\u25c2", + "lurdshar;": "\u294a", + "luruhar;": "\u2966", + "lvertneqq;": "\u2268\ufe00", + "lvnE;": "\u2268\ufe00", + "mDDot;": "\u223a", + "macr": "\xaf", + "macr;": "\xaf", + "male;": "\u2642", + "malt;": "\u2720", + "maltese;": "\u2720", + "map;": "\u21a6", + "mapsto;": "\u21a6", + "mapstodown;": "\u21a7", + "mapstoleft;": "\u21a4", + "mapstoup;": "\u21a5", + "marker;": "\u25ae", + "mcomma;": "\u2a29", + "mcy;": "\u043c", + "mdash;": "\u2014", + "measuredangle;": "\u2221", + "mfr;": "\U0001d52a", + "mho;": "\u2127", + "micro": "\xb5", + "micro;": "\xb5", + "mid;": "\u2223", + "midast;": "*", + "midcir;": "\u2af0", + "middot": "\xb7", + "middot;": "\xb7", + "minus;": "\u2212", + "minusb;": "\u229f", + "minusd;": "\u2238", + "minusdu;": "\u2a2a", + "mlcp;": "\u2adb", + "mldr;": "\u2026", + "mnplus;": "\u2213", + "models;": "\u22a7", + "mopf;": "\U0001d55e", + "mp;": "\u2213", + "mscr;": "\U0001d4c2", + "mstpos;": "\u223e", + "mu;": "\u03bc", + "multimap;": "\u22b8", + "mumap;": "\u22b8", + "nGg;": "\u22d9\u0338", + "nGt;": "\u226b\u20d2", + "nGtv;": "\u226b\u0338", + "nLeftarrow;": "\u21cd", + "nLeftrightarrow;": "\u21ce", + "nLl;": "\u22d8\u0338", + "nLt;": "\u226a\u20d2", + "nLtv;": "\u226a\u0338", + "nRightarrow;": "\u21cf", + "nVDash;": "\u22af", + "nVdash;": "\u22ae", + "nabla;": "\u2207", + "nacute;": "\u0144", + "nang;": "\u2220\u20d2", + "nap;": "\u2249", + "napE;": "\u2a70\u0338", + "napid;": "\u224b\u0338", + "napos;": "\u0149", + "napprox;": "\u2249", + "natur;": "\u266e", + "natural;": "\u266e", + "naturals;": "\u2115", + "nbsp": "\xa0", + "nbsp;": "\xa0", + "nbump;": "\u224e\u0338", + "nbumpe;": "\u224f\u0338", + "ncap;": "\u2a43", + "ncaron;": "\u0148", + "ncedil;": "\u0146", + "ncong;": "\u2247", + "ncongdot;": "\u2a6d\u0338", + "ncup;": "\u2a42", + "ncy;": "\u043d", + "ndash;": "\u2013", + "ne;": "\u2260", + "neArr;": "\u21d7", + "nearhk;": "\u2924", + "nearr;": "\u2197", + "nearrow;": "\u2197", + "nedot;": "\u2250\u0338", + "nequiv;": "\u2262", + "nesear;": "\u2928", + "nesim;": "\u2242\u0338", + "nexist;": "\u2204", + "nexists;": "\u2204", + "nfr;": "\U0001d52b", + "ngE;": "\u2267\u0338", + "nge;": "\u2271", + "ngeq;": "\u2271", + "ngeqq;": "\u2267\u0338", + "ngeqslant;": "\u2a7e\u0338", + "nges;": "\u2a7e\u0338", + "ngsim;": "\u2275", + "ngt;": "\u226f", + "ngtr;": "\u226f", + "nhArr;": "\u21ce", + "nharr;": "\u21ae", + "nhpar;": "\u2af2", + "ni;": "\u220b", + "nis;": "\u22fc", + "nisd;": "\u22fa", + "niv;": "\u220b", + "njcy;": "\u045a", + "nlArr;": "\u21cd", + "nlE;": "\u2266\u0338", + "nlarr;": "\u219a", + "nldr;": "\u2025", + "nle;": "\u2270", + "nleftarrow;": "\u219a", + "nleftrightarrow;": "\u21ae", + "nleq;": "\u2270", + "nleqq;": "\u2266\u0338", + "nleqslant;": "\u2a7d\u0338", + "nles;": "\u2a7d\u0338", + "nless;": "\u226e", + "nlsim;": "\u2274", + "nlt;": "\u226e", + "nltri;": "\u22ea", + "nltrie;": "\u22ec", + "nmid;": "\u2224", + "nopf;": "\U0001d55f", + "not": "\xac", + "not;": "\xac", + "notin;": "\u2209", + "notinE;": "\u22f9\u0338", + "notindot;": "\u22f5\u0338", + "notinva;": "\u2209", + "notinvb;": "\u22f7", + "notinvc;": "\u22f6", + "notni;": "\u220c", + "notniva;": "\u220c", + "notnivb;": "\u22fe", + "notnivc;": "\u22fd", + "npar;": "\u2226", + "nparallel;": "\u2226", + "nparsl;": "\u2afd\u20e5", + "npart;": "\u2202\u0338", + "npolint;": "\u2a14", + "npr;": "\u2280", + "nprcue;": "\u22e0", + "npre;": "\u2aaf\u0338", + "nprec;": "\u2280", + "npreceq;": "\u2aaf\u0338", + "nrArr;": "\u21cf", + "nrarr;": "\u219b", + "nrarrc;": "\u2933\u0338", + "nrarrw;": "\u219d\u0338", + "nrightarrow;": "\u219b", + "nrtri;": "\u22eb", + "nrtrie;": "\u22ed", + "nsc;": "\u2281", + "nsccue;": "\u22e1", + "nsce;": "\u2ab0\u0338", + "nscr;": "\U0001d4c3", + "nshortmid;": "\u2224", + "nshortparallel;": "\u2226", + "nsim;": "\u2241", + "nsime;": "\u2244", + "nsimeq;": "\u2244", + "nsmid;": "\u2224", + "nspar;": "\u2226", + "nsqsube;": "\u22e2", + "nsqsupe;": "\u22e3", + "nsub;": "\u2284", + "nsubE;": "\u2ac5\u0338", + "nsube;": "\u2288", + "nsubset;": "\u2282\u20d2", + "nsubseteq;": "\u2288", + "nsubseteqq;": "\u2ac5\u0338", + "nsucc;": "\u2281", + "nsucceq;": "\u2ab0\u0338", + "nsup;": "\u2285", + "nsupE;": "\u2ac6\u0338", + "nsupe;": "\u2289", + "nsupset;": "\u2283\u20d2", + "nsupseteq;": "\u2289", + "nsupseteqq;": "\u2ac6\u0338", + "ntgl;": "\u2279", + "ntilde": "\xf1", + "ntilde;": "\xf1", + "ntlg;": "\u2278", + "ntriangleleft;": "\u22ea", + "ntrianglelefteq;": "\u22ec", + "ntriangleright;": "\u22eb", + "ntrianglerighteq;": "\u22ed", + "nu;": "\u03bd", + "num;": "#", + "numero;": "\u2116", + "numsp;": "\u2007", + "nvDash;": "\u22ad", + "nvHarr;": "\u2904", + "nvap;": "\u224d\u20d2", + "nvdash;": "\u22ac", + "nvge;": "\u2265\u20d2", + "nvgt;": ">\u20d2", + "nvinfin;": "\u29de", + "nvlArr;": "\u2902", + "nvle;": "\u2264\u20d2", + "nvlt;": "<\u20d2", + "nvltrie;": "\u22b4\u20d2", + "nvrArr;": "\u2903", + "nvrtrie;": "\u22b5\u20d2", + "nvsim;": "\u223c\u20d2", + "nwArr;": "\u21d6", + "nwarhk;": "\u2923", + "nwarr;": "\u2196", + "nwarrow;": "\u2196", + "nwnear;": "\u2927", + "oS;": "\u24c8", + "oacute": "\xf3", + "oacute;": "\xf3", + "oast;": "\u229b", + "ocir;": "\u229a", + "ocirc": "\xf4", + "ocirc;": "\xf4", + "ocy;": "\u043e", + "odash;": "\u229d", + "odblac;": "\u0151", + "odiv;": "\u2a38", + "odot;": "\u2299", + "odsold;": "\u29bc", + "oelig;": "\u0153", + "ofcir;": "\u29bf", + "ofr;": "\U0001d52c", + "ogon;": "\u02db", + "ograve": "\xf2", + "ograve;": "\xf2", + "ogt;": "\u29c1", + "ohbar;": "\u29b5", + "ohm;": "\u03a9", + "oint;": "\u222e", + "olarr;": "\u21ba", + "olcir;": "\u29be", + "olcross;": "\u29bb", + "oline;": "\u203e", + "olt;": "\u29c0", + "omacr;": "\u014d", + "omega;": "\u03c9", + "omicron;": "\u03bf", + "omid;": "\u29b6", + "ominus;": "\u2296", + "oopf;": "\U0001d560", + "opar;": "\u29b7", + "operp;": "\u29b9", + "oplus;": "\u2295", + "or;": "\u2228", + "orarr;": "\u21bb", + "ord;": "\u2a5d", + "order;": "\u2134", + "orderof;": "\u2134", + "ordf": "\xaa", + "ordf;": "\xaa", + "ordm": "\xba", + "ordm;": "\xba", + "origof;": "\u22b6", + "oror;": "\u2a56", + "orslope;": "\u2a57", + "orv;": "\u2a5b", + "oscr;": "\u2134", + "oslash": "\xf8", + "oslash;": "\xf8", + "osol;": "\u2298", + "otilde": "\xf5", + "otilde;": "\xf5", + "otimes;": "\u2297", + "otimesas;": "\u2a36", + "ouml": "\xf6", + "ouml;": "\xf6", + "ovbar;": "\u233d", + "par;": "\u2225", + "para": "\xb6", + "para;": "\xb6", + "parallel;": "\u2225", + "parsim;": "\u2af3", + "parsl;": "\u2afd", + "part;": "\u2202", + "pcy;": "\u043f", + "percnt;": "%", + "period;": ".", + "permil;": "\u2030", + "perp;": "\u22a5", + "pertenk;": "\u2031", + "pfr;": "\U0001d52d", + "phi;": "\u03c6", + "phiv;": "\u03d5", + "phmmat;": "\u2133", + "phone;": "\u260e", + "pi;": "\u03c0", + "pitchfork;": "\u22d4", + "piv;": "\u03d6", + "planck;": "\u210f", + "planckh;": "\u210e", + "plankv;": "\u210f", + "plus;": "+", + "plusacir;": "\u2a23", + "plusb;": "\u229e", + "pluscir;": "\u2a22", + "plusdo;": "\u2214", + "plusdu;": "\u2a25", + "pluse;": "\u2a72", + "plusmn": "\xb1", + "plusmn;": "\xb1", + "plussim;": "\u2a26", + "plustwo;": "\u2a27", + "pm;": "\xb1", + "pointint;": "\u2a15", + "popf;": "\U0001d561", + "pound": "\xa3", + "pound;": "\xa3", + "pr;": "\u227a", + "prE;": "\u2ab3", + "prap;": "\u2ab7", + "prcue;": "\u227c", + "pre;": "\u2aaf", + "prec;": "\u227a", + "precapprox;": "\u2ab7", + "preccurlyeq;": "\u227c", + "preceq;": "\u2aaf", + "precnapprox;": "\u2ab9", + "precneqq;": "\u2ab5", + "precnsim;": "\u22e8", + "precsim;": "\u227e", + "prime;": "\u2032", + "primes;": "\u2119", + "prnE;": "\u2ab5", + "prnap;": "\u2ab9", + "prnsim;": "\u22e8", + "prod;": "\u220f", + "profalar;": "\u232e", + "profline;": "\u2312", + "profsurf;": "\u2313", + "prop;": "\u221d", + "propto;": "\u221d", + "prsim;": "\u227e", + "prurel;": "\u22b0", + "pscr;": "\U0001d4c5", + "psi;": "\u03c8", + "puncsp;": "\u2008", + "qfr;": "\U0001d52e", + "qint;": "\u2a0c", + "qopf;": "\U0001d562", + "qprime;": "\u2057", + "qscr;": "\U0001d4c6", + "quaternions;": "\u210d", + "quatint;": "\u2a16", + "quest;": "?", + "questeq;": "\u225f", + "quot": "\"", + "quot;": "\"", + "rAarr;": "\u21db", + "rArr;": "\u21d2", + "rAtail;": "\u291c", + "rBarr;": "\u290f", + "rHar;": "\u2964", + "race;": "\u223d\u0331", + "racute;": "\u0155", + "radic;": "\u221a", + "raemptyv;": "\u29b3", + "rang;": "\u27e9", + "rangd;": "\u2992", + "range;": "\u29a5", + "rangle;": "\u27e9", + "raquo": "\xbb", + "raquo;": "\xbb", + "rarr;": "\u2192", + "rarrap;": "\u2975", + "rarrb;": "\u21e5", + "rarrbfs;": "\u2920", + "rarrc;": "\u2933", + "rarrfs;": "\u291e", + "rarrhk;": "\u21aa", + "rarrlp;": "\u21ac", + "rarrpl;": "\u2945", + "rarrsim;": "\u2974", + "rarrtl;": "\u21a3", + "rarrw;": "\u219d", + "ratail;": "\u291a", + "ratio;": "\u2236", + "rationals;": "\u211a", + "rbarr;": "\u290d", + "rbbrk;": "\u2773", + "rbrace;": "}", + "rbrack;": "]", + "rbrke;": "\u298c", + "rbrksld;": "\u298e", + "rbrkslu;": "\u2990", + "rcaron;": "\u0159", + "rcedil;": "\u0157", + "rceil;": "\u2309", + "rcub;": "}", + "rcy;": "\u0440", + "rdca;": "\u2937", + "rdldhar;": "\u2969", + "rdquo;": "\u201d", + "rdquor;": "\u201d", + "rdsh;": "\u21b3", + "real;": "\u211c", + "realine;": "\u211b", + "realpart;": "\u211c", + "reals;": "\u211d", + "rect;": "\u25ad", + "reg": "\xae", + "reg;": "\xae", + "rfisht;": "\u297d", + "rfloor;": "\u230b", + "rfr;": "\U0001d52f", + "rhard;": "\u21c1", + "rharu;": "\u21c0", + "rharul;": "\u296c", + "rho;": "\u03c1", + "rhov;": "\u03f1", + "rightarrow;": "\u2192", + "rightarrowtail;": "\u21a3", + "rightharpoondown;": "\u21c1", + "rightharpoonup;": "\u21c0", + "rightleftarrows;": "\u21c4", + "rightleftharpoons;": "\u21cc", + "rightrightarrows;": "\u21c9", + "rightsquigarrow;": "\u219d", + "rightthreetimes;": "\u22cc", + "ring;": "\u02da", + "risingdotseq;": "\u2253", + "rlarr;": "\u21c4", + "rlhar;": "\u21cc", + "rlm;": "\u200f", + "rmoust;": "\u23b1", + "rmoustache;": "\u23b1", + "rnmid;": "\u2aee", + "roang;": "\u27ed", + "roarr;": "\u21fe", + "robrk;": "\u27e7", + "ropar;": "\u2986", + "ropf;": "\U0001d563", + "roplus;": "\u2a2e", + "rotimes;": "\u2a35", + "rpar;": ")", + "rpargt;": "\u2994", + "rppolint;": "\u2a12", + "rrarr;": "\u21c9", + "rsaquo;": "\u203a", + "rscr;": "\U0001d4c7", + "rsh;": "\u21b1", + "rsqb;": "]", + "rsquo;": "\u2019", + "rsquor;": "\u2019", + "rthree;": "\u22cc", + "rtimes;": "\u22ca", + "rtri;": "\u25b9", + "rtrie;": "\u22b5", + "rtrif;": "\u25b8", + "rtriltri;": "\u29ce", + "ruluhar;": "\u2968", + "rx;": "\u211e", + "sacute;": "\u015b", + "sbquo;": "\u201a", + "sc;": "\u227b", + "scE;": "\u2ab4", + "scap;": "\u2ab8", + "scaron;": "\u0161", + "sccue;": "\u227d", + "sce;": "\u2ab0", + "scedil;": "\u015f", + "scirc;": "\u015d", + "scnE;": "\u2ab6", + "scnap;": "\u2aba", + "scnsim;": "\u22e9", + "scpolint;": "\u2a13", + "scsim;": "\u227f", + "scy;": "\u0441", + "sdot;": "\u22c5", + "sdotb;": "\u22a1", + "sdote;": "\u2a66", + "seArr;": "\u21d8", + "searhk;": "\u2925", + "searr;": "\u2198", + "searrow;": "\u2198", + "sect": "\xa7", + "sect;": "\xa7", + "semi;": ";", + "seswar;": "\u2929", + "setminus;": "\u2216", + "setmn;": "\u2216", + "sext;": "\u2736", + "sfr;": "\U0001d530", + "sfrown;": "\u2322", + "sharp;": "\u266f", + "shchcy;": "\u0449", + "shcy;": "\u0448", + "shortmid;": "\u2223", + "shortparallel;": "\u2225", + "shy": "\xad", + "shy;": "\xad", + "sigma;": "\u03c3", + "sigmaf;": "\u03c2", + "sigmav;": "\u03c2", + "sim;": "\u223c", + "simdot;": "\u2a6a", + "sime;": "\u2243", + "simeq;": "\u2243", + "simg;": "\u2a9e", + "simgE;": "\u2aa0", + "siml;": "\u2a9d", + "simlE;": "\u2a9f", + "simne;": "\u2246", + "simplus;": "\u2a24", + "simrarr;": "\u2972", + "slarr;": "\u2190", + "smallsetminus;": "\u2216", + "smashp;": "\u2a33", + "smeparsl;": "\u29e4", + "smid;": "\u2223", + "smile;": "\u2323", + "smt;": "\u2aaa", + "smte;": "\u2aac", + "smtes;": "\u2aac\ufe00", + "softcy;": "\u044c", + "sol;": "/", + "solb;": "\u29c4", + "solbar;": "\u233f", + "sopf;": "\U0001d564", + "spades;": "\u2660", + "spadesuit;": "\u2660", + "spar;": "\u2225", + "sqcap;": "\u2293", + "sqcaps;": "\u2293\ufe00", + "sqcup;": "\u2294", + "sqcups;": "\u2294\ufe00", + "sqsub;": "\u228f", + "sqsube;": "\u2291", + "sqsubset;": "\u228f", + "sqsubseteq;": "\u2291", + "sqsup;": "\u2290", + "sqsupe;": "\u2292", + "sqsupset;": "\u2290", + "sqsupseteq;": "\u2292", + "squ;": "\u25a1", + "square;": "\u25a1", + "squarf;": "\u25aa", + "squf;": "\u25aa", + "srarr;": "\u2192", + "sscr;": "\U0001d4c8", + "ssetmn;": "\u2216", + "ssmile;": "\u2323", + "sstarf;": "\u22c6", + "star;": "\u2606", + "starf;": "\u2605", + "straightepsilon;": "\u03f5", + "straightphi;": "\u03d5", + "strns;": "\xaf", + "sub;": "\u2282", + "subE;": "\u2ac5", + "subdot;": "\u2abd", + "sube;": "\u2286", + "subedot;": "\u2ac3", + "submult;": "\u2ac1", + "subnE;": "\u2acb", + "subne;": "\u228a", + "subplus;": "\u2abf", + "subrarr;": "\u2979", + "subset;": "\u2282", + "subseteq;": "\u2286", + "subseteqq;": "\u2ac5", + "subsetneq;": "\u228a", + "subsetneqq;": "\u2acb", + "subsim;": "\u2ac7", + "subsub;": "\u2ad5", + "subsup;": "\u2ad3", + "succ;": "\u227b", + "succapprox;": "\u2ab8", + "succcurlyeq;": "\u227d", + "succeq;": "\u2ab0", + "succnapprox;": "\u2aba", + "succneqq;": "\u2ab6", + "succnsim;": "\u22e9", + "succsim;": "\u227f", + "sum;": "\u2211", + "sung;": "\u266a", + "sup1": "\xb9", + "sup1;": "\xb9", + "sup2": "\xb2", + "sup2;": "\xb2", + "sup3": "\xb3", + "sup3;": "\xb3", + "sup;": "\u2283", + "supE;": "\u2ac6", + "supdot;": "\u2abe", + "supdsub;": "\u2ad8", + "supe;": "\u2287", + "supedot;": "\u2ac4", + "suphsol;": "\u27c9", + "suphsub;": "\u2ad7", + "suplarr;": "\u297b", + "supmult;": "\u2ac2", + "supnE;": "\u2acc", + "supne;": "\u228b", + "supplus;": "\u2ac0", + "supset;": "\u2283", + "supseteq;": "\u2287", + "supseteqq;": "\u2ac6", + "supsetneq;": "\u228b", + "supsetneqq;": "\u2acc", + "supsim;": "\u2ac8", + "supsub;": "\u2ad4", + "supsup;": "\u2ad6", + "swArr;": "\u21d9", + "swarhk;": "\u2926", + "swarr;": "\u2199", + "swarrow;": "\u2199", + "swnwar;": "\u292a", + "szlig": "\xdf", + "szlig;": "\xdf", + "target;": "\u2316", + "tau;": "\u03c4", + "tbrk;": "\u23b4", + "tcaron;": "\u0165", + "tcedil;": "\u0163", + "tcy;": "\u0442", + "tdot;": "\u20db", + "telrec;": "\u2315", + "tfr;": "\U0001d531", + "there4;": "\u2234", + "therefore;": "\u2234", + "theta;": "\u03b8", + "thetasym;": "\u03d1", + "thetav;": "\u03d1", + "thickapprox;": "\u2248", + "thicksim;": "\u223c", + "thinsp;": "\u2009", + "thkap;": "\u2248", + "thksim;": "\u223c", + "thorn": "\xfe", + "thorn;": "\xfe", + "tilde;": "\u02dc", + "times": "\xd7", + "times;": "\xd7", + "timesb;": "\u22a0", + "timesbar;": "\u2a31", + "timesd;": "\u2a30", + "tint;": "\u222d", + "toea;": "\u2928", + "top;": "\u22a4", + "topbot;": "\u2336", + "topcir;": "\u2af1", + "topf;": "\U0001d565", + "topfork;": "\u2ada", + "tosa;": "\u2929", + "tprime;": "\u2034", + "trade;": "\u2122", + "triangle;": "\u25b5", + "triangledown;": "\u25bf", + "triangleleft;": "\u25c3", + "trianglelefteq;": "\u22b4", + "triangleq;": "\u225c", + "triangleright;": "\u25b9", + "trianglerighteq;": "\u22b5", + "tridot;": "\u25ec", + "trie;": "\u225c", + "triminus;": "\u2a3a", + "triplus;": "\u2a39", + "trisb;": "\u29cd", + "tritime;": "\u2a3b", + "trpezium;": "\u23e2", + "tscr;": "\U0001d4c9", + "tscy;": "\u0446", + "tshcy;": "\u045b", + "tstrok;": "\u0167", + "twixt;": "\u226c", + "twoheadleftarrow;": "\u219e", + "twoheadrightarrow;": "\u21a0", + "uArr;": "\u21d1", + "uHar;": "\u2963", + "uacute": "\xfa", + "uacute;": "\xfa", + "uarr;": "\u2191", + "ubrcy;": "\u045e", + "ubreve;": "\u016d", + "ucirc": "\xfb", + "ucirc;": "\xfb", + "ucy;": "\u0443", + "udarr;": "\u21c5", + "udblac;": "\u0171", + "udhar;": "\u296e", + "ufisht;": "\u297e", + "ufr;": "\U0001d532", + "ugrave": "\xf9", + "ugrave;": "\xf9", + "uharl;": "\u21bf", + "uharr;": "\u21be", + "uhblk;": "\u2580", + "ulcorn;": "\u231c", + "ulcorner;": "\u231c", + "ulcrop;": "\u230f", + "ultri;": "\u25f8", + "umacr;": "\u016b", + "uml": "\xa8", + "uml;": "\xa8", + "uogon;": "\u0173", + "uopf;": "\U0001d566", + "uparrow;": "\u2191", + "updownarrow;": "\u2195", + "upharpoonleft;": "\u21bf", + "upharpoonright;": "\u21be", + "uplus;": "\u228e", + "upsi;": "\u03c5", + "upsih;": "\u03d2", + "upsilon;": "\u03c5", + "upuparrows;": "\u21c8", + "urcorn;": "\u231d", + "urcorner;": "\u231d", + "urcrop;": "\u230e", + "uring;": "\u016f", + "urtri;": "\u25f9", + "uscr;": "\U0001d4ca", + "utdot;": "\u22f0", + "utilde;": "\u0169", + "utri;": "\u25b5", + "utrif;": "\u25b4", + "uuarr;": "\u21c8", + "uuml": "\xfc", + "uuml;": "\xfc", + "uwangle;": "\u29a7", + "vArr;": "\u21d5", + "vBar;": "\u2ae8", + "vBarv;": "\u2ae9", + "vDash;": "\u22a8", + "vangrt;": "\u299c", + "varepsilon;": "\u03f5", + "varkappa;": "\u03f0", + "varnothing;": "\u2205", + "varphi;": "\u03d5", + "varpi;": "\u03d6", + "varpropto;": "\u221d", + "varr;": "\u2195", + "varrho;": "\u03f1", + "varsigma;": "\u03c2", + "varsubsetneq;": "\u228a\ufe00", + "varsubsetneqq;": "\u2acb\ufe00", + "varsupsetneq;": "\u228b\ufe00", + "varsupsetneqq;": "\u2acc\ufe00", + "vartheta;": "\u03d1", + "vartriangleleft;": "\u22b2", + "vartriangleright;": "\u22b3", + "vcy;": "\u0432", + "vdash;": "\u22a2", + "vee;": "\u2228", + "veebar;": "\u22bb", + "veeeq;": "\u225a", + "vellip;": "\u22ee", + "verbar;": "|", + "vert;": "|", + "vfr;": "\U0001d533", + "vltri;": "\u22b2", + "vnsub;": "\u2282\u20d2", + "vnsup;": "\u2283\u20d2", + "vopf;": "\U0001d567", + "vprop;": "\u221d", + "vrtri;": "\u22b3", + "vscr;": "\U0001d4cb", + "vsubnE;": "\u2acb\ufe00", + "vsubne;": "\u228a\ufe00", + "vsupnE;": "\u2acc\ufe00", + "vsupne;": "\u228b\ufe00", + "vzigzag;": "\u299a", + "wcirc;": "\u0175", + "wedbar;": "\u2a5f", + "wedge;": "\u2227", + "wedgeq;": "\u2259", + "weierp;": "\u2118", + "wfr;": "\U0001d534", + "wopf;": "\U0001d568", + "wp;": "\u2118", + "wr;": "\u2240", + "wreath;": "\u2240", + "wscr;": "\U0001d4cc", + "xcap;": "\u22c2", + "xcirc;": "\u25ef", + "xcup;": "\u22c3", + "xdtri;": "\u25bd", + "xfr;": "\U0001d535", + "xhArr;": "\u27fa", + "xharr;": "\u27f7", + "xi;": "\u03be", + "xlArr;": "\u27f8", + "xlarr;": "\u27f5", + "xmap;": "\u27fc", + "xnis;": "\u22fb", + "xodot;": "\u2a00", + "xopf;": "\U0001d569", + "xoplus;": "\u2a01", + "xotime;": "\u2a02", + "xrArr;": "\u27f9", + "xrarr;": "\u27f6", + "xscr;": "\U0001d4cd", + "xsqcup;": "\u2a06", + "xuplus;": "\u2a04", + "xutri;": "\u25b3", + "xvee;": "\u22c1", + "xwedge;": "\u22c0", + "yacute": "\xfd", + "yacute;": "\xfd", + "yacy;": "\u044f", + "ycirc;": "\u0177", + "ycy;": "\u044b", + "yen": "\xa5", + "yen;": "\xa5", + "yfr;": "\U0001d536", + "yicy;": "\u0457", + "yopf;": "\U0001d56a", + "yscr;": "\U0001d4ce", + "yucy;": "\u044e", + "yuml": "\xff", + "yuml;": "\xff", + "zacute;": "\u017a", + "zcaron;": "\u017e", + "zcy;": "\u0437", + "zdot;": "\u017c", + "zeetrf;": "\u2128", + "zeta;": "\u03b6", + "zfr;": "\U0001d537", + "zhcy;": "\u0436", + "zigrarr;": "\u21dd", + "zopf;": "\U0001d56b", + "zscr;": "\U0001d4cf", + "zwj;": "\u200d", + "zwnj;": "\u200c", +} + +replacementCharacters = { + 0x0: "\uFFFD", + 0x0d: "\u000D", + 0x80: "\u20AC", + 0x81: "\u0081", + 0x81: "\u0081", + 0x82: "\u201A", + 0x83: "\u0192", + 0x84: "\u201E", + 0x85: "\u2026", + 0x86: "\u2020", + 0x87: "\u2021", + 0x88: "\u02C6", + 0x89: "\u2030", + 0x8A: "\u0160", + 0x8B: "\u2039", + 0x8C: "\u0152", + 0x8D: "\u008D", + 0x8E: "\u017D", + 0x8F: "\u008F", + 0x90: "\u0090", + 0x91: "\u2018", + 0x92: "\u2019", + 0x93: "\u201C", + 0x94: "\u201D", + 0x95: "\u2022", + 0x96: "\u2013", + 0x97: "\u2014", + 0x98: "\u02DC", + 0x99: "\u2122", + 0x9A: "\u0161", + 0x9B: "\u203A", + 0x9C: "\u0153", + 0x9D: "\u009D", + 0x9E: "\u017E", + 0x9F: "\u0178", +} + +encodings = { + '437': 'cp437', + '850': 'cp850', + '852': 'cp852', + '855': 'cp855', + '857': 'cp857', + '860': 'cp860', + '861': 'cp861', + '862': 'cp862', + '863': 'cp863', + '865': 'cp865', + '866': 'cp866', + '869': 'cp869', + 'ansix341968': 'ascii', + 'ansix341986': 'ascii', + 'arabic': 'iso8859-6', + 'ascii': 'ascii', + 'asmo708': 'iso8859-6', + 'big5': 'big5', + 'big5hkscs': 'big5hkscs', + 'chinese': 'gbk', + 'cp037': 'cp037', + 'cp1026': 'cp1026', + 'cp154': 'ptcp154', + 'cp367': 'ascii', + 'cp424': 'cp424', + 'cp437': 'cp437', + 'cp500': 'cp500', + 'cp775': 'cp775', + 'cp819': 'windows-1252', + 'cp850': 'cp850', + 'cp852': 'cp852', + 'cp855': 'cp855', + 'cp857': 'cp857', + 'cp860': 'cp860', + 'cp861': 'cp861', + 'cp862': 'cp862', + 'cp863': 'cp863', + 'cp864': 'cp864', + 'cp865': 'cp865', + 'cp866': 'cp866', + 'cp869': 'cp869', + 'cp936': 'gbk', + 'cpgr': 'cp869', + 'cpis': 'cp861', + 'csascii': 'ascii', + 'csbig5': 'big5', + 'cseuckr': 'cp949', + 'cseucpkdfmtjapanese': 'euc_jp', + 'csgb2312': 'gbk', + 'cshproman8': 'hp-roman8', + 'csibm037': 'cp037', + 'csibm1026': 'cp1026', + 'csibm424': 'cp424', + 'csibm500': 'cp500', + 'csibm855': 'cp855', + 'csibm857': 'cp857', + 'csibm860': 'cp860', + 'csibm861': 'cp861', + 'csibm863': 'cp863', + 'csibm864': 'cp864', + 'csibm865': 'cp865', + 'csibm866': 'cp866', + 'csibm869': 'cp869', + 'csiso2022jp': 'iso2022_jp', + 'csiso2022jp2': 'iso2022_jp_2', + 'csiso2022kr': 'iso2022_kr', + 'csiso58gb231280': 'gbk', + 'csisolatin1': 'windows-1252', + 'csisolatin2': 'iso8859-2', + 'csisolatin3': 'iso8859-3', + 'csisolatin4': 'iso8859-4', + 'csisolatin5': 'windows-1254', + 'csisolatin6': 'iso8859-10', + 'csisolatinarabic': 'iso8859-6', + 'csisolatincyrillic': 'iso8859-5', + 'csisolatingreek': 'iso8859-7', + 'csisolatinhebrew': 'iso8859-8', + 'cskoi8r': 'koi8-r', + 'csksc56011987': 'cp949', + 'cspc775baltic': 'cp775', + 'cspc850multilingual': 'cp850', + 'cspc862latinhebrew': 'cp862', + 'cspc8codepage437': 'cp437', + 'cspcp852': 'cp852', + 'csptcp154': 'ptcp154', + 'csshiftjis': 'shift_jis', + 'csunicode11utf7': 'utf-7', + 'cyrillic': 'iso8859-5', + 'cyrillicasian': 'ptcp154', + 'ebcdiccpbe': 'cp500', + 'ebcdiccpca': 'cp037', + 'ebcdiccpch': 'cp500', + 'ebcdiccphe': 'cp424', + 'ebcdiccpnl': 'cp037', + 'ebcdiccpus': 'cp037', + 'ebcdiccpwt': 'cp037', + 'ecma114': 'iso8859-6', + 'ecma118': 'iso8859-7', + 'elot928': 'iso8859-7', + 'eucjp': 'euc_jp', + 'euckr': 'cp949', + 'extendedunixcodepackedformatforjapanese': 'euc_jp', + 'gb18030': 'gb18030', + 'gb2312': 'gbk', + 'gb231280': 'gbk', + 'gbk': 'gbk', + 'greek': 'iso8859-7', + 'greek8': 'iso8859-7', + 'hebrew': 'iso8859-8', + 'hproman8': 'hp-roman8', + 'hzgb2312': 'hz', + 'ibm037': 'cp037', + 'ibm1026': 'cp1026', + 'ibm367': 'ascii', + 'ibm424': 'cp424', + 'ibm437': 'cp437', + 'ibm500': 'cp500', + 'ibm775': 'cp775', + 'ibm819': 'windows-1252', + 'ibm850': 'cp850', + 'ibm852': 'cp852', + 'ibm855': 'cp855', + 'ibm857': 'cp857', + 'ibm860': 'cp860', + 'ibm861': 'cp861', + 'ibm862': 'cp862', + 'ibm863': 'cp863', + 'ibm864': 'cp864', + 'ibm865': 'cp865', + 'ibm866': 'cp866', + 'ibm869': 'cp869', + 'iso2022jp': 'iso2022_jp', + 'iso2022jp2': 'iso2022_jp_2', + 'iso2022kr': 'iso2022_kr', + 'iso646irv1991': 'ascii', + 'iso646us': 'ascii', + 'iso88591': 'windows-1252', + 'iso885910': 'iso8859-10', + 'iso8859101992': 'iso8859-10', + 'iso885911987': 'windows-1252', + 'iso885913': 'iso8859-13', + 'iso885914': 'iso8859-14', + 'iso8859141998': 'iso8859-14', + 'iso885915': 'iso8859-15', + 'iso885916': 'iso8859-16', + 'iso8859162001': 'iso8859-16', + 'iso88592': 'iso8859-2', + 'iso885921987': 'iso8859-2', + 'iso88593': 'iso8859-3', + 'iso885931988': 'iso8859-3', + 'iso88594': 'iso8859-4', + 'iso885941988': 'iso8859-4', + 'iso88595': 'iso8859-5', + 'iso885951988': 'iso8859-5', + 'iso88596': 'iso8859-6', + 'iso885961987': 'iso8859-6', + 'iso88597': 'iso8859-7', + 'iso885971987': 'iso8859-7', + 'iso88598': 'iso8859-8', + 'iso885981988': 'iso8859-8', + 'iso88599': 'windows-1254', + 'iso885991989': 'windows-1254', + 'isoceltic': 'iso8859-14', + 'isoir100': 'windows-1252', + 'isoir101': 'iso8859-2', + 'isoir109': 'iso8859-3', + 'isoir110': 'iso8859-4', + 'isoir126': 'iso8859-7', + 'isoir127': 'iso8859-6', + 'isoir138': 'iso8859-8', + 'isoir144': 'iso8859-5', + 'isoir148': 'windows-1254', + 'isoir149': 'cp949', + 'isoir157': 'iso8859-10', + 'isoir199': 'iso8859-14', + 'isoir226': 'iso8859-16', + 'isoir58': 'gbk', + 'isoir6': 'ascii', + 'koi8r': 'koi8-r', + 'koi8u': 'koi8-u', + 'korean': 'cp949', + 'ksc5601': 'cp949', + 'ksc56011987': 'cp949', + 'ksc56011989': 'cp949', + 'l1': 'windows-1252', + 'l10': 'iso8859-16', + 'l2': 'iso8859-2', + 'l3': 'iso8859-3', + 'l4': 'iso8859-4', + 'l5': 'windows-1254', + 'l6': 'iso8859-10', + 'l8': 'iso8859-14', + 'latin1': 'windows-1252', + 'latin10': 'iso8859-16', + 'latin2': 'iso8859-2', + 'latin3': 'iso8859-3', + 'latin4': 'iso8859-4', + 'latin5': 'windows-1254', + 'latin6': 'iso8859-10', + 'latin8': 'iso8859-14', + 'latin9': 'iso8859-15', + 'ms936': 'gbk', + 'mskanji': 'shift_jis', + 'pt154': 'ptcp154', + 'ptcp154': 'ptcp154', + 'r8': 'hp-roman8', + 'roman8': 'hp-roman8', + 'shiftjis': 'shift_jis', + 'tis620': 'cp874', + 'unicode11utf7': 'utf-7', + 'us': 'ascii', + 'usascii': 'ascii', + 'utf16': 'utf-16', + 'utf16be': 'utf-16-be', + 'utf16le': 'utf-16-le', + 'utf8': 'utf-8', + 'windows1250': 'cp1250', + 'windows1251': 'cp1251', + 'windows1252': 'cp1252', + 'windows1253': 'cp1253', + 'windows1254': 'cp1254', + 'windows1255': 'cp1255', + 'windows1256': 'cp1256', + 'windows1257': 'cp1257', + 'windows1258': 'cp1258', + 'windows936': 'gbk', + 'x-x-big5': 'big5'} + +tokenTypes = { + "Doctype": 0, + "Characters": 1, + "SpaceCharacters": 2, + "StartTag": 3, + "EndTag": 4, + "EmptyTag": 5, + "Comment": 6, + "ParseError": 7 +} + +tagTokenTypes = frozenset((tokenTypes["StartTag"], tokenTypes["EndTag"], + tokenTypes["EmptyTag"])) + + +prefixes = dict([(v, k) for k, v in namespaces.items()]) +prefixes["http://www.w3.org/1998/Math/MathML"] = "math" + + +class DataLossWarning(UserWarning): + pass + + +class ReparseException(Exception): + pass diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/__init__.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/_base.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/_base.py new file mode 100644 index 0000000..c7dbaed --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/_base.py @@ -0,0 +1,12 @@ +from __future__ import absolute_import, division, unicode_literals + + +class Filter(object): + def __init__(self, source): + self.source = source + + def __iter__(self): + return iter(self.source) + + def __getattr__(self, name): + return getattr(self.source, name) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py new file mode 100644 index 0000000..fed6996 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py @@ -0,0 +1,20 @@ +from __future__ import absolute_import, division, unicode_literals + +from . import _base + +try: + from collections import OrderedDict +except ImportError: + from ordereddict import OrderedDict + + +class Filter(_base.Filter): + def __iter__(self): + for token in _base.Filter.__iter__(self): + if token["type"] in ("StartTag", "EmptyTag"): + attrs = OrderedDict() + for name, value in sorted(token["data"].items(), + key=lambda x: x[0]): + attrs[name] = value + token["data"] = attrs + yield token diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py new file mode 100644 index 0000000..ca33b70 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py @@ -0,0 +1,65 @@ +from __future__ import absolute_import, division, unicode_literals + +from . import _base + + +class Filter(_base.Filter): + def __init__(self, source, encoding): + _base.Filter.__init__(self, source) + self.encoding = encoding + + def __iter__(self): + state = "pre_head" + meta_found = (self.encoding is None) + pending = [] + + for token in _base.Filter.__iter__(self): + type = token["type"] + if type == "StartTag": + if token["name"].lower() == "head": + state = "in_head" + + elif type == "EmptyTag": + if token["name"].lower() == "meta": + # replace charset with actual encoding + has_http_equiv_content_type = False + for (namespace, name), value in token["data"].items(): + if namespace is not None: + continue + elif name.lower() == 'charset': + token["data"][(namespace, name)] = self.encoding + meta_found = True + break + elif name == 'http-equiv' and value.lower() == 'content-type': + has_http_equiv_content_type = True + else: + if has_http_equiv_content_type and (None, "content") in token["data"]: + token["data"][(None, "content")] = 'text/html; charset=%s' % self.encoding + meta_found = True + + elif token["name"].lower() == "head" and not meta_found: + # insert meta into empty head + yield {"type": "StartTag", "name": "head", + "data": token["data"]} + yield {"type": "EmptyTag", "name": "meta", + "data": {(None, "charset"): self.encoding}} + yield {"type": "EndTag", "name": "head"} + meta_found = True + continue + + elif type == "EndTag": + if token["name"].lower() == "head" and pending: + # insert meta into head (if necessary) and flush pending queue + yield pending.pop(0) + if not meta_found: + yield {"type": "EmptyTag", "name": "meta", + "data": {(None, "charset"): self.encoding}} + while pending: + yield pending.pop(0) + meta_found = True + state = "post_head" + + if state == "in_head": + pending.append(token) + else: + yield token diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/lint.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/lint.py new file mode 100644 index 0000000..83ad639 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/lint.py @@ -0,0 +1,93 @@ +from __future__ import absolute_import, division, unicode_literals + +from gettext import gettext +_ = gettext + +from . import _base +from ..constants import cdataElements, rcdataElements, voidElements + +from ..constants import spaceCharacters +spaceCharacters = "".join(spaceCharacters) + + +class LintError(Exception): + pass + + +class Filter(_base.Filter): + def __iter__(self): + open_elements = [] + contentModelFlag = "PCDATA" + for token in _base.Filter.__iter__(self): + type = token["type"] + if type in ("StartTag", "EmptyTag"): + name = token["name"] + if contentModelFlag != "PCDATA": + raise LintError(_("StartTag not in PCDATA content model flag: %s") % name) + if not isinstance(name, str): + raise LintError(_("Tag name is not a string: %r") % name) + if not name: + raise LintError(_("Empty tag name")) + if type == "StartTag" and name in voidElements: + raise LintError(_("Void element reported as StartTag token: %s") % name) + elif type == "EmptyTag" and name not in voidElements: + raise LintError(_("Non-void element reported as EmptyTag token: %s") % token["name"]) + if type == "StartTag": + open_elements.append(name) + for name, value in token["data"]: + if not isinstance(name, str): + raise LintError(_("Attribute name is not a string: %r") % name) + if not name: + raise LintError(_("Empty attribute name")) + if not isinstance(value, str): + raise LintError(_("Attribute value is not a string: %r") % value) + if name in cdataElements: + contentModelFlag = "CDATA" + elif name in rcdataElements: + contentModelFlag = "RCDATA" + elif name == "plaintext": + contentModelFlag = "PLAINTEXT" + + elif type == "EndTag": + name = token["name"] + if not isinstance(name, str): + raise LintError(_("Tag name is not a string: %r") % name) + if not name: + raise LintError(_("Empty tag name")) + if name in voidElements: + raise LintError(_("Void element reported as EndTag token: %s") % name) + start_name = open_elements.pop() + if start_name != name: + raise LintError(_("EndTag (%s) does not match StartTag (%s)") % (name, start_name)) + contentModelFlag = "PCDATA" + + elif type == "Comment": + if contentModelFlag != "PCDATA": + raise LintError(_("Comment not in PCDATA content model flag")) + + elif type in ("Characters", "SpaceCharacters"): + data = token["data"] + if not isinstance(data, str): + raise LintError(_("Attribute name is not a string: %r") % data) + if not data: + raise LintError(_("%s token with empty data") % type) + if type == "SpaceCharacters": + data = data.strip(spaceCharacters) + if data: + raise LintError(_("Non-space character(s) found in SpaceCharacters token: ") % data) + + elif type == "Doctype": + name = token["name"] + if contentModelFlag != "PCDATA": + raise LintError(_("Doctype not in PCDATA content model flag: %s") % name) + if not isinstance(name, str): + raise LintError(_("Tag name is not a string: %r") % name) + # XXX: what to do with token["data"] ? + + elif type in ("ParseError", "SerializeError"): + pass + + else: + raise LintError(_("Unknown token type: %s") % type) + + yield token diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/optionaltags.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/optionaltags.py new file mode 100644 index 0000000..fefe0b3 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/optionaltags.py @@ -0,0 +1,205 @@ +from __future__ import absolute_import, division, unicode_literals + +from . import _base + + +class Filter(_base.Filter): + def slider(self): + previous1 = previous2 = None + for token in self.source: + if previous1 is not None: + yield previous2, previous1, token + previous2 = previous1 + previous1 = token + yield previous2, previous1, None + + def __iter__(self): + for previous, token, next in self.slider(): + type = token["type"] + if type == "StartTag": + if (token["data"] or + not self.is_optional_start(token["name"], previous, next)): + yield token + elif type == "EndTag": + if not self.is_optional_end(token["name"], next): + yield token + else: + yield token + + def is_optional_start(self, tagname, previous, next): + type = next and next["type"] or None + if tagname in 'html': + # An html element's start tag may be omitted if the first thing + # inside the html element is not a space character or a comment. + return type not in ("Comment", "SpaceCharacters") + elif tagname == 'head': + # A head element's start tag may be omitted if the first thing + # inside the head element is an element. + # XXX: we also omit the start tag if the head element is empty + if type in ("StartTag", "EmptyTag"): + return True + elif type == "EndTag": + return next["name"] == "head" + elif tagname == 'body': + # A body element's start tag may be omitted if the first thing + # inside the body element is not a space character or a comment, + # except if the first thing inside the body element is a script + # or style element and the node immediately preceding the body + # element is a head element whose end tag has been omitted. + if type in ("Comment", "SpaceCharacters"): + return False + elif type == "StartTag": + # XXX: we do not look at the preceding event, so we never omit + # the body element's start tag if it's followed by a script or + # a style element. + return next["name"] not in ('script', 'style') + else: + return True + elif tagname == 'colgroup': + # A colgroup element's start tag may be omitted if the first thing + # inside the colgroup element is a col element, and if the element + # is not immediately preceeded by another colgroup element whose + # end tag has been omitted. + if type in ("StartTag", "EmptyTag"): + # XXX: we do not look at the preceding event, so instead we never + # omit the colgroup element's end tag when it is immediately + # followed by another colgroup element. See is_optional_end. + return next["name"] == "col" + else: + return False + elif tagname == 'tbody': + # A tbody element's start tag may be omitted if the first thing + # inside the tbody element is a tr element, and if the element is + # not immediately preceeded by a tbody, thead, or tfoot element + # whose end tag has been omitted. + if type == "StartTag": + # omit the thead and tfoot elements' end tag when they are + # immediately followed by a tbody element. See is_optional_end. + if previous and previous['type'] == 'EndTag' and \ + previous['name'] in ('tbody', 'thead', 'tfoot'): + return False + return next["name"] == 'tr' + else: + return False + return False + + def is_optional_end(self, tagname, next): + type = next and next["type"] or None + if tagname in ('html', 'head', 'body'): + # An html element's end tag may be omitted if the html element + # is not immediately followed by a space character or a comment. + return type not in ("Comment", "SpaceCharacters") + elif tagname in ('li', 'optgroup', 'tr'): + # A li element's end tag may be omitted if the li element is + # immediately followed by another li element or if there is + # no more content in the parent element. + # An optgroup element's end tag may be omitted if the optgroup + # element is immediately followed by another optgroup element, + # or if there is no more content in the parent element. + # A tr element's end tag may be omitted if the tr element is + # immediately followed by another tr element, or if there is + # no more content in the parent element. + if type == "StartTag": + return next["name"] == tagname + else: + return type == "EndTag" or type is None + elif tagname in ('dt', 'dd'): + # A dt element's end tag may be omitted if the dt element is + # immediately followed by another dt element or a dd element. + # A dd element's end tag may be omitted if the dd element is + # immediately followed by another dd element or a dt element, + # or if there is no more content in the parent element. + if type == "StartTag": + return next["name"] in ('dt', 'dd') + elif tagname == 'dd': + return type == "EndTag" or type is None + else: + return False + elif tagname == 'p': + # A p element's end tag may be omitted if the p element is + # immediately followed by an address, article, aside, + # blockquote, datagrid, dialog, dir, div, dl, fieldset, + # footer, form, h1, h2, h3, h4, h5, h6, header, hr, menu, + # nav, ol, p, pre, section, table, or ul, element, or if + # there is no more content in the parent element. + if type in ("StartTag", "EmptyTag"): + return next["name"] in ('address', 'article', 'aside', + 'blockquote', 'datagrid', 'dialog', + 'dir', 'div', 'dl', 'fieldset', 'footer', + 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', + 'header', 'hr', 'menu', 'nav', 'ol', + 'p', 'pre', 'section', 'table', 'ul') + else: + return type == "EndTag" or type is None + elif tagname == 'option': + # An option element's end tag may be omitted if the option + # element is immediately followed by another option element, + # or if it is immediately followed by an optgroup + # element, or if there is no more content in the parent + # element. + if type == "StartTag": + return next["name"] in ('option', 'optgroup') + else: + return type == "EndTag" or type is None + elif tagname in ('rt', 'rp'): + # An rt element's end tag may be omitted if the rt element is + # immediately followed by an rt or rp element, or if there is + # no more content in the parent element. + # An rp element's end tag may be omitted if the rp element is + # immediately followed by an rt or rp element, or if there is + # no more content in the parent element. + if type == "StartTag": + return next["name"] in ('rt', 'rp') + else: + return type == "EndTag" or type is None + elif tagname == 'colgroup': + # A colgroup element's end tag may be omitted if the colgroup + # element is not immediately followed by a space character or + # a comment. + if type in ("Comment", "SpaceCharacters"): + return False + elif type == "StartTag": + # XXX: we also look for an immediately following colgroup + # element. See is_optional_start. + return next["name"] != 'colgroup' + else: + return True + elif tagname in ('thead', 'tbody'): + # A thead element's end tag may be omitted if the thead element + # is immediately followed by a tbody or tfoot element. + # A tbody element's end tag may be omitted if the tbody element + # is immediately followed by a tbody or tfoot element, or if + # there is no more content in the parent element. + # A tfoot element's end tag may be omitted if the tfoot element + # is immediately followed by a tbody element, or if there is no + # more content in the parent element. + # XXX: we never omit the end tag when the following element is + # a tbody. See is_optional_start. + if type == "StartTag": + return next["name"] in ['tbody', 'tfoot'] + elif tagname == 'tbody': + return type == "EndTag" or type is None + else: + return False + elif tagname == 'tfoot': + # A tfoot element's end tag may be omitted if the tfoot element + # is immediately followed by a tbody element, or if there is no + # more content in the parent element. + # XXX: we never omit the end tag when the following element is + # a tbody. See is_optional_start. + if type == "StartTag": + return next["name"] == 'tbody' + else: + return type == "EndTag" or type is None + elif tagname in ('td', 'th'): + # A td element's end tag may be omitted if the td element is + # immediately followed by a td or th element, or if there is + # no more content in the parent element. + # A th element's end tag may be omitted if the th element is + # immediately followed by a td or th element, or if there is + # no more content in the parent element. + if type == "StartTag": + return next["name"] in ('td', 'th') + else: + return type == "EndTag" or type is None + return False diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/sanitizer.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/sanitizer.py new file mode 100644 index 0000000..b206b54 --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/sanitizer.py @@ -0,0 +1,12 @@ +from __future__ import absolute_import, division, unicode_literals + +from . import _base +from ..sanitizer import HTMLSanitizerMixin + + +class Filter(_base.Filter, HTMLSanitizerMixin): + def __iter__(self): + for token in _base.Filter.__iter__(self): + token = self.sanitize_token(token) + if token: + yield token diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/whitespace.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/whitespace.py new file mode 100644 index 0000000..dfc60ee --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/whitespace.py @@ -0,0 +1,38 @@ +from __future__ import absolute_import, division, unicode_literals + +import re + +from . import _base +from ..constants import rcdataElements, spaceCharacters +spaceCharacters = "".join(spaceCharacters) + +SPACES_REGEX = re.compile("[%s]+" % spaceCharacters) + + +class Filter(_base.Filter): + + spacePreserveElements = frozenset(["pre", "textarea"] + list(rcdataElements)) + + def __iter__(self): + preserve = 0 + for token in _base.Filter.__iter__(self): + type = token["type"] + if type == "StartTag" \ + and (preserve or token["name"] in self.spacePreserveElements): + preserve += 1 + + elif type == "EndTag" and preserve: + preserve -= 1 + + elif not preserve and type == "SpaceCharacters" and token["data"]: + # Test on token["data"] above to not introduce spaces where there were not + token["data"] = " " + + elif not preserve and type == "Characters": + token["data"] = collapse_spaces(token["data"]) + + yield token + + +def collapse_spaces(text): + return SPACES_REGEX.sub(' ', text) diff --git a/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/html5parser.py b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/html5parser.py new file mode 100644 index 0000000..8a5acfe --- /dev/null +++ b/tools/flumotion-config/pusher/lib/python2.7/site-packages/pip/_vendor/html5lib/html5parser.py @@ -0,0 +1,2725 @@ +from __future__ import absolute_import, division, unicode_literals +from pip._vendor.six import with_metaclass + +import types + +from . import inputstream +from . import tokenizer + +from . import treebuilders +from .treebuilders._base import Marker + +from . import utils +from . import constants +from .constants import spaceCharacters, asciiUpper2Lower +from .constants import specialElements +from .constants import headingElements +from .constants import cdataElements, rcdataElements +from .constants import tokenTypes, ReparseException, namespaces +from .constants import htmlIntegrationPointElements, mathmlTextIntegrationPointElements + + +def parse(doc, treebuilder="etree", encoding=None, + namespaceHTMLElements=True): + """Parse a string or file-like object into a tree""" + tb = treebuilders.getTreeBuilder(treebuilder) + p = HTMLParser(tb, namespaceHTMLElements=namespaceHTMLElements) + return p.parse(doc, encoding=encoding) + + +def parseFragment(doc, container="div", treebuilder="etree", encoding=None, + namespaceHTMLElements=True): + tb = treebuilders.getTreeBuilder(treebuilder) + p = HTMLParser(tb, namespaceHTMLElements=namespaceHTMLElements) + return p.parseFragment(doc, container=container, encoding=encoding) + + +def method_decorator_metaclass(function): + class Decorated(type): + def __new__(meta, classname, bases, classDict): + for attributeName, attribute in classDict.items(): + if isinstance(attribute, types.FunctionType): + attribute = function(attribute) + + classDict[attributeName] = attribute + return type.__new__(meta, classname, bases, classDict) + return Decorated + + +class HTMLParser(object): + """HTML parser. Generates a tree structure from a stream of (possibly + malformed) HTML""" + + def __init__(self, tree=None, tokenizer=tokenizer.HTMLTokenizer, + strict=False, namespaceHTMLElements=True, debug=False): + """ + strict - raise an exception when a parse error is encountered + + tree - a treebuilder class controlling the type of tree that will be + returned. Built in treebuilders can be accessed through + html5lib.treebuilders.getTreeBuilder(treeType) + + tokenizer - a class that provides a stream of tokens to the treebuilder. + This may be replaced for e.g. a sanitizer which converts some tags to + text + """ + + # Raise an exception on the first error encountered + self.strict = strict + + if tree is None: + tree = treebuilders.getTreeBuilder("etree") + self.tree = tree(namespaceHTMLElements) + self.tokenizer_class = tokenizer + self.errors = [] + + self.phases = dict([(name, cls(self, self.tree)) for name, cls in + getPhases(debug).items()]) + + def _parse(self, stream, innerHTML=False, container="div", + encoding=None, parseMeta=True, useChardet=True, **kwargs): + + self.innerHTMLMode = innerHTML + self.container = container + self.tokenizer = self.tokenizer_class(stream, encoding=encoding, + parseMeta=parseMeta, + useChardet=useChardet, + parser=self, **kwargs) + self.reset() + + while True: + try: + self.mainLoop() + break + except ReparseException: + self.reset() + + def reset(self): + self.tree.reset() + self.firstStartTag = False + self.errors = [] + self.log = [] # only used with debug mode + # "quirks" / "limited quirks" / "no quirks" + self.compatMode = "no quirks" + + if self.innerHTMLMode: + self.innerHTML = self.container.lower() + + if self.innerHTML in cdataElements: + self.tokenizer.state = self.tokenizer.rcdataState + elif self.innerHTML in rcdataElements: + self.tokenizer.state = self.tokenizer.rawtextState + elif self.innerHTML == 'plaintext': + self.tokenizer.state = self.tokenizer.plaintextState + else: + # state already is data state + # self.tokenizer.state = self.tokenizer.dataState + pass + self.phase = self.phases["beforeHtml"] + self.phase.insertHtmlElement() + self.resetInsertionMode() + else: + self.innerHTML = False + self.phase = self.phases["initial"] + + self.lastPhase = None + + self.beforeRCDataPhase = None + + self.framesetOK = True + + def isHTMLIntegrationPoint(self, element): + if (element.name == "annotation-xml" and + element.namespace == namespaces["mathml"]): + return ("encoding" in element.attributes and + element.attributes["encoding"].translate( + asciiUpper2Lower) in + ("text/html", "application/xhtml+xml")) + else: + return (element.namespace, element.name) in htmlIntegrationPointElements + + def isMathMLTextIntegrationPoint(self, element): + return (element.namespace, element.name) in mathmlTextIntegrationPointElements + + def mainLoop(self): + CharactersToken = tokenTypes["Characters"] + SpaceCharactersToken = tokenTypes["SpaceCharacters"] + StartTagToken = tokenTypes["StartTag"] + EndTagToken = tokenTypes["EndTag"] + CommentToken = tokenTypes["Comment"] + DoctypeToken = tokenTypes["Doctype"] + ParseErrorToken = tokenTypes["ParseError"] + + for token in self.normalizedTokens(): + new_token = token + while new_token is not None: + currentNode = self.tree.openElements[-1] if self.tree.openElements else None + currentNodeNamespace = currentNode.namespace if currentNode else None + currentNodeName = currentNode.name if currentNode else None + + type = new_token["type"] + + if type == ParseErrorToken: + self.parseError(new_token["data"], new_token.get("datavars", {})) + new_token = None + else: + if (len(self.tree.openElements) == 0 or + currentNodeNamespace == self.tree.defaultNamespace or + (self.isMathMLTextIntegrationPoint(currentNode) and + ((type == StartTagToken and + token["name"] not in frozenset(["mglyph", "malignmark"])) or + type in (CharactersToken, SpaceCharactersToken))) or + (currentNodeNamespace == namespaces["mathml"] and + currentNodeName == "annotation-xml" and + token["name"] == "svg") or + (self.isHTMLIntegrationPoint(currentNode) and + type in (StartTagToken, CharactersToken, SpaceCharactersToken))): + phase = self.phase + else: + phase = self.phases["inForeignContent"] + + if type == CharactersToken: + new_token = phase.processCharacters(new_token) + elif type == SpaceCharactersToken: + new_token = phase.processSpaceCharacters(new_token) + elif type == StartTagToken: + new_token = phase.processStartTag(new_token) + elif type == EndTagToken: + new_token = phase.processEndTag(new_token) + elif type == CommentToken: + new_token = phase.processComment(new_token) + elif type == DoctypeToken: + new_token = phase.processDoctype(new_token) + + if (type == StartTagToken and token["selfClosing"] + and not token["selfClosingAcknowledged"]): + self.parseError("non-void-element-with-trailing-solidus", + {"name": token["name"]}) + + # When the loop finishes it's EOF + reprocess = True + phases = [] + while reprocess: + phases.append(self.phase) + reprocess = self.phase.processEOF() + if reprocess: + assert self.phase not in phases + + def normalizedTokens(self): + for token in self.tokenizer: + yield self.normalizeToken(token) + + def parse(self, stream, encoding=None, parseMeta=True, useChardet=True): + """Parse a HTML document into a well-formed tree + + stream - a filelike object or string containing the HTML to be parsed + + The optional encoding parameter must be a string that indicates + the encoding. If specified, that encoding will be used, + regardless of any BOM or later declaration (such as in a meta + element) + """ + self._parse(stream, innerHTML=False, encoding=encoding, + parseMeta=parseMeta, useChardet=useChardet) + return self.tree.getDocument() + + def parseFragment(self, stream, container="div", encoding=None, + parseMeta=False, useChardet=True): + """Parse a HTML fragment into a well-formed tree fragment + + container - name of the element we're setting the innerHTML property + if set to None, default to 'div' + + stream - a filelike object or string containing the HTML to be parsed + + The optional encoding parameter must be a string that indicates + the encoding. If specified, that encoding will be used, + regardless of any BOM or later declaration (such as in a meta + element) + """ + self._parse(stream, True, container=container, encoding=encoding) + return self.tree.getFragment() + + def parseError(self, errorcode="XXX-undefined-error", datavars={}): + # XXX The idea is to make errorcode mandatory. + self.errors.append((self.tokenizer.stream.position(), errorcode, datavars)) + if self.strict: + raise ParseError + + def normalizeToken(self, token): + """ HTML5 specific normalizations to the token stream """ + + if token["type"] == tokenTypes["StartTag"]: + token["data"] = dict(token["data"][::-1]) + + return token + + def adjustMathMLAttributes(self, token): + replacements = {"definitionurl": "definitionURL"} + for k, v in replacements.items(): + if k in token["data"]: + token["data"][v] = token["data"][k] + del token["data"][k] + + def adjustSVGAttributes(self, token): + replacements = { + "attributename": "attributeName", + "attributetype": "attributeType", + "basefrequency": "baseFrequency", + "baseprofile": "baseProfile", + "calcmode": "calcMode", + "clippathunits": "clipPathUnits", + "contentscripttype": "contentScriptType", + "contentstyletype": "contentStyleType", + "diffuseconstant": "diffuseConstant", + "edgemode": "edgeMode", + "externalresourcesrequired": "externalResourcesRequired", + "filterres": "filterRes", + "filterunits": "filterUnits", + "glyphref": "glyphRef", + "gradienttransform": "gradientTransform", + "gradientunits": "gradientUnits", + "kernelmatrix": "kernelMatrix", + "kernelunitlength": "kernelUnitLength", + "keypoints": "keyPoints", + "keysplines": "keySplines", + "keytimes": "keyTimes", + "lengthadjust": "lengthAdjust", + "limitingconeangle": "limitingConeAngle", + "markerheight": "markerHeight", + "markerunits": "markerUnits", + "markerwidth": "markerWidth", + "maskcontentunits": "maskContentUnits", + "maskunits": "maskUnits", + "numoctaves": "numOctaves", + "pathlength": "pathLength", + "patterncontentunits": "patternContentUnits", + "patterntransform": "patternTransform", + "patternunits": "patternUnits", + "pointsatx": "pointsAtX", + "pointsaty": "pointsAtY", + "pointsatz": "pointsAtZ", + "preservealpha": "preserveAlpha", + "preserveaspectratio": "preserveAspectRatio", + "primitiveunits": "primitiveUnits", + "refx": "refX", + "refy": "refY", + "repeatcount": "repeatCount", + "repeatdur": "repeatDur", + "requiredextensions": "requiredExtensions", + "requiredfeatures": "requiredFeatures", + "specularconstant": "specularConstant", + "specularexponent": "specularExponent", + "spreadmethod": "spreadMethod", + "startoffset": "startOffset", + "stddeviation": "stdDeviation", + "stitchtiles": "stitchTiles", + "surfacescale": "surfaceScale", + "systemlanguage": "systemLanguage", + "tablevalues": "tableValues", + "targetx": "targetX", + "targety": "targetY", + "textlength": "textLength", + "viewbox": "viewBox", + "viewtarget": "viewTarget", + "xchannelselector": "xChannelSelector", + "ychannelselector": "yChannelSelector", + "zoomandpan": "zoomAndPan" + } + for originalName in list(token["data"].keys()): + if originalName in replacements: + svgName = replacements[originalName] + token["data"][svgName] = token["data"][originalName] + del token["data"][originalName] + + def adjustForeignAttributes(self, token): + replacements = { + "xlink:actuate": ("xlink", "actuate", namespaces["xlink"]), + "xlink:arcrole": ("xlink", "arcrole", namespaces["xlink"]), + "xlink:href": ("xlink", "href", namespaces["xlink"]), + "xlink:role": ("xlink", "role", namespaces["xlink"]), + "xlink:show": ("xlink", "show", namespaces["xlink"]), + "xlink:title": ("xlink", "title", namespaces["xlink"]), + "xlink:type": ("xlink", "type", namespaces["xlink"]), + "xml:base": ("xml", "base", namespaces["xml"]), + "xml:lang": ("xml", "lang", namespaces["xml"]), + "xml:space": ("xml", "space", namespaces["xml"]), + "xmlns": (None, "xmlns", namespaces["xmlns"]), + "xmlns:xlink": ("xmlns", "xlink", namespaces["xmlns"]) + } + + for originalName in token["data"].keys(): + if originalName in replacements: + foreignName = replacements[originalName] + token["data"][foreignName] = token["data"][originalName] + del token["data"][originalName] + + def reparseTokenNormal(self, token): + self.parser.phase() + + def resetInsertionMode(self): + # The name of this method is mostly historical. (It's also used in the + # specification.) + last = False + newModes = { + "select": "inSelect", + "td": "inCell", + "th": "inCell", + "tr": "inRow", + "tbody": "inTableBody", + "thead": "inTableBody", + "tfoot": "inTableBody", + "caption": "inCaption", + "colgroup": "inColumnGroup", + "table": "inTable", + "head": "inBody", + "body": "inBody", + "frameset": "inFrameset", + "html": "beforeHead" + } + for node in self.tree.openElements[::-1]: + nodeName = node.name + new_phase = None + if node == self.tree.openElements[0]: + assert self.innerHTML + last = True + nodeName = self.innerHTML + # Check for conditions that should only happen in the innerHTML + # case + if nodeName in ("select", "colgroup", "head", "html"): + assert self.innerHTML + + if not last and node.namespace != self.tree.defaultNamespace: + continue + + if nodeName in newModes: + new_phase = self.phases[newModes[nodeName]] + break + elif last: + new_phase = self.phases["inBody"] + break + + self.phase = new_phase + + def parseRCDataRawtext(self, token, contentType): + """Generic RCDATA/RAWTEXT Parsing algorithm + contentType - RCDATA or RAWTEXT + """ + assert contentType in ("RAWTEXT", "RCDATA") + + self.tree.insertElement(token) + + if contentType == "RAWTEXT": + self.tokenizer.state = self.tokenizer.rawtextState + else: + self.tokenizer.state = self.tokenizer.rcdataState + + self.originalPhase = self.phase + + self.phase = self.phases["text"] + + +def getPhases(debug): + def log(function): + """Logger that records which phase processes each token""" + type_names = dict((value, key) for key, value in + constants.tokenTypes.items()) + + def wrapped(self, *args, **kwargs): + if function.__name__.startswith("process") and len(args) > 0: + token = args[0] + try: + info = {"type": type_names[token['type']]} + except: + raise + if token['type'] in constants.tagTokenTypes: + info["name"] = token['name'] + + self.parser.log.append((self.parser.tokenizer.state.__name__, + self.parser.phase.__class__.__name__, + self.__class__.__name__, + function.__name__, + info)) + return function(self, *args, **kwargs) + else: + return function(self, *args, **kwargs) + return wrapped + + def getMetaclass(use_metaclass, metaclass_func): + if use_metaclass: + return method_decorator_metaclass(metaclass_func) + else: + return type + + class Phase(with_metaclass(getMetaclass(debug, log))): + """Base class for helper object that implements each phase of processing + """ + + def __init__(self, parser, tree): + self.parser = parser + self.tree = tree + + def processEOF(self): + raise NotImplementedError + + def processComment(self, token): + # For most phases the following is correct. Where it's not it will be + # overridden. + self.tree.insertComment(token, self.tree.openElements[-1]) + + def processDoctype(self, token): + self.parser.parseError("unexpected-doctype") + + def processCharacters(self, token): + self.tree.insertText(token["data"]) + + def processSpaceCharacters(self, token): + self.tree.insertText(token["data"]) + + def processStartTag(self, token): + return self.startTagHandler[token["name"]](token) + + def startTagHtml(self, token): + if not self.parser.firstStartTag and token["name"] == "html": + self.parser.parseError("non-html-root") + # XXX Need a check here to see if the first start tag token emitted is + # this token... If it's not, invoke self.parser.parseError(). + for attr, value in token["data"].items(): + if attr not in self.tree.openElements[0].attributes: + self.tree.openElements[0].attributes[attr] = value + self.parser.firstStartTag = False + + def processEndTag(self, token): + return self.endTagHandler[token["name"]](token) + + class InitialPhase(Phase): + def processSpaceCharacters(self, token): + pass + + def processComment(self, token): + self.tree.insertComment(token, self.tree.document) + + def processDoctype(self, token): + name = token["name"] + publicId = token["publicId"] + systemId = token["systemId"] + correct = token["correct"] + + if (name != "html" or publicId is not None or + systemId is not None and systemId != "about:legacy-compat"): + self.parser.parseError("unknown-doctype") + + if publicId is None: + publicId = "" + + self.tree.insertDoctype(token) + + if publicId != "": + publicId = publicId.translate(asciiUpper2Lower) + + if (not correct or token["name"] != "html" + or publicId.startswith( + ("+//silmaril//dtd html pro v0r11 19970101//", + "-//advasoft ltd//dtd html 3.0 aswedit + extensions//", + "-//as//dtd html 3.0 aswedit + extensions//", + "-//ietf//dtd html 2.0 level 1//", + "-//ietf//dtd html 2.0 level 2//", + "-//ietf//dtd html 2.0 strict level 1//", + "-//ietf//dtd html 2.0 strict level 2//", + "-//ietf//dtd html 2.0 strict//", + "-//ietf//dtd html 2.0//", + "-//ietf//dtd html 2.1e//", + "-//ietf//dtd html 3.0//", + "-//ietf//dtd html 3.2 final//", + "-//ietf//dtd html 3.2//", + "-//ietf//dtd html 3//", + "-//ietf//dtd html level 0//", + "-//ietf//dtd html level 1//", + "-//ietf//dtd html level 2//", + "-//ietf//dtd html level 3//", + "-//ietf//dtd html strict level 0//", + "-//ietf//dtd html strict level 1//", + "-//ietf//dtd html strict level 2//", + "-//ietf//dtd html strict level 3//", + "-//ietf//dtd html strict//", + "-//ietf//dtd html//", + "-//metrius//dtd metrius presentational//", + "-//microsoft//dtd internet explorer 2.0 html strict//", + "-//microsoft//dtd internet explorer 2.0 html//", + "-//microsoft//dtd internet explorer 2.0 tables//", + "-//microsoft//dtd internet explorer 3.0 html strict//", + "-//microsoft//dtd internet explorer 3.0 html//", + "-//microsoft//dtd internet explorer 3.0 tables//", + "-//netscape comm. corp.//dtd html//", + "-//netscape comm. corp.//dtd strict html//", + "-//o'reilly and associates//dtd html 2.0//", + "-//o'reilly and associates//dtd html extended 1.0//", + "-//o'reilly and associates//dtd html extended relaxed 1.0//", + "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//", + "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//", + "-//spyglass//dtd html 2.0 extended//", + "-//sq//dtd html 2.0 hotmetal + extensions//", + "-//sun microsystems corp.//dtd hotjava html//", + "-//sun microsystems corp.//dtd hotjava strict html//", + "-//w3c//dtd html 3 1995-03-24//", + "-//w3c//dtd html 3.2 draft//", + "-//w3c//dtd html 3.2 final//", + "-//w3c//dtd html 3.2//", + "-//w3c//dtd html 3.2s draft//", + "-//w3c//dtd html 4.0 frameset//", + "-//w3c//dtd html 4.0 transitional//", + "-//w3c//dtd html experimental 19960712//", + "-//w3c//dtd html experimental 970421//", + "-//w3c//dtd w3 html//", + "-//w3o//dtd w3 html 3.0//", + "-//webtechs//dtd mozilla html 2.0//", + "-//webtechs//dtd mozilla html//")) + or publicId in + ("-//w3o//dtd w3 html strict 3.0//en//", + "-/w3c/dtd html 4.0 transitional/en", + "html") + or publicId.startswith( + ("-//w3c//dtd html 4.01 frameset//", + "-//w3c//dtd html 4.01 transitional//")) and + systemId is None + or systemId and systemId.lower() == "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd"): + self.parser.compatMode = "quirks" + elif (publicId.startswith( + ("-//w3c//dtd xhtml 1.0 frameset//", + "-//w3c//dtd xhtml 1.0 transitional//")) + or publicId.startswith( + ("-//w3c//dtd html 4.01 frameset//", + "-//w3c//dtd html 4.01 transitional//")) and + systemId is not None): + self.parser.compatMode = "limited quirks" + + self.parser.phase = self.parser.phases["beforeHtml"] + + def anythingElse(self): + self.parser.compatMode = "quirks" + self.parser.phase = self.parser.phases["beforeHtml"] + + def processCharacters(self, token): + self.parser.parseError("expected-doctype-but-got-chars") + self.anythingElse() + return token + + def processStartTag(self, token): + self.parser.parseError("expected-doctype-but-got-start-tag", + {"name": token["name"]}) + self.anythingElse() + return token + + def processEndTag(self, token): + self.parser.parseError("expected-doctype-but-got-end-tag", + {"name": token["name"]}) + self.anythingElse() + return token + + def processEOF(self): + self.parser.parseError("expected-doctype-but-got-eof") + self.anythingElse() + return True + + class BeforeHtmlPhase(Phase): + # helper methods + def insertHtmlElement(self): + self.tree.insertRoot(impliedTagToken("html", "StartTag")) + self.parser.phase = self.parser.phases["beforeHead"] + + # other + def processEOF(self): + self.insertHtmlElement() + return True + + def processComment(self, token): + self.tree.insertComment(token, self.tree.document) + + def processSpaceCharacters(self, token): + pass + + def processCharacters(self, token): + self.insertHtmlElement() + return token + + def processStartTag(self, token): + if token["name"] == "html": + self.parser.firstStartTag = True + self.insertHtmlElement() + return token + + def processEndTag(self, token): + if token["name"] not in ("head", "body", "html", "br"): + self.parser.parseError("unexpected-end-tag-before-html", + {"name": token["name"]}) + else: + self.insertHtmlElement() + return token + + class BeforeHeadPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("head", self.startTagHead) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = utils.MethodDispatcher([ + (("head", "body", "html", "br"), self.endTagImplyHead) + ]) + self.endTagHandler.default = self.endTagOther + + def processEOF(self): + self.startTagHead(impliedTagToken("head", "StartTag")) + return True + + def processSpaceCharacters(self, token): + pass + + def processCharacters(self, token): + self.startTagHead(impliedTagToken("head", "StartTag")) + return token + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagHead(self, token): + self.tree.insertElement(token) + self.tree.headPointer = self.tree.openElements[-1] + self.parser.phase = self.parser.phases["inHead"] + + def startTagOther(self, token): + self.startTagHead(impliedTagToken("head", "StartTag")) + return token + + def endTagImplyHead(self, token): + self.startTagHead(impliedTagToken("head", "StartTag")) + return token + + def endTagOther(self, token): + self.parser.parseError("end-tag-after-implied-root", + {"name": token["name"]}) + + class InHeadPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("title", self.startTagTitle), + (("noscript", "noframes", "style"), self.startTagNoScriptNoFramesStyle), + ("script", self.startTagScript), + (("base", "basefont", "bgsound", "command", "link"), + self.startTagBaseLinkCommand), + ("meta", self.startTagMeta), + ("head", self.startTagHead) + ]) + self.startTagHandler.default = self.startTagOther + + self. endTagHandler = utils.MethodDispatcher([ + ("head", self.endTagHead), + (("br", "html", "body"), self.endTagHtmlBodyBr) + ]) + self.endTagHandler.default = self.endTagOther + + # the real thing + def processEOF(self): + self.anythingElse() + return True + + def processCharacters(self, token): + self.anythingElse() + return token + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagHead(self, token): + self.parser.parseError("two-heads-are-not-better-than-one") + + def startTagBaseLinkCommand(self, token): + self.tree.insertElement(token) + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + + def startTagMeta(self, token): + self.tree.insertElement(token) + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + + attributes = token["data"] + if self.parser.tokenizer.stream.charEncoding[1] == "tentative": + if "charset" in attributes: + self.parser.tokenizer.stream.changeEncoding(attributes["charset"]) + elif ("content" in attributes and + "http-equiv" in attributes and + attributes["http-equiv"].lower() == "content-type"): + # Encoding it as UTF-8 here is a hack, as really we should pass + # the abstract Unicode string, and just use the + # ContentAttrParser on that, but using UTF-8 allows all chars + # to be encoded and as a ASCII-superset works. + data = inputstream.EncodingBytes(attributes["content"].encode("utf-8")) + parser = inputstream.ContentAttrParser(data) + codec = parser.parse() + self.parser.tokenizer.stream.changeEncoding(codec) + + def startTagTitle(self, token): + self.parser.parseRCDataRawtext(token, "RCDATA") + + def startTagNoScriptNoFramesStyle(self, token): + # Need to decide whether to implement the scripting-disabled case + self.parser.parseRCDataRawtext(token, "RAWTEXT") + + def startTagScript(self, token): + self.tree.insertElement(token) + self.parser.tokenizer.state = self.parser.tokenizer.scriptDataState + self.parser.originalPhase = self.parser.phase + self.parser.phase = self.parser.phases["text"] + + def startTagOther(self, token): + self.anythingElse() + return token + + def endTagHead(self, token): + node = self.parser.tree.openElements.pop() + assert node.name == "head", "Expected head got %s" % node.name + self.parser.phase = self.parser.phases["afterHead"] + + def endTagHtmlBodyBr(self, token): + self.anythingElse() + return token + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def anythingElse(self): + self.endTagHead(impliedTagToken("head")) + + # XXX If we implement a parser for which scripting is disabled we need to + # implement this phase. + # + # class InHeadNoScriptPhase(Phase): + class AfterHeadPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("body", self.startTagBody), + ("frameset", self.startTagFrameset), + (("base", "basefont", "bgsound", "link", "meta", "noframes", "script", + "style", "title"), + self.startTagFromHead), + ("head", self.startTagHead) + ]) + self.startTagHandler.default = self.startTagOther + self.endTagHandler = utils.MethodDispatcher([(("body", "html", "br"), + self.endTagHtmlBodyBr)]) + self.endTagHandler.default = self.endTagOther + + def processEOF(self): + self.anythingElse() + return True + + def processCharacters(self, token): + self.anythingElse() + return token + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagBody(self, token): + self.parser.framesetOK = False + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inBody"] + + def startTagFrameset(self, token): + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inFrameset"] + + def startTagFromHead(self, token): + self.parser.parseError("unexpected-start-tag-out-of-my-head", + {"name": token["name"]}) + self.tree.openElements.append(self.tree.headPointer) + self.parser.phases["inHead"].processStartTag(token) + for node in self.tree.openElements[::-1]: + if node.name == "head": + self.tree.openElements.remove(node) + break + + def startTagHead(self, token): + self.parser.parseError("unexpected-start-tag", {"name": token["name"]}) + + def startTagOther(self, token): + self.anythingElse() + return token + + def endTagHtmlBodyBr(self, token): + self.anythingElse() + return token + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def anythingElse(self): + self.tree.insertElement(impliedTagToken("body", "StartTag")) + self.parser.phase = self.parser.phases["inBody"] + self.parser.framesetOK = True + + class InBodyPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#parsing-main-inbody + # the really-really-really-very crazy mode + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + # Keep a ref to this for special handling of whitespace in
+            self.processSpaceCharactersNonPre = self.processSpaceCharacters
+
+            self.startTagHandler = utils.MethodDispatcher([
+                ("html", self.startTagHtml),
+                (("base", "basefont", "bgsound", "command", "link", "meta",
+                  "noframes", "script", "style", "title"),
+                 self.startTagProcessInHead),
+                ("body", self.startTagBody),
+                ("frameset", self.startTagFrameset),
+                (("address", "article", "aside", "blockquote", "center", "details",
+                  "details", "dir", "div", "dl", "fieldset", "figcaption", "figure",
+                  "footer", "header", "hgroup", "main", "menu", "nav", "ol", "p",
+                  "section", "summary", "ul"),
+                 self.startTagCloseP),
+                (headingElements, self.startTagHeading),
+                (("pre", "listing"), self.startTagPreListing),
+                ("form", self.startTagForm),
+                (("li", "dd", "dt"), self.startTagListItem),
+                ("plaintext", self.startTagPlaintext),
+                ("a", self.startTagA),
+                (("b", "big", "code", "em", "font", "i", "s", "small", "strike",
+                  "strong", "tt", "u"), self.startTagFormatting),
+                ("nobr", self.startTagNobr),
+                ("button", self.startTagButton),
+                (("applet", "marquee", "object"), self.startTagAppletMarqueeObject),
+                ("xmp", self.startTagXmp),
+                ("table", self.startTagTable),
+                (("area", "br", "embed", "img", "keygen", "wbr"),
+                 self.startTagVoidFormatting),
+                (("param", "source", "track"), self.startTagParamSource),
+                ("input", self.startTagInput),
+                ("hr", self.startTagHr),
+                ("image", self.startTagImage),
+                ("isindex", self.startTagIsIndex),
+                ("textarea", self.startTagTextarea),
+                ("iframe", self.startTagIFrame),
+                (("noembed", "noframes", "noscript"), self.startTagRawtext),
+                ("select", self.startTagSelect),
+                (("rp", "rt"), self.startTagRpRt),
+                (("option", "optgroup"), self.startTagOpt),
+                (("math"), self.startTagMath),
+                (("svg"), self.startTagSvg),
+                (("caption", "col", "colgroup", "frame", "head",
+                  "tbody", "td", "tfoot", "th", "thead",
+                  "tr"), self.startTagMisplaced)
+            ])
+            self.startTagHandler.default = self.startTagOther
+
+            self.endTagHandler = utils.MethodDispatcher([
+                ("body", self.endTagBody),
+                ("html", self.endTagHtml),
+                (("address", "article", "aside", "blockquote", "button", "center",
+                  "details", "dialog", "dir", "div", "dl", "fieldset", "figcaption", "figure",
+                  "footer", "header", "hgroup", "listing", "main", "menu", "nav", "ol", "pre",
+                  "section", "summary", "ul"), self.endTagBlock),
+                ("form", self.endTagForm),
+                ("p", self.endTagP),
+                (("dd", "dt", "li"), self.endTagListItem),
+                (headingElements, self.endTagHeading),
+                (("a", "b", "big", "code", "em", "font", "i", "nobr", "s", "small",
+                  "strike", "strong", "tt", "u"), self.endTagFormatting),
+                (("applet", "marquee", "object"), self.endTagAppletMarqueeObject),
+                ("br", self.endTagBr),
+            ])
+            self.endTagHandler.default = self.endTagOther
+
+        def isMatchingFormattingElement(self, node1, node2):
+            if node1.name != node2.name or node1.namespace != node2.namespace:
+                return False
+            elif len(node1.attributes) != len(node2.attributes):
+                return False
+            else:
+                attributes1 = sorted(node1.attributes.items())
+                attributes2 = sorted(node2.attributes.items())
+                for attr1, attr2 in zip(attributes1, attributes2):
+                    if attr1 != attr2:
+                        return False
+            return True
+
+        # helper
+        def addFormattingElement(self, token):
+            self.tree.insertElement(token)
+            element = self.tree.openElements[-1]
+
+            matchingElements = []
+            for node in self.tree.activeFormattingElements[::-1]:
+                if node is Marker:
+                    break
+                elif self.isMatchingFormattingElement(node, element):
+                    matchingElements.append(node)
+
+            assert len(matchingElements) <= 3
+            if len(matchingElements) == 3:
+                self.tree.activeFormattingElements.remove(matchingElements[-1])
+            self.tree.activeFormattingElements.append(element)
+
+        # the real deal
+        def processEOF(self):
+            allowed_elements = frozenset(("dd", "dt", "li", "p", "tbody", "td",
+                                          "tfoot", "th", "thead", "tr", "body",
+                                          "html"))
+            for node in self.tree.openElements[::-1]:
+                if node.name not in allowed_elements:
+                    self.parser.parseError("expected-closing-tag-but-got-eof")
+                    break
+            # Stop parsing
+
+        def processSpaceCharactersDropNewline(self, token):
+            # Sometimes (start of 
, , and