Ejemplo n.º 1
0
    def __merge(self, component = None):
        """
        merge the config options between the default comp dictionaries
        and the file we're parsing now
        """
        # Caches this component's configuration options
        if component is None:
            component = self.__component

        opts = UserDictCase()
        comps = parse_comps(component)
        for comp in comps:
            if not self.__defaults.has_key(comp):
                warn('key not found in config default dict', comp)
                continue
            opts.update(self.__defaults[comp])

        # Now load the specific stuff, and perform syntax checking too
        for comp in comps:
            if not self.__parsedConfig.has_key(comp):
                # No such entry in the config file
                continue
            for key, (values, lineno) in self.__parsedConfig[comp].items():
                ## XXX: we don't really want to force every item in the
                ## config file to have a default value first. If we do,
                ## uncomment this section
                #if not opts.has_key(key): # Unknown keyword
                #    warn("Warning: in file %s, line %s: unknown "
                #        "option name `%s'" % (self.file, lineno, key))
                #    continue
                opts[key] = values
        # and now save it
        self.__configs[component] = opts
Ejemplo n.º 2
0
    def __init__(self, transfer=0, encoding=0, connection=None, method="POST"):
        # Assumes connection is an instance of HTTPConnection
        if connection:
            if not isinstance(connection, connections.HTTPConnection):
                raise Exception("Expected an HTTPConnection type object")

        self.method = method

        # Store the connection
        self._connection = connection

        self.data = None
        self.headers = UserDictCase()
        self.encoding = 0
        self.transfer = 0
        self.transport_flags = {}
        # for authenticated proxies
        self.username = None
        self.password = None
        # Fields to keep the information about the server
        self._host = None
        self._handler = None
        self._http_type = None
        self._protocol = None
        # Initialize self.transfer and self.encoding
        self.set_transport_flags(transfer=transfer, encoding=encoding)

        # internal flags
        self.__processed = 0
Ejemplo n.º 3
0
    def __init__(self, transfer=0, encoding=0, connection=None, method="POST"):
        # Assumes connection is an instance of HTTPConnection
        if connection:
            if not isinstance(connection, connections.HTTPConnection):
                raise Exception("Expected an HTTPConnection type object")

        self.method = method

        # Store the connection
        self._connection = connection

        self.data = None
        self.headers = UserDictCase()
        self.encoding = 0
        self.transfer = 0
        self.transport_flags = {}
        # for authenticated proxies
        self.username = None
        self.password = None
        # Fields to keep the information about the server
        self._host = None
        self._handler = None
        self._http_type = None
        self._protocol = None
        # Initialize self.transfer and self.encoding
        self.set_transport_flags(transfer=transfer, encoding=encoding)

        # internal flags
        self.__processed = 0
Ejemplo n.º 4
0
 def __init__(self, transfer=0, encoding=0, refreshCallback=None,
         progressCallback=None, use_datetime=None, timeout=None):
     self._transport_flags = {'transfer' : 0, 'encoding' : 0}
     self.set_transport_flags(transfer=transfer, encoding=encoding)
     self._headers = UserDictCase()
     self.verbose = 0
     self.connection = None
     self.method = "POST"
     self._lang = None
     self.refreshCallback = refreshCallback
     self.progressCallback = progressCallback
     self.bufferSize = 16384
     self.headers_in = None
     self.response_status = None
     self.response_reason = None
     self._redirected = None
     self._use_datetime = use_datetime
     self.timeout = timeout
Ejemplo n.º 5
0
 def __init__(self, transfer=0, encoding=0, refreshCallback=None,
         progressCallback=None):
     self._transport_flags = {'transfer' : 0, 'encoding' : 0}
     self.set_transport_flags(transfer=transfer, encoding=encoding)
     self._headers = UserDictCase()
     self.verbose = 0
     self.connection = None
     self.method = "POST"
     self._lang = None
     self.refreshCallback = refreshCallback
     self.progressCallback = progressCallback
     self.bufferSize = 16384
     self.headers_in = None
     self.response_status = None
     self.response_reason = None
     self._redirected = None
Ejemplo n.º 6
0
class BaseOutput:
    # DEFINES for instances use
    # Content-Encoding
    ENCODE_NONE = 0
    ENCODE_GZIP = 1
    ENCODE_ZLIB = 2
    ENCODE_GPG  = 3

    # Content-Transfer-Encoding
    TRANSFER_NONE   = 0
    TRANSFER_BINARY = 1
    TRANSFER_BASE64 = 2

     # Mappings to make things easy
    encodings = [
         [None, "__plain"],     # ENCODE_NONE
         ["x-gzip", "gzip"],    # ENCODE_GZIP
         ["x-zlib", "deflate"], # ENCODE_ZLIB
         ["x-gpg"],             # ENCODE_GPG
    ]
    transfers = [
         None,          # TRANSFER_NONE
         "binary",      # TRANSFRE_BINARY
         "base64",      # TRANSFER_BASE64
    ]

    def __init__(self, transfer=0, encoding=0, connection=None, method="POST"):
        # Assumes connection is an instance of HTTPConnection
        if connection:
            if not isinstance(connection, connections.HTTPConnection):
                raise Exception("Expected an HTTPConnection type object")

        self.method = method

        # Store the connection
        self._connection = connection

        self.data = None
        self.headers = UserDictCase()
        self.encoding = 0
        self.transfer = 0
        self.transport_flags = {}
        # for authenticated proxies
        self.username = None
        self.password = None
        # Fields to keep the information about the server
        self._host = None
        self._handler = None
        self._http_type = None
        self._protocol = None
        # Initialize self.transfer and self.encoding
        self.set_transport_flags(transfer=transfer, encoding=encoding)

        # internal flags
        self.__processed = 0

    def set_header(self, name, arg):
        if type(arg) in [ type([]), type(()) ]:
            # Multi-valued header
            #
            # Per RFC 2616, section 4.2 (Message Headers):
            # Multiple message-header fields with the same field-name MAY be
            # present in a message if and only if the entire field-value for
            # the header field is defined as a comma-separated list [i.e.
            # #(values)]. It MUST be possible to combine the multiple header
            # fields into one "field-name: field-value" pair, without
            # changing the semantics of the message, by appending each
            # subsequent field-value to the first, each separated by a comma.
            self.headers[name] = ','.join(map(str, arg))
        else:
            self.headers[name] = str(arg)

    def clear_header(self, name):
        if self.headers.has_key(name):
            del self.headers[name]

    def process(self, data):
        # Assume straight text/xml
        self.data = data

        # Content-Encoding header
        if self.encoding == self.ENCODE_GZIP:
            import gzip
            encoding_name = self.encodings[self.ENCODE_GZIP][0]
            self.set_header("Content-Encoding", encoding_name)
            f = SmartIO(force_mem=1)
            gz = gzip.GzipFile(mode="wb", compresslevel=COMPRESS_LEVEL,
                               fileobj = f)
            gz.write(data)
            gz.close()
            self.data = f.getvalue()
            f.close()
        elif self.encoding == self.ENCODE_ZLIB:
            import zlib
            encoding_name = self.encodings[self.ENCODE_ZLIB][0]
            self.set_header("Content-Encoding", encoding_name)
            obj = zlib.compressobj(COMPRESS_LEVEL)
            self.data = obj.compress(data) + obj.flush()
        elif self.encoding == self.ENCODE_GPG:
            # XXX: fix me.
            raise NotImplementedError(self.transfer, self.encoding)
            encoding_name = self.encodings[self.ENCODE_GPG][0]
            self.set_header("Content-Encoding", encoding_name)

        # Content-Transfer-Encoding header
        if self.transfer == self.TRANSFER_BINARY:
            transfer_name = self.transfers[self.TRANSFER_BINARY]
            self.set_header("Content-Transfer-Encoding", transfer_name)
            self.set_header("Content-Type", "application/binary")
        elif self.transfer == self.TRANSFER_BASE64:
            import base64
            transfer_name = self.transfers[self.TRANSFER_BASE64]
            self.set_header("Content-Transfer-Encoding", transfer_name)
            self.set_header("Content-Type", "text/base64")
            self.data = base64.encodestring(self.data)

        self.set_header("Content-Length", len(self.data))

        rpc_version = __version__
        if len(__version__.split()) > 1:
            rpc_version = __version__.split()[1]

        # other headers
        self.set_header("X-Transport-Info",
            'Extended Capabilities Transport (C) Red Hat, Inc (version %s)' %
            rpc_version)
        self.__processed = 1

    # reset the transport options
    def set_transport_flags(self, transfer=0, encoding=0, **kwargs):
        self.transfer = transfer
        self.encoding = encoding
        self.transport_flags.update(kwargs)

    def send_http(self, host, handler="/RPC2"):
        if not self.__processed:
            raise NotProcessed

        self._host = host

        if self._connection is None:
            raise Exception("No connection object found")
        self._connection.connect()
        self._connection.request(self.method, handler, body=self.data,
            headers=self.headers)

        response = self._connection.getresponse()

        if not self.response_acceptable(response):
            raise xmlrpclib.ProtocolError("%s %s" %
                (self._host, handler),
                response.status, response.reason, response.msg)

        # A response object has read() and close() methods, so we can safely
        # pass the whole object back
        return response.msg, response

    def response_acceptable(self, response):
        """Returns true if the response is acceptable"""
        if response.status == 200:
            return 1
        if response.status in (301, 302):
            return 1
        if response.status != 206:
            return 0
        # If the flag is not set, it's unacceptable
        if not self.transport_flags.get('allow_partial_content'):
            return 0
        if response.msg['Content-Type'] != 'application/octet-stream':
            # Don't allow anything else to be requested as a range, it could
            # break the XML parser
            return 0
        return 1

    def close(self):
        if self._connection:
            self._connection.close()
            self._connection = None
Ejemplo n.º 7
0
class Transport(xmlrpclib.Transport):
    user_agent = "rhn.rpclib.py/%s" % __version__

    def __init__(self, transfer=0, encoding=0, refreshCallback=None,
            progressCallback=None, use_datetime=None, timeout=None):
        self._transport_flags = {'transfer' : 0, 'encoding' : 0}
        self.set_transport_flags(transfer=transfer, encoding=encoding)
        self._headers = UserDictCase()
        self.verbose = 0
        self.connection = None
        self.method = "POST"
        self._lang = None
        self.refreshCallback = refreshCallback
        self.progressCallback = progressCallback
        self.bufferSize = 16384
        self.headers_in = None
        self.response_status = None
        self.response_reason = None
        self._redirected = None
        self._use_datetime = use_datetime
        self.timeout = timeout

    # set the progress callback
    def set_progress_callback(self, progressCallback, bufferSize=16384):
        self.progressCallback = progressCallback
        self.bufferSize = bufferSize

    # set the refresh callback
    def set_refresh_callback(self, refreshCallback):
        self.refreshCallback = refreshCallback

    # set the buffer size
    # The bigger this is, the faster the read is, but the more seldom is the
    # progress callback called
    def set_buffer_size(self, bufferSize):
        if bufferSize is None:
            # No buffer size specified; go with 16k
            bufferSize = 16384

        self.bufferSize = bufferSize

    # set the request method
    def set_method(self, method):
        if method not in ("GET", "POST"):
            raise IOError("Unknown request method %s" % method)
        self.method = method

    # reset the transport options
    def set_transport_flags(self, transfer=None, encoding=None, **kwargs):
        # For backwards compatibility, we keep transfer and encoding as
        # positional parameters (they could come in as kwargs easily)

        self._transport_flags.update(kwargs)
        if transfer is not None:
            self._transport_flags['transfer'] = transfer
        if encoding is not None:
            self._transport_flags['encoding'] = encoding
        self.validate_transport_flags()

    def get_transport_flags(self):
        return self._transport_flags.copy()

    def validate_transport_flags(self):
        # Transfer and encoding are guaranteed to be there
        transfer = self._transport_flags.get('transfer')
        transfer = lookupTransfer(transfer, strict=1)
        self._transport_flags['transfer'] = transfer

        encoding = self._transport_flags.get('encoding')
        encoding = lookupEncoding(encoding, strict=1)
        self._transport_flags['encoding'] = encoding

    # Add arbitrary additional headers.
    def set_header(self, name, arg):
        if type(arg) in [ type([]), type(()) ]:
            # Multivalued header
            self._headers[name] = map(str, arg)
        else:
            self._headers[name] = str(arg)

    def add_header(self, name, arg):
        if self._headers.has_key(name):
            vlist = self._headers[name]
            if not isinstance(vlist, ListType):
                vlist = [ vlist ]
        else:
            vlist = self._headers[name] = []
        vlist.append(str(arg))

    def clear_headers(self):
        self._headers.clear()

    def get_connection(self, host):
        if self.verbose:
            print("Connecting via http to %s" % (host, ))
        if self.timeout:
            return connections.HTTPConnection(host, timeout=self.timeout)
        else:
            return connections.HTTPConnection(host)

    def request(self, host, handler, request_body, verbose=0):
        # issue XML-RPC request
        # XXX: automatically compute how to send depending on how much data
        #      you want to send

        # XXX Deal with HTTP/1.1 if necessary
        self.verbose = verbose

        # implement BASIC HTTP AUTHENTICATION
        host, extra_headers, x509 = self.get_host_info(host)
        if not extra_headers:
            extra_headers = []
        # Establish the connection
        connection = self.get_connection(host)
        # Setting the user agent. Only interesting for SSL tunnels, in any
        # other case the general headers are good enough.
        connection.set_user_agent(self.user_agent)
        if self.verbose:
            connection.set_debuglevel(self.verbose - 1)
        # Get the output object to push data with
        req = Output(connection=connection, method=self.method)
        apply(req.set_transport_flags, (), self._transport_flags)

        # Add the extra headers
        req.set_header('User-Agent', self.user_agent)
        for header, value in self._headers.items() + extra_headers:
            # Output.set_header correctly deals with multivalued headers now
            req.set_header(header, value)

        # Content-Type
        req.set_header("Content-Type", "text/xml")
        req.process(request_body)

        # Host and Content-Length are set by HTTP*Connection
        for h in ['Content-Length', 'Host']:
            req.clear_header(h)

        headers, fd = req.send_http(host, handler)

        if self.verbose:
            print("Incoming headers:")
            for header, value in headers.items():
                print("\t%s : %s" % (header, value))

        if fd.status in (301, 302):
            self._redirected = headers["Location"]
            self.response_status = fd.status
            return None

        # Save the headers
        self.headers_in = headers
        self.response_status = fd.status
        self.response_reason = fd.reason

        return self._process_response(fd, connection)

    def _process_response(self, fd, connection):
        # Now use the Input class in case we get an enhanced response
        resp = Input(self.headers_in, progressCallback=self.progressCallback,
                bufferSize=self.bufferSize)

        fd = resp.decode(fd)

        if isinstance(fd, InputStream):
            # When the File object goes out of scope, so will the InputStream;
            # that will eventually call the connection's close() method and
            # cleanly reap it
            f = File(fd.fd, fd.length, fd.name, bufferSize=self.bufferSize,
                progressCallback=self.progressCallback)
            # Set the File's close method to the connection's
            # Note that calling the HTTPResponse's close() is not enough,
            # since the main socket would remain open, and this is
            # particularily bad with SSL
            f.close = connection.close
            return f

        # We can safely close the connection now; if we had an
        # application/octet/stream (for which Input.read passes the original
        # socket object), Input.decode would return an InputStream,
        # so we wouldn't reach this point
        connection.close()

        return self.parse_response(fd)

    # Give back the new URL if redirected
    def redirected(self):
        return self._redirected

    # Rewrite parse_response to provide refresh callbacks
    def parse_response(self, f):
        # read response from input file, and parse it

        p, u = self.getparser()

        while 1:
            response = f.read(1024)
            if not response:
                break
            if self.refreshCallback:
                self.refreshCallback()
            if self.verbose:
                print("body:", repr(response))
            p.feed(response)

        f.close()
        p.close()
        return u.close()


    def setlang(self, lang):
        self._lang = lang
Ejemplo n.º 8
0
class Server:
    """uri [,options] -> a logical connection to an XML-RPC server

    uri is the connection point on the server, given as
    scheme://host/target.

    The standard implementation always supports the "http" scheme.  If
    SSL socket support is available (Python 2.0), it also supports
    "https".

    If the target part and the slash preceding it are both omitted,
    "/RPC2" is assumed.

    The following options can be given as keyword arguments:

        transport: a transport factory
        encoding: the request encoding (default is UTF-8)
        verbose: verbosity level
        proxy: use an HTTP proxy
        username: username for authenticated HTTP proxy
        password: password for authenticated HTTP proxy

    All 8-bit strings passed to the server proxy are assumed to use
    the given encoding.
    """

    # Default factories
    _transport_class = transports.Transport
    _transport_class_https = transports.SafeTransport
    _transport_class_proxy = transports.ProxyTransport
    _transport_class_https_proxy = transports.SafeProxyTransport
    def __init__(self, uri, transport=None, encoding=None, verbose=0,
        proxy=None, username=None, password=None, refreshCallback=None,
        progressCallback=None):
        # establish a "logical" server connection

        #
        # First parse the proxy information if available
        #
        if proxy != None:
            (ph, pp, pu, pw) = get_proxy_info(proxy)

            if pp is not None:
                proxy = "%s:%s" % (ph, pp)
            else:
                proxy = ph

            # username and password will override whatever was passed in the
            # URL
            if pu is not None and username is None:
                username = pu

                if pw is not None and password is None:
                    password = pw

        self._uri = uri
        self._refreshCallback = None
        self._progressCallback = None
        self._bufferSize = None
        self._proxy = proxy
        self._username = username
        self._password = password

        if len(__version__.split()) > 1:
            self.rpc_version = __version__.split()[1]
        else:
            self.rpc_version = __version__

        self._reset_host_handler_and_type()

        if transport is None:
            self._allow_redirect = 1
            transport = self.default_transport(self._type, proxy, username, password)
        else:
            #
            # dont allow redirect on unknow transports, that should be
            # set up independantly
            #
            self._allow_redirect = 0

        self._redirected = None
        self.use_handler_path = 1
        self._transport = transport

        self._trusted_cert_files = []
        self._lang = None

        self._encoding = encoding
        self._verbose = verbose

        self.set_refresh_callback(refreshCallback)
        self.set_progress_callback(progressCallback)

        # referer, which redirect us to new handler
        self.send_handler=None

        self._headers = UserDictCase()

    def default_transport(self, type, proxy=None, username=None, password=None):
        if proxy:
            if type == 'https':
                transport = self._transport_class_https_proxy(proxy,
                    proxyUsername=username, proxyPassword=password)
            else:
                transport = self._transport_class_proxy(proxy,
                    proxyUsername=username, proxyPassword=password)
        else:
            if type == 'https':
                transport = self._transport_class_https()
            else:
                transport = self._transport_class()
        return transport

    def allow_redirect(self, allow):
        self._allow_redirect = allow

    def redirected(self):
        if not self._allow_redirect:
            return None
        return self._redirected

    def set_refresh_callback(self, refreshCallback):
        self._refreshCallback = refreshCallback
        self._transport.set_refresh_callback(refreshCallback)

    def set_buffer_size(self, bufferSize):
        self._bufferSize = bufferSize
        self._transport.set_buffer_size(bufferSize)

    def set_progress_callback(self, progressCallback, bufferSize=16384):
        self._progressCallback = progressCallback
        self._transport.set_progress_callback(progressCallback, bufferSize)

    def _req_body(self, params, methodname):
        return xmlrpclib.dumps(params, methodname, encoding=self._encoding)

    def get_response_headers(self):
        if self._transport:
            return self._transport.headers_in
        return None

    def get_response_status(self):
        if self._transport:
            return self._transport.response_status
        return None

    def get_response_reason(self):
        if self._transport:
            return self._transport.response_reason
        return None

    def get_content_range(self):
        """Returns a dictionary with three values:
            length: the total length of the entity-body (can be None)
            first_byte_pos: the position of the first byte (zero based)
            last_byte_pos: the position of the last byte (zero based)
           The range is inclusive; that is, a response 8-9/102 means two bytes
        """
        headers = self.get_response_headers()
        if not headers:
            return None
        content_range = headers.get('Content-Range')
        if not content_range:
            return None
        arr = filter(None, content_range.split())
        assert arr[0] == "bytes"
        assert len(arr) == 2
        arr = arr[1].split('/')
        assert len(arr) == 2

        brange, total_len = arr
        if total_len == '*':
            # Per RFC, the server is allowed to use * if the length of the
            # entity-body is unknown or difficult to determine
            total_len = None
        else:
            total_len = int(total_len)

        start, end = brange.split('-')
        result = {
            'length'            : total_len,
            'first_byte_pos'    : int(start),
            'last_byte_pos'     : int(end),
        }
        return result

    def accept_ranges(self):
        headers = self.get_response_headers()
        if not headers:
            return None
        if headers.has_key('Accept-Ranges'):
            return headers['Accept-Ranges']
        return None

    def _reset_host_handler_and_type(self):
        """ Reset the attributes:
            self._host, self._handler, self._type
            according the value of self._uri.
        """
        # get the url
        type, uri = urllib.splittype(self._uri)
        if type is None:
            raise MalformedURIError, "missing protocol in uri"
        # with a real uri passed in, uri will now contain "//hostname..." so we
        # need at least 3 chars for it to maybe be ok...
        if len(uri) < 3 or uri[0:2] != "//":
            raise MalformedURIError
        if type != None:
            self._type = type.lower()
        else:
            self._type = type
        if self._type not in ("http", "https"):
            raise IOError, "unsupported XML-RPC protocol"
        self._host, self._handler = urllib.splithost(uri)
        if not self._handler:
            self._handler = "/RPC2"

    def _strip_characters(self, *args):
        """ Strip characters, which are not allowed according:
            http://www.w3.org/TR/2006/REC-xml-20060816/#charsets
            From spec:
            Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]  /* any Unicode character, excluding the surrogate blocks, FFFE, and FFFF. */
        """
        regexp = r'[\x00-\x09]|[\x0b-\x0c]|[\x0e-\x1f]'
        result=[]
        for item in args:
            item_type = type(item)
            if item_type == StringType or item_type == UnicodeType:
                item = re.sub(regexp, '', item)
            elif item_type == TupleType:
                item = tuple(map(self._strip_characters, item))
            elif item_type == ListType:
                item = map(self._strip_characters, item)
            elif item_type == DictType or item_type == DictionaryType:
                item = dict([(self._strip_characters(name, val)) for name, val in item.iteritems()])
            # else: some object - should take care of himself
            #        numbers - are safe
            result.append(item)
        if len(result) == 1:
            return result[0]
        else:
            return tuple(result)

    def _request(self, methodname, params):
        """ Call a method on the remote server
            we can handle redirections. """
        # the loop is used to handle redirections
        redirect_response = 0
        retry = 0

        self._reset_host_handler_and_type()

        while 1:
            if retry >= MAX_REDIRECTIONS:
                raise InvalidRedirectionError(
                      "Unable to fetch requested Package")

            # Clear the transport headers first
            self._transport.clear_headers()
            for k, v in self._headers.items():
                self._transport.set_header(k, v)

            self._transport.add_header("X-Info",
                'RPC Processor (C) Red Hat, Inc (version %s)' %
                self.rpc_version)
            # identify the capability set of this client to the server
            self._transport.set_header("X-Client-Version", 1)

            if self._allow_redirect:
                # Advertise that we follow redirects
                #changing the version from 1 to 2 to support backward compatibility
                self._transport.add_header("X-RHN-Transport-Capability",
                    "follow-redirects=3")

            if redirect_response:
                self._transport.add_header('X-RHN-Redirect', '0')
                if self.send_handler:
                    self._transport.add_header('X-RHN-Path', self.send_handler)

            request = self._req_body(self._strip_characters(params), methodname)

            try:
                response = self._transport.request(self._host, \
                                self._handler, request, verbose=self._verbose)
                save_response = self._transport.response_status
            except xmlrpclib.ProtocolError, pe:
                if self.use_handler_path:
                    raise
                else:
                     save_response = pe.errcode

            self._redirected = None
            retry += 1
            if save_response == 200:
                # exit redirects loop and return response
                break
            elif save_response not in (301, 302):
                # Retry pkg fetch
                 self.use_handler_path = 1
                 continue
            # rest of loop is run only if we are redirected (301, 302)
            self._redirected = self._transport.redirected()
            self.use_handler_path = 0
            redirect_response = 1

            if not self._allow_redirect:
                raise InvalidRedirectionError("Redirects not allowed")

            if self._verbose:
                print "%s redirected to %s" % (self._uri, self._redirected)

            typ, uri = urllib.splittype(self._redirected)

            if typ != None:
                typ = typ.lower()
            if typ not in ("http", "https"):
                raise InvalidRedirectionError(
                    "Redirected to unsupported protocol %s" % typ)

            #
            # We forbid HTTPS -> HTTP for security reasons
            # Note that HTTP -> HTTPS -> HTTP is allowed (because we compare
            # the protocol for the redirect with the original one)
            #
            if self._type == "https" and typ == "http":
                raise InvalidRedirectionError(
                    "HTTPS redirected to HTTP is not supported")

            self._host, self._handler = urllib.splithost(uri)
            if not self._handler:
                self._handler = "/RPC2"

            # Create a new transport for the redirected service and
            # set up the parameters on the new transport
            del self._transport
            self._transport = self.default_transport(typ, self._proxy,
                                     self._username, self._password)
            self.set_progress_callback(self._progressCallback)
            self.set_refresh_callback(self._refreshCallback)
            self.set_buffer_size(self._bufferSize)
            self.setlang(self._lang)

            if self._trusted_cert_files != [] and \
                hasattr(self._transport, "add_trusted_cert"):
                for certfile in self._trusted_cert_files:
                    self._transport.add_trusted_cert(certfile)
            # Then restart the loop to try the new entry point.

        if isinstance(response, transports.File):
            # Just return the file
            return response

        # an XML-RPC encoded data structure
        if isinstance(response, TupleType) and len(response) == 1:
            response = response[0]

        return response
Ejemplo n.º 9
0
    def __init__(self, uri, transport=None, encoding=None, verbose=0,
        proxy=None, username=None, password=None, refreshCallback=None,
        progressCallback=None):
        # establish a "logical" server connection

        #
        # First parse the proxy information if available
        #
        if proxy != None:
            (ph, pp, pu, pw) = get_proxy_info(proxy)

            if pp is not None:
                proxy = "%s:%s" % (ph, pp)
            else:
                proxy = ph

            # username and password will override whatever was passed in the
            # URL
            if pu is not None and username is None:
                username = pu

                if pw is not None and password is None:
                    password = pw

        self._uri = uri
        self._refreshCallback = None
        self._progressCallback = None
        self._bufferSize = None
        self._proxy = proxy
        self._username = username
        self._password = password

        if len(__version__.split()) > 1:
            self.rpc_version = __version__.split()[1]
        else:
            self.rpc_version = __version__

        self._reset_host_handler_and_type()

        if transport is None:
            self._allow_redirect = 1
            transport = self.default_transport(self._type, proxy, username, password)
        else:
            #
            # dont allow redirect on unknow transports, that should be
            # set up independantly
            #
            self._allow_redirect = 0

        self._redirected = None
        self.use_handler_path = 1
        self._transport = transport

        self._trusted_cert_files = []
        self._lang = None

        self._encoding = encoding
        self._verbose = verbose

        self.set_refresh_callback(refreshCallback)
        self.set_progress_callback(progressCallback)

        # referer, which redirect us to new handler
        self.send_handler=None

        self._headers = UserDictCase()
Ejemplo n.º 10
0
class BaseOutput:
    # DEFINES for instances use
    # Content-Encoding
    ENCODE_NONE = 0
    ENCODE_GZIP = 1
    ENCODE_ZLIB = 2
    ENCODE_GPG = 3

    # Content-Transfer-Encoding
    TRANSFER_NONE = 0
    TRANSFER_BINARY = 1
    TRANSFER_BASE64 = 2

    # Mappings to make things easy
    encodings = [
        [None, "__plain"],  # ENCODE_NONE
        ["x-gzip", "gzip"],  # ENCODE_GZIP
        ["x-zlib", "deflate"],  # ENCODE_ZLIB
        ["x-gpg"],  # ENCODE_GPG
    ]
    transfers = [
        None,  # TRANSFER_NONE
        "binary",  # TRANSFRE_BINARY
        "base64",  # TRANSFER_BASE64
    ]

    def __init__(self, transfer=0, encoding=0, connection=None, method="POST"):
        # Assumes connection is an instance of HTTPConnection
        if connection:
            if not isinstance(connection, connections.HTTPConnection):
                raise Exception("Expected an HTTPConnection type object")

        self.method = method

        # Store the connection
        self._connection = connection

        self.data = None
        self.headers = UserDictCase()
        self.encoding = 0
        self.transfer = 0
        self.transport_flags = {}
        # for authenticated proxies
        self.username = None
        self.password = None
        # Fields to keep the information about the server
        self._host = None
        self._handler = None
        self._http_type = None
        self._protocol = None
        # Initialize self.transfer and self.encoding
        self.set_transport_flags(transfer=transfer, encoding=encoding)

        # internal flags
        self.__processed = 0

    def set_header(self, name, arg):
        if type(arg) in [type([]), type(())]:
            # Multi-valued header
            #
            # Per RFC 2616, section 4.2 (Message Headers):
            # Multiple message-header fields with the same field-name MAY be
            # present in a message if and only if the entire field-value for
            # the header field is defined as a comma-separated list [i.e.
            # #(values)]. It MUST be possible to combine the multiple header
            # fields into one "field-name: field-value" pair, without
            # changing the semantics of the message, by appending each
            # subsequent field-value to the first, each separated by a comma.
            self.headers[name] = ','.join(map(str, arg))
        else:
            self.headers[name] = str(arg)

    def clear_header(self, name):
        if self.headers.has_key(name):
            del self.headers[name]

    def process(self, data):
        # Assume straight text/xml
        self.data = data

        # Content-Encoding header
        if self.encoding == self.ENCODE_GZIP:
            import gzip
            encoding_name = self.encodings[self.ENCODE_GZIP][0]
            self.set_header("Content-Encoding", encoding_name)
            f = SmartIO(force_mem=1)
            gz = gzip.GzipFile(mode="wb",
                               compresslevel=COMPRESS_LEVEL,
                               fileobj=f)
            gz.write(data)
            gz.close()
            self.data = f.getvalue()
            f.close()
        elif self.encoding == self.ENCODE_ZLIB:
            import zlib
            encoding_name = self.encodings[self.ENCODE_ZLIB][0]
            self.set_header("Content-Encoding", encoding_name)
            obj = zlib.compressobj(COMPRESS_LEVEL)
            self.data = obj.compress(data) + obj.flush()
        elif self.encoding == self.ENCODE_GPG:
            # XXX: fix me.
            raise NotImplementedError(self.transfer, self.encoding)
            encoding_name = self.encodings[self.ENCODE_GPG][0]
            self.set_header("Content-Encoding", encoding_name)

        # Content-Transfer-Encoding header
        if self.transfer == self.TRANSFER_BINARY:
            transfer_name = self.transfers[self.TRANSFER_BINARY]
            self.set_header("Content-Transfer-Encoding", transfer_name)
            self.set_header("Content-Type", "application/binary")
        elif self.transfer == self.TRANSFER_BASE64:
            import base64
            transfer_name = self.transfers[self.TRANSFER_BASE64]
            self.set_header("Content-Transfer-Encoding", transfer_name)
            self.set_header("Content-Type", "text/base64")
            self.data = base64.encodestring(self.data)

        self.set_header("Content-Length", len(self.data))

        rpc_version = __version__
        if len(__version__.split()) > 1:
            rpc_version = __version__.split()[1]

        # other headers
        self.set_header(
            "X-Transport-Info",
            'Extended Capabilities Transport (C) Red Hat, Inc (version %s)' %
            rpc_version)
        self.__processed = 1

    # reset the transport options
    def set_transport_flags(self, transfer=0, encoding=0, **kwargs):
        self.transfer = transfer
        self.encoding = encoding
        self.transport_flags.update(kwargs)

    def send_http(self, host, handler="/RPC2"):
        if not self.__processed:
            raise NotProcessed

        self._host = host

        if self._connection is None:
            raise Exception("No connection object found")
        self._connection.connect()
        self._connection.request(self.method,
                                 handler,
                                 body=self.data,
                                 headers=self.headers)

        response = self._connection.getresponse()

        if not self.response_acceptable(response):
            raise xmlrpclib.ProtocolError("%s %s" % (self._host, handler),
                                          response.status, response.reason,
                                          response.msg)

        # A response object has read() and close() methods, so we can safely
        # pass the whole object back
        return response.msg, response

    def response_acceptable(self, response):
        """Returns true if the response is acceptable"""
        if response.status == 200:
            return 1
        if response.status in (301, 302):
            return 1
        if response.status != 206:
            return 0
        # If the flag is not set, it's unacceptable
        if not self.transport_flags.get('allow_partial_content'):
            return 0
        if response.msg['Content-Type'] != 'application/octet-stream':
            # Don't allow anything else to be requested as a range, it could
            # break the XML parser
            return 0
        return 1

    def close(self):
        if self._connection:
            self._connection.close()
            self._connection = None
Ejemplo n.º 11
0
class Transport(xmlrpclib.Transport):
    user_agent = "rhn.rpclib.py/%s" % __version__

    def __init__(self,
                 transfer=0,
                 encoding=0,
                 refreshCallback=None,
                 progressCallback=None,
                 use_datetime=None,
                 timeout=None):
        self._transport_flags = {'transfer': 0, 'encoding': 0}
        self.set_transport_flags(transfer=transfer, encoding=encoding)
        self._headers = UserDictCase()
        self.verbose = 0
        self.connection = None
        self.method = "POST"
        self._lang = None
        self.refreshCallback = refreshCallback
        self.progressCallback = progressCallback
        self.bufferSize = 16384
        self.headers_in = None
        self.response_status = None
        self.response_reason = None
        self._redirected = None
        self._use_datetime = use_datetime
        self.timeout = timeout

    # set the progress callback
    def set_progress_callback(self, progressCallback, bufferSize=16384):
        self.progressCallback = progressCallback
        self.bufferSize = bufferSize

    # set the refresh callback
    def set_refresh_callback(self, refreshCallback):
        self.refreshCallback = refreshCallback

    # set the buffer size
    # The bigger this is, the faster the read is, but the more seldom is the
    # progress callback called
    def set_buffer_size(self, bufferSize):
        if bufferSize is None:
            # No buffer size specified; go with 16k
            bufferSize = 16384

        self.bufferSize = bufferSize

    # set the request method
    def set_method(self, method):
        if method not in ("GET", "POST"):
            raise IOError("Unknown request method %s" % method)
        self.method = method

    # reset the transport options
    def set_transport_flags(self, transfer=None, encoding=None, **kwargs):
        # For backwards compatibility, we keep transfer and encoding as
        # positional parameters (they could come in as kwargs easily)

        self._transport_flags.update(kwargs)
        if transfer is not None:
            self._transport_flags['transfer'] = transfer
        if encoding is not None:
            self._transport_flags['encoding'] = encoding
        self.validate_transport_flags()

    def get_transport_flags(self):
        return self._transport_flags.copy()

    def validate_transport_flags(self):
        # Transfer and encoding are guaranteed to be there
        transfer = self._transport_flags.get('transfer')
        transfer = lookupTransfer(transfer, strict=1)
        self._transport_flags['transfer'] = transfer

        encoding = self._transport_flags.get('encoding')
        encoding = lookupEncoding(encoding, strict=1)
        self._transport_flags['encoding'] = encoding

    # Add arbitrary additional headers.
    def set_header(self, name, arg):
        if type(arg) in [type([]), type(())]:
            # Multivalued header
            self._headers[name] = map(str, arg)
        else:
            self._headers[name] = str(arg)

    def add_header(self, name, arg):
        if self._headers.has_key(name):
            vlist = self._headers[name]
            if not isinstance(vlist, ListType):
                vlist = [vlist]
        else:
            vlist = self._headers[name] = []
        vlist.append(str(arg))

    def clear_headers(self):
        self._headers.clear()

    def get_connection(self, host):
        if self.verbose:
            print("Connecting via http to %s" % (host, ))
        if self.timeout:
            return connections.HTTPConnection(host, timeout=self.timeout)
        else:
            return connections.HTTPConnection(host)

    def request(self, host, handler, request_body, verbose=0):
        # issue XML-RPC request
        # XXX: automatically compute how to send depending on how much data
        #      you want to send

        # XXX Deal with HTTP/1.1 if necessary
        self.verbose = verbose

        # implement BASIC HTTP AUTHENTICATION
        host, extra_headers, x509 = self.get_host_info(host)
        if not extra_headers:
            extra_headers = []
        # Establish the connection
        connection = self.get_connection(host)
        # Setting the user agent. Only interesting for SSL tunnels, in any
        # other case the general headers are good enough.
        connection.set_user_agent(self.user_agent)
        if self.verbose:
            connection.set_debuglevel(self.verbose - 1)
        # Get the output object to push data with
        req = Output(connection=connection, method=self.method)
        apply(req.set_transport_flags, (), self._transport_flags)

        # Add the extra headers
        req.set_header('User-Agent', self.user_agent)
        for header, value in self._headers.items() + extra_headers:
            # Output.set_header correctly deals with multivalued headers now
            req.set_header(header, value)

        # Content-Type
        req.set_header("Content-Type", "text/xml")
        req.process(request_body)

        # Host and Content-Length are set by HTTP*Connection
        for h in ['Content-Length', 'Host']:
            req.clear_header(h)

        headers, fd = req.send_http(host, handler)

        if self.verbose:
            print("Incoming headers:")
            for header, value in headers.items():
                print("\t%s : %s" % (header, value))

        if fd.status in (301, 302):
            self._redirected = headers["Location"]
            self.response_status = fd.status
            return None

        # Save the headers
        self.headers_in = headers
        self.response_status = fd.status
        self.response_reason = fd.reason

        return self._process_response(fd, connection)

    def _process_response(self, fd, connection):
        # Now use the Input class in case we get an enhanced response
        resp = Input(self.headers_in,
                     progressCallback=self.progressCallback,
                     bufferSize=self.bufferSize)

        fd = resp.decode(fd)

        if isinstance(fd, InputStream):
            # When the File object goes out of scope, so will the InputStream;
            # that will eventually call the connection's close() method and
            # cleanly reap it
            f = File(fd.fd,
                     fd.length,
                     fd.name,
                     bufferSize=self.bufferSize,
                     progressCallback=self.progressCallback)
            # Set the File's close method to the connection's
            # Note that calling the HTTPResponse's close() is not enough,
            # since the main socket would remain open, and this is
            # particularily bad with SSL
            f.close = connection.close
            return f

        # We can safely close the connection now; if we had an
        # application/octet/stream (for which Input.read passes the original
        # socket object), Input.decode would return an InputStream,
        # so we wouldn't reach this point
        connection.close()

        return self.parse_response(fd)

    # Give back the new URL if redirected
    def redirected(self):
        return self._redirected

    # Rewrite parse_response to provide refresh callbacks
    def parse_response(self, f):
        # read response from input file, and parse it

        p, u = self.getparser()

        while 1:
            response = f.read(1024)
            if not response:
                break
            if self.refreshCallback:
                self.refreshCallback()
            if self.verbose:
                print("body:", repr(response))
            p.feed(response)

        f.close()
        p.close()
        return u.close()

    def setlang(self, lang):
        self._lang = lang
Ejemplo n.º 12
0
    def __init__(self, uri, transport=None, encoding=None, verbose=0, 
        proxy=None, username=None, password=None, refreshCallback=None,
        progressCallback=None):
        # establish a "logical" server connection

        #
        # First parse the proxy information if available
        #
        if proxy != None:
            (ph, pp, pu, pw) = get_proxy_info(proxy)

            if pp is not None:
                proxy = "%s:%s" % (ph, pp)
            else:
                proxy = ph

            # username and password will override whatever was passed in the
            # URL
            if pu is not None and username is None:
                username = pu

                if pw is not None and password is None:
                    password = pw
                    
        self._uri = uri
        self._refreshCallback = None
        self._progressCallback = None
        self._bufferSize = None
        self._proxy = proxy
        self._username = username
        self._password = password

        # get the url
        type, uri = urllib.splittype(uri)
        type = (string.lower(type)).strip()
        self._type = type
        if type not in ("http", "https"):
            raise IOError, "unsupported XML-RPC protocol"
        self._host, self._handler = urllib.splithost(uri)
        if not self._handler:
            self._handler = "/RPC2"

        if transport is None:
            self._allow_redirect = 1
            transport = self.default_transport(type, proxy, username, password)
        else:
            #
            # dont allow redirect on unknow transports, that should be
            # set up independantly
            #
            self._allow_redirect = 0
            
        self._redirected = None
        self.use_handler_path = 1
        self._transport = transport

        self._trusted_cert_files = []
        self._lang = None

        self._encoding = encoding
        self._verbose = verbose

        self.set_refresh_callback(refreshCallback)
        self.set_progress_callback(progressCallback)

        self._headers = UserDictCase()
Ejemplo n.º 13
0
    def __init__(self,
                 uri,
                 transport=None,
                 encoding=None,
                 verbose=0,
                 proxy=None,
                 username=None,
                 password=None,
                 refreshCallback=None,
                 progressCallback=None,
                 timeout=None):
        # establish a "logical" server connection

        #
        # First parse the proxy information if available
        #
        if proxy != None:
            (ph, pp, pu, pw) = get_proxy_info(proxy)

            if pp is not None:
                proxy = "%s:%s" % (ph, pp)
            else:
                proxy = ph

            # username and password will override whatever was passed in the
            # URL
            if pu is not None and username is None:
                username = pu

                if pw is not None and password is None:
                    password = pw

        self._uri = uri
        self._refreshCallback = None
        self._progressCallback = None
        self._bufferSize = None
        self._proxy = proxy
        self._username = username
        self._password = password
        self._timeout = timeout

        if len(__version__.split()) > 1:
            self.rpc_version = __version__.split()[1]
        else:
            self.rpc_version = __version__

        self._reset_host_handler_and_type()

        if transport is None:
            self._allow_redirect = 1
            transport = self.default_transport(self._type, proxy, username,
                                               password, timeout)
        else:
            #
            # dont allow redirect on unknow transports, that should be
            # set up independantly
            #
            self._allow_redirect = 0

        self._redirected = None
        self.use_handler_path = 1
        self._transport = transport

        self._trusted_cert_files = []
        self._lang = None

        self._encoding = encoding
        self._verbose = verbose

        self.set_refresh_callback(refreshCallback)
        self.set_progress_callback(progressCallback)

        # referer, which redirect us to new handler
        self.send_handler = None

        self._headers = UserDictCase()
Ejemplo n.º 14
0
class Server:
    """uri [,options] -> a logical connection to an XML-RPC server

    uri is the connection point on the server, given as
    scheme://host/target.

    The standard implementation always supports the "http" scheme.  If
    SSL socket support is available (Python 2.0), it also supports
    "https".

    If the target part and the slash preceding it are both omitted,
    "/RPC2" is assumed.

    The following options can be given as keyword arguments:

        transport: a transport factory
        encoding: the request encoding (default is UTF-8)
        verbose: verbosity level
        proxy: use an HTTP proxy
        username: username for authenticated HTTP proxy
        password: password for authenticated HTTP proxy

    All 8-bit strings passed to the server proxy are assumed to use
    the given encoding.
    """

    # Default factories
    _transport_class = transports.Transport
    _transport_class_https = transports.SafeTransport
    _transport_class_proxy = transports.ProxyTransport
    _transport_class_https_proxy = transports.SafeProxyTransport

    def __init__(self,
                 uri,
                 transport=None,
                 encoding=None,
                 verbose=0,
                 proxy=None,
                 username=None,
                 password=None,
                 refreshCallback=None,
                 progressCallback=None,
                 timeout=None):
        # establish a "logical" server connection

        #
        # First parse the proxy information if available
        #
        if proxy != None:
            (ph, pp, pu, pw) = get_proxy_info(proxy)

            if pp is not None:
                proxy = "%s:%s" % (ph, pp)
            else:
                proxy = ph

            # username and password will override whatever was passed in the
            # URL
            if pu is not None and username is None:
                username = pu

                if pw is not None and password is None:
                    password = pw

        self._uri = uri
        self._refreshCallback = None
        self._progressCallback = None
        self._bufferSize = None
        self._proxy = proxy
        self._username = username
        self._password = password
        self._timeout = timeout

        if len(__version__.split()) > 1:
            self.rpc_version = __version__.split()[1]
        else:
            self.rpc_version = __version__

        self._reset_host_handler_and_type()

        if transport is None:
            self._allow_redirect = 1
            transport = self.default_transport(self._type, proxy, username,
                                               password, timeout)
        else:
            #
            # dont allow redirect on unknow transports, that should be
            # set up independantly
            #
            self._allow_redirect = 0

        self._redirected = None
        self.use_handler_path = 1
        self._transport = transport

        self._trusted_cert_files = []
        self._lang = None

        self._encoding = encoding
        self._verbose = verbose

        self.set_refresh_callback(refreshCallback)
        self.set_progress_callback(progressCallback)

        # referer, which redirect us to new handler
        self.send_handler = None

        self._headers = UserDictCase()

    def default_transport(self,
                          type,
                          proxy=None,
                          username=None,
                          password=None,
                          timeout=None):
        if proxy:
            if type == 'https':
                transport = self._transport_class_https_proxy(
                    proxy,
                    proxyUsername=username,
                    proxyPassword=password,
                    timeout=timeout)
            else:
                transport = self._transport_class_proxy(proxy,
                                                        proxyUsername=username,
                                                        proxyPassword=password,
                                                        timeout=timeout)
        else:
            if type == 'https':
                transport = self._transport_class_https(timeout=timeout)
            else:
                transport = self._transport_class(timeout=timeout)
        return transport

    def allow_redirect(self, allow):
        self._allow_redirect = allow

    def redirected(self):
        if not self._allow_redirect:
            return None
        return self._redirected

    def set_refresh_callback(self, refreshCallback):
        self._refreshCallback = refreshCallback
        self._transport.set_refresh_callback(refreshCallback)

    def set_buffer_size(self, bufferSize):
        self._bufferSize = bufferSize
        self._transport.set_buffer_size(bufferSize)

    def set_progress_callback(self, progressCallback, bufferSize=16384):
        self._progressCallback = progressCallback
        self._transport.set_progress_callback(progressCallback, bufferSize)

    def _req_body(self, params, methodname):
        return xmlrpclib.dumps(params, methodname, encoding=self._encoding)

    def get_response_headers(self):
        if self._transport:
            return self._transport.headers_in
        return None

    def get_response_status(self):
        if self._transport:
            return self._transport.response_status
        return None

    def get_response_reason(self):
        if self._transport:
            return self._transport.response_reason
        return None

    def get_content_range(self):
        """Returns a dictionary with three values:
            length: the total length of the entity-body (can be None)
            first_byte_pos: the position of the first byte (zero based)
            last_byte_pos: the position of the last byte (zero based)
           The range is inclusive; that is, a response 8-9/102 means two bytes
        """
        headers = self.get_response_headers()
        if not headers:
            return None
        content_range = headers.get('Content-Range')
        if not content_range:
            return None
        arr = filter(None, content_range.split())
        assert arr[0] == "bytes"
        assert len(arr) == 2
        arr = arr[1].split('/')
        assert len(arr) == 2

        brange, total_len = arr
        if total_len == '*':
            # Per RFC, the server is allowed to use * if the length of the
            # entity-body is unknown or difficult to determine
            total_len = None
        else:
            total_len = int(total_len)

        start, end = brange.split('-')
        result = {
            'length': total_len,
            'first_byte_pos': int(start),
            'last_byte_pos': int(end),
        }
        return result

    def accept_ranges(self):
        headers = self.get_response_headers()
        if not headers:
            return None
        if headers.has_key('Accept-Ranges'):
            return headers['Accept-Ranges']
        return None

    def _reset_host_handler_and_type(self):
        """ Reset the attributes:
            self._host, self._handler, self._type
            according the value of self._uri.
        """
        # get the url
        type, uri = urllib.splittype(self._uri)
        if type is None:
            raise MalformedURIError, "missing protocol in uri"
        # with a real uri passed in, uri will now contain "//hostname..." so we
        # need at least 3 chars for it to maybe be ok...
        if len(uri) < 3 or uri[0:2] != "//":
            raise MalformedURIError
        if type != None:
            self._type = type.lower()
        else:
            self._type = type
        if self._type not in ("http", "https"):
            raise IOError, "unsupported XML-RPC protocol"
        self._host, self._handler = urllib.splithost(uri)
        if not self._handler:
            self._handler = "/RPC2"

    def _strip_characters(self, *args):
        """ Strip characters, which are not allowed according:
            http://www.w3.org/TR/2006/REC-xml-20060816/#charsets
            From spec:
            Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]  /* any Unicode character, excluding the surrogate blocks, FFFE, and FFFF. */
        """
        regexp = r'[\x00-\x09]|[\x0b-\x0c]|[\x0e-\x1f]'
        result = []
        for item in args:
            item_type = type(item)
            if item_type == StringType or item_type == UnicodeType:
                item = re.sub(regexp, '', item)
            elif item_type == TupleType:
                item = tuple(map(self._strip_characters, item))
            elif item_type == ListType:
                item = map(self._strip_characters, item)
            elif item_type == DictType or item_type == DictionaryType:
                item = dict([(self._strip_characters(name, val))
                             for name, val in item.iteritems()])
            # else: some object - should take care of himself
            #        numbers - are safe
            result.append(item)
        if len(result) == 1:
            return result[0]
        else:
            return tuple(result)

    def _request(self, methodname, params):
        """ Call a method on the remote server
            we can handle redirections. """
        # the loop is used to handle redirections
        redirect_response = 0
        retry = 0

        self._reset_host_handler_and_type()

        while 1:
            if retry >= MAX_REDIRECTIONS:
                raise InvalidRedirectionError(
                    "Unable to fetch requested Package")

            # Clear the transport headers first
            self._transport.clear_headers()
            for k, v in self._headers.items():
                self._transport.set_header(k, v)

            self._transport.add_header(
                "X-Info", 'RPC Processor (C) Red Hat, Inc (version %s)' %
                self.rpc_version)
            # identify the capability set of this client to the server
            self._transport.set_header("X-Client-Version", 1)

            if self._allow_redirect:
                # Advertise that we follow redirects
                #changing the version from 1 to 2 to support backward compatibility
                self._transport.add_header("X-RHN-Transport-Capability",
                                           "follow-redirects=3")

            if redirect_response:
                self._transport.add_header('X-RHN-Redirect', '0')
                if self.send_handler:
                    self._transport.add_header('X-RHN-Path', self.send_handler)

            request = self._req_body(self._strip_characters(params),
                                     methodname)

            try:
                response = self._transport.request(self._host, \
                                self._handler, request, verbose=self._verbose)
                save_response = self._transport.response_status
            except xmlrpclib.ProtocolError, pe:
                if self.use_handler_path:
                    raise
                else:
                    save_response = pe.errcode

            self._redirected = None
            retry += 1
            if save_response == 200:
                # exit redirects loop and return response
                break
            elif save_response not in (301, 302):
                # Retry pkg fetch
                self.use_handler_path = 1
                continue
            # rest of loop is run only if we are redirected (301, 302)
            self._redirected = self._transport.redirected()
            self.use_handler_path = 0
            redirect_response = 1

            if not self._allow_redirect:
                raise InvalidRedirectionError("Redirects not allowed")

            if self._verbose:
                print "%s redirected to %s" % (self._uri, self._redirected)

            typ, uri = urllib.splittype(self._redirected)

            if typ != None:
                typ = typ.lower()
            if typ not in ("http", "https"):
                raise InvalidRedirectionError(
                    "Redirected to unsupported protocol %s" % typ)

            #
            # We forbid HTTPS -> HTTP for security reasons
            # Note that HTTP -> HTTPS -> HTTP is allowed (because we compare
            # the protocol for the redirect with the original one)
            #
            if self._type == "https" and typ == "http":
                raise InvalidRedirectionError(
                    "HTTPS redirected to HTTP is not supported")

            self._host, self._handler = urllib.splithost(uri)
            if not self._handler:
                self._handler = "/RPC2"

            # Create a new transport for the redirected service and
            # set up the parameters on the new transport
            del self._transport
            self._transport = self.default_transport(typ, self._proxy,
                                                     self._username,
                                                     self._password,
                                                     self._timeout)
            self.set_progress_callback(self._progressCallback)
            self.set_refresh_callback(self._refreshCallback)
            self.set_buffer_size(self._bufferSize)
            self.setlang(self._lang)

            if self._trusted_cert_files != [] and \
                hasattr(self._transport, "add_trusted_cert"):
                for certfile in self._trusted_cert_files:
                    self._transport.add_trusted_cert(certfile)
            # Then restart the loop to try the new entry point.

        if isinstance(response, transports.File):
            # Just return the file
            return response

        # an XML-RPC encoded data structure
        if isinstance(response, TupleType) and len(response) == 1:
            response = response[0]

        return response
Ejemplo n.º 15
0
class Server:
    """uri [,options] -> a logical connection to an XML-RPC server

    uri is the connection point on the server, given as
    scheme://host/target.

    The standard implementation always supports the "http" scheme.  If
    SSL socket support is available (Python 2.0), it also supports
    "https".

    If the target part and the slash preceding it are both omitted,
    "/RPC2" is assumed.

    The following options can be given as keyword arguments:

        transport: a transport factory
        encoding: the request encoding (default is UTF-8)
        verbose: verbosity level
        proxy: use an HTTP proxy
        username: username for authenticated HTTP proxy
        password: password for authenticated HTTP proxy

    All 8-bit strings passed to the server proxy are assumed to use
    the given encoding.
    """

    # Default factories
    _transport_class = transports.Transport
    _transport_class_https = transports.SafeTransport
    _transport_class_proxy = transports.ProxyTransport
    _transport_class_https_proxy = transports.SafeProxyTransport
    def __init__(self, uri, transport=None, encoding=None, verbose=0, 
        proxy=None, username=None, password=None, refreshCallback=None,
        progressCallback=None):
        # establish a "logical" server connection

        #
        # First parse the proxy information if available
        #
        if proxy != None:
            (ph, pp, pu, pw) = get_proxy_info(proxy)

            if pp is not None:
                proxy = "%s:%s" % (ph, pp)
            else:
                proxy = ph

            # username and password will override whatever was passed in the
            # URL
            if pu is not None and username is None:
                username = pu

                if pw is not None and password is None:
                    password = pw
                    
        self._uri = uri
        self._refreshCallback = None
        self._progressCallback = None
        self._bufferSize = None
        self._proxy = proxy
        self._username = username
        self._password = password

        # get the url
        type, uri = urllib.splittype(uri)
        type = (string.lower(type)).strip()
        self._type = type
        if type not in ("http", "https"):
            raise IOError, "unsupported XML-RPC protocol"
        self._host, self._handler = urllib.splithost(uri)
        if not self._handler:
            self._handler = "/RPC2"

        if transport is None:
            self._allow_redirect = 1
            transport = self.default_transport(type, proxy, username, password)
        else:
            #
            # dont allow redirect on unknow transports, that should be
            # set up independantly
            #
            self._allow_redirect = 0
            
        self._redirected = None
        self.use_handler_path = 1
        self._transport = transport

        self._trusted_cert_files = []
        self._lang = None

        self._encoding = encoding
        self._verbose = verbose

        self.set_refresh_callback(refreshCallback)
        self.set_progress_callback(progressCallback)

        self._headers = UserDictCase()

    def default_transport(self, type, proxy=None, username=None, password=None):
        if proxy:
            if type == 'https':
                transport = self._transport_class_https_proxy(proxy, 
                    proxyUsername=username, proxyPassword=password)
            else:
                transport = self._transport_class_proxy(proxy, 
                    proxyUsername=username, proxyPassword=password)
        else:
            if type == 'https':
                transport = self._transport_class_https()
            else:
                transport = self._transport_class()
        return transport

    def allow_redirect(self, allow):
        self._allow_redirect = allow

    def redirected(self):
        if not self._allow_redirect:
            return None
        return self._redirected

    def set_refresh_callback(self, refreshCallback):
        self._refreshCallback = refreshCallback
        self._transport.set_refresh_callback(refreshCallback)

    def set_buffer_size(self, bufferSize):
        self._bufferSize = bufferSize
        self._transport.set_buffer_size(bufferSize)

    def set_progress_callback(self, progressCallback, bufferSize=16384):
        self._progressCallback = progressCallback
        self._transport.set_progress_callback(progressCallback, bufferSize)

    def _req_body(self, params, methodname):
        return xmlrpclib.dumps(params, methodname, encoding=self._encoding)

    def get_response_headers(self):
        if self._transport:
            return self._transport.headers_in
        return None

    def get_response_status(self):
        if self._transport:
            return self._transport.response_status
        return None

    def get_response_reason(self):
        if self._transport:
            return self._transport.response_reason
        return None

    def get_content_range(self):
        """Returns a dictionary with three values:
            length: the total length of the entity-body (can be None)
            first_byte_pos: the position of the first byte (zero based)
            last_byte_pos: the position of the last byte (zero based)
           The range is inclusive; that is, a response 8-9/102 means two bytes
        """
        headers = self.get_response_headers()
        if not headers:
            return None
        content_range = headers.get('Content-Range')
        if not content_range:
            return None
        arr = filter(None, string.split(content_range))
        assert arr[0] == "bytes"
        assert len(arr) == 2
        arr = string.split(arr[1], '/')
        assert len(arr) == 2

        brange, total_len = arr
        if total_len == '*':
            # Per RFC, the server is allowed to use * if the length of the
            # entity-body is unknown or difficult to determine
            total_len = None
        else:
            total_len = int(total_len)

        start, end = string.split(brange, '-')
        result = {
            'length'            : total_len,
            'first_byte_pos'    : int(start),
            'last_byte_pos'     : int(end),
        }
        return result

    def accept_ranges(self):
        headers = self.get_response_headers()
        if not headers:
            return None
        if headers.has_key('Accept-Ranges'):
            return headers['Accept-Ranges']
        return None

    def _request(self, methodname, params):
        # call a method on the remote server
        # the loop is used to handle redirections
        num = 0
        redirect_response = 0
                
        while 1:
            if num >= MAX_REDIRECTIONS:
                raise InvalidRedirectionError("Too many redirects")
            num = num + 1

            # Clear the transport headers first
            self._transport.clear_headers()
            for k, v in self._headers.items():
                self._transport.set_header(k, v)
            
            self._transport.add_header("X-Info",
                'RPC Processor (C) Red Hat, Inc (version %s)' % 
                string.split(__version__)[1])
            # identify the capability set of this client to the server
            self._transport.set_header("X-Client-Version", 1)
            
            if self._allow_redirect:
                # Advertise that we follow redirects
                #changing the version from 1 to 2 to support backward compatibility
                self._transport.add_header("X-RHN-Transport-Capability",
                    "follow-redirects=2")

            if redirect_response:
                self._transport.add_header('X-RHN-Redirect', '0')
                if send_handler:
                    self._transport.add_header('X-RHN-Path', send_handler)

            request = self._req_body(params, methodname)

            try:
                response = self._transport.request(self._host, self._handler,
                    request, verbose=self._verbose)
                save_response = self._transport.response_status
            except xmlrpclib.ProtocolError, pe:
                if self.use_handler_path:
                    raise pe
                else:
                     save_response = pe.errcode

            if not self._allow_redirect:
                raise InvalidRedirectionError("Redirects not allowed")
           
            if save_response == 200:
                break
            elif save_response not in [200,302]:
                self._redirected = self._uri
                self.use_handler_path = 1
            else:
                self._redirected = self._transport.redirected()
                self.use_handler_path = 0
                                
            if self._verbose:
                print "%s redirected to %s" % (self._uri, self._redirected)

            typ, uri = urllib.splittype(self._redirected)
            
            if typ != None:
                typ = string.lower(typ)
            if typ not in ("http", "https"):
                raise InvalidRedirectionError(
                    "Redirected to unsupported protocol %s" % typ)

            #
            # We forbid HTTPS -> HTTP for security reasons
            # Note that HTTP -> HTTPS -> HTTP is allowed (because we compare
            # the protocol for the redirect with the original one)
            #
            if self._type == "https" and typ == "http":
                raise InvalidRedirectionError(
                    "HTTPS redirected to HTTP is not supported")

            self._host, self._handler = urllib.splithost(uri)
            if not self._handler:
                self._handler = "/RPC2"

            if save_response == 302:
                if not self._allow_redirect:
                    raise InvalidRedirectionError("Redirects not allowed")
                else:
                    redirect_response = 1
                    num = 0
                    continue

            # 
            # Create a new transport for the redirected service and 
            # set up the parameters on the new transport
            #
            del self._transport
            self._transport = self.default_transport(typ, self._proxy,
                                             self._username, self._password)
            self.set_progress_callback(self._progressCallback)
            self.set_refresh_callback(self._refreshCallback)
            self.set_buffer_size(self._bufferSize)
            self.setlang(self._lang)

            if self._trusted_cert_files != [] and \
               hasattr(self._transport, "add_trusted_cert"):
                for certfile in self._trusted_cert_files:
                    self._transport.add_trusted_cert(certfile)
            #
            # Then restart the loop to try the new entry point.
            #

        if isinstance(response, transports.File):
            # Just return the file
            return response
            
        # an XML-RPC encoded data structure
        if isinstance(response, TupleType) and len(response) == 1:
            response = response[0]

        return response
Ejemplo n.º 16
0
    def __init__(self,
                 uri,
                 transport=None,
                 encoding=None,
                 verbose=0,
                 proxy=None,
                 username=None,
                 password=None,
                 refreshCallback=None,
                 progressCallback=None):
        # establish a "logical" server connection

        #
        # First parse the proxy information if available
        #
        if proxy != None:
            (ph, pp, pu, pw) = get_proxy_info(proxy)

            if pp is not None:
                proxy = "%s:%s" % (ph, pp)
            else:
                proxy = ph

            # username and password will override whatever was passed in the
            # URL
            if pu is not None and username is None:
                username = pu

                if pw is not None and password is None:
                    password = pw

        self._uri = uri
        self._refreshCallback = None
        self._progressCallback = None
        self._bufferSize = None
        self._proxy = proxy
        self._username = username
        self._password = password

        # get the url
        type, uri = urllib.splittype(uri)
        type = (string.lower(type)).strip()
        self._type = type
        if type not in ("http", "https"):
            raise IOError, "unsupported XML-RPC protocol"
        self._host, self._handler = urllib.splithost(uri)
        if not self._handler:
            self._handler = "/RPC2"

        if transport is None:
            self._allow_redirect = 1
            transport = self.default_transport(type, proxy, username, password)
        else:
            #
            # dont allow redirect on unknow transports, that should be
            # set up independantly
            #
            self._allow_redirect = 0

        self._redirected = None
        self.use_handler_path = 1
        self._transport = transport

        self._trusted_cert_files = []
        self._lang = None

        self._encoding = encoding
        self._verbose = verbose

        self.set_refresh_callback(refreshCallback)
        self.set_progress_callback(progressCallback)

        self._headers = UserDictCase()