File rhnlib-git-0.c65b45e.obscpio of Package rhnlib.34322

07070100000000000041FD000000000000000000000002666AB47A00000000000000000000000000000000000000000000000700000000rhnlib07070100000001000081B4000000000000000000000001666AB47A000046AC000000000000000000000000000000000000000F00000000rhnlib/COPYING                    GNU GENERAL PUBLIC LICENSE
                       Version 2, June 1991

 Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users.  This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it.  (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.)  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.

  To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have.  You must make sure that they, too, receive or can get the
source code.  And you must show them these terms so they know their
rights.

  We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.

  Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software.  If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.

  Finally, any free program is threatened constantly by software
patents.  We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary.  To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.

  The precise terms and conditions for copying, distribution and
modification follow.

                    GNU GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License.  The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language.  (Hereinafter, translation is included without limitation in
the term "modification".)  Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.

  1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.

You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.

  2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) You must cause the modified files to carry prominent notices
    stating that you changed the files and the date of any change.

    b) You must cause any work that you distribute or publish, that in
    whole or in part contains or is derived from the Program or any
    part thereof, to be licensed as a whole at no charge to all third
    parties under the terms of this License.

    c) If the modified program normally reads commands interactively
    when run, you must cause it, when started running for such
    interactive use in the most ordinary way, to print or display an
    announcement including an appropriate copyright notice and a
    notice that there is no warranty (or else, saying that you provide
    a warranty) and that users may redistribute the program under
    these conditions, and telling the user how to view a copy of this
    License.  (Exception: if the Program itself is interactive but
    does not normally print such an announcement, your work based on
    the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.

In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:

    a) Accompany it with the complete corresponding machine-readable
    source code, which must be distributed under the terms of Sections
    1 and 2 above on a medium customarily used for software interchange; or,

    b) Accompany it with a written offer, valid for at least three
    years, to give any third party, for a charge no more than your
    cost of physically performing source distribution, a complete
    machine-readable copy of the corresponding source code, to be
    distributed under the terms of Sections 1 and 2 above on a medium
    customarily used for software interchange; or,

    c) Accompany it with the information you received as to the offer
    to distribute corresponding source code.  (This alternative is
    allowed only for noncommercial distribution and only if you
    received the program in object code or executable form with such
    an offer, in accord with Subsection b above.)

The source code for a work means the preferred form of the work for
making modifications to it.  For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable.  However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.

If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.

  4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License.  Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.

  5. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Program or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.

  6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.

  7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all.  For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.

If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded.  In such case, this License incorporates
the limitation as if written in the body of this License.

  9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

Each version is given a distinguishing version number.  If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation.  If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.

  10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission.  For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this.  Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.

                            NO WARRANTY

  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.

  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

Also add information on how to contact you by electronic and paper mail.

If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:

    Gnomovision version 69, Copyright (C) year name of author
    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
  `Gnomovision' (which makes passes at compilers) written by James Hacker.

  <signature of Ty Coon>, 1 April 1989
  Ty Coon, President of Vice

This General Public License does not permit incorporating your program into
proprietary programs.  If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
07070100000002000081B4000000000000000000000001666AB47A0000129C000000000000000000000000000000000000001100000000rhnlib/ChangeLogVersion 1.8
 - Breaking transports.Transport.request() into smaller pieces
 - Added factories for the transport classes in the Server object
 - Fixed a bug in File.__del__ (if close is called twice, it tried to call
   None)
 - Fixed a bug in SSL.read() (on an open stream, read() would block instead of
   returning with the amount of data available).
 - The previous fix in SSL.read uncovered a nastier bug in httplib:
   http://python.org/sf/988120
   Fixed it in our HTTPResponse subclass
 - The internal _httplib did not have a _read_chunked
   (http://bugzilla.redhat.com/128008)

Version 1.7
 - Renamed _xmlrpclib.py to _internal_xmlrpclib.py to avoid _xmlrpclib from
   self-importing (bug #119876)
 - Added a User-Agent header for CONNECTION tunnels.
 - Fixed lookupEncoding

Version 1.6
 - GET methods have empty bodies, so rpclib.GETServer._req_body should return
   empty string not None
 - Properly convert chunk sizes in python 1.5.2

Version 1.5
 - Added iterator for the UserDictCase objects. Easier than subclassing
   IterableUserDict since the code has to work with python 1.5.2.
 - rhn/transports.py: one can pass to an Input object a stream that doesn't
   have a close() method.
 - rpclib exposes File now too
 - rhn/transports.py: the Output object now sets Content-Length
   (which will be cleared when sent upstream)

Version 1.3
 - work on HTTP/HTTPS redirection support at the Server class level in
   rhnlib.py:
   - new data in the instance to keep setting made to the underlying transport
   - new methods to control and check redirection: allow_redirect() and
     redirected() which returns the redirected URL or None
   - separated the initialization code to provide a default transport as an
     new method default_transport()
   - added redirection handling in _request() with some associated checking
     like https->http redirection being forbidden
   - at the transport level, request() checks redirection, position a
     redirection flag and returns None if redirected.
 - Added some flexibility on the way to specify proxy informations, the
   get_proxy_info() function of rhnlib.py is used to extract host, port,
   username and password information from the proxy URL or old host:port
   format.
 - Fixed an error related to decoding the tuples that XMLRPC returns back.

Version 1.2
 - Changed the ugly way of passing around InputStream as an exception
 - The transport flags are now more generic, allowing for future extensibility
 - Added a transport flag 'allow_partial_content' which will prevent the
   transport from raising an exception if a 206 status code is received
 - New Server methods:  get_response_headers, get_response_status and
   get_response_reason; you can now inspect the headers even for successful
   connections
 - Changed the GETServer method calls to accept offset and amount
 - Added get_content_range, to parse the interesting values out of the HTTP
   headers
 - Added accept_ranges, a function that returns true if the server accepts
   ranges. Note that even though the server doesn't advertise itself as
   accepting ranges, it's still possible for the client to issue a range
   request; depending on the response status code, the client can see if the
   server supports ranges (206) or not (200)
 - set_header and add_header are now different and doing what they are
   supposed to do
 - added get_transport_flags() as a Server method
 - fixed an instance when Input.io was not rewound

Version 1.1
 - Minor bugfixes
 - Building the rpm with distutils is too painful; switching to a
   human-maintained spec file instead.

Version 1.0
 - More or less 0.9 for public consumption

Version 0.9
 - SSL.py: use load_verify_locations multiple times. This function seems to do
   more than my poor method of getting the cert store and adding certs by 
   hand, probably because load_certificate from a buffer can only handle one
   certificate at the time.
 - With the addition of SmartIO, the memory usage should be limited to 16k,
   after which the objects will be dumped on the disk in temporary files.
 - Fixed yet another file descriptor leak (and still in SSL).

Version 0.8
 - User-Agent, X-Info and X-Client-Version were not present in the HTTP
   headers; fixed
 - Completely deprecating rhnHTTPlib: swallowed reportError
 - Fixed stupid typo introduced when fixing the previous bug.
 - Fixed #69518 (up2date seems to never properly reauthenticate after a auth
   timeout)
 - Fixed #69311 (leaking file descriptors over SSL connections).
 - Fixed #68911 (and some other bugs that were related to this one).
 - Fixed a proxy related bug. Thanks to Gil Chilton <Gil.Chilton@capitalone.com>
   for helping me discover and test it.

Version 0.7
 - More careful distutils packaging.
07070100000003000081B4000000000000000000000001666AB47A00000048000000000000000000000000000000000000001300000000rhnlib/MANIFEST.ininclude ChangeLog COPYING README TODO rhnlib.spec Makefile.rhnlib_build
07070100000004000081B4000000000000000000000001666AB47A00000361000000000000000000000000000000000000001700000000rhnlib/Makefile.pythonTHIS_MAKEFILE := $(realpath $(lastword $(MAKEFILE_LIST)))
CURRENT_DIR := $(dir $(THIS_MAKEFILE))
include $(CURRENT_DIR)../../rel-eng/Makefile.python

# Docker tests variables
DOCKER_CONTAINER_BASE = systemsmanagement/uyuni/master/docker/containers/uyuni-master
DOCKER_REGISTRY       = registry.opensuse.org
DOCKER_RUN_EXPORT     = "PYTHONPATH=$PYTHONPATH"
DOCKER_VOLUMES        = -v "$(CURDIR)/../../:/manager"

__pylint ::
	$(call update_pip_env)
	cd ..; pylint --rcfile=rhn/pylintrc rhn | tee rhn/reports/pylint.log || true

docker_pylint ::
	docker run --rm -e $(DOCKER_RUN_EXPORT) $(DOCKER_VOLUMES) $(DOCKER_REGISTRY)/$(DOCKER_CONTAINER_BASE)-pgsql /bin/sh -c "cd /manager/python/rhn; make -f Makefile.python __pylint"

docker_shell ::
	docker run -t -i --rm -e $(DOCKER_RUN_EXPORT) $(DOCKER_VOLUMES) $(DOCKER_REGISTRY)/$(DOCKER_CONTAINER_BASE)-pgsql /bin/bash
07070100000005000081B4000000000000000000000001666AB47A0000061F000000000000000000000000000000000000001700000000rhnlib/Makefile.rhnlib# Makefile for the rhn modules
#

PYTHON      = /usr/bin/python

NAME	    = rhnlib
VERSION	    = $(call _get_tag,VERSION)
RELEASE	    = $(call _get_tag,RELEASE)
_get_tag    = $(shell rpm -q --queryformat "%{$1}" --specfile $(NAME).spec | head -1)

WORKDIR	    = $(shell pwd)
SOURCEDIR   = $(WORKDIR)
SRCRPMDIR   = $(WORKDIR)
BUILDDIR    = $(WORKDIR)
RPMDIR	    = $(WORKDIR)

RPMBUILD    = rpmbuild
RPMBUILDCMD = $(RPMBUILD) --define "_sourcedir $(SOURCEDIR)" --define "_srcrpmdir $(SRCRPMDIR)" --define "_builddir $(BUILDDIR)" --define "_rpmdir $(RPMDIR)" --define "_build_name_fmt %{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}.rpm"

TARBALL	    = $(NAME)-$(VERSION).tar.gz

.PHONY: build install rpm devel release clean

all:	build

setup.py: setup.py.in $(VERFILE)
	@sed -e 's/@VERSION@/$(VERSION)/' -e 's/@NAME@/$(NAME)/' $< > $@

setup.cfg: setup.cfg.in $(VERFILE)
	@sed 's/@RELEASE@/$(RELEASE)/' $< > $@

build: setup.py
	@$(PYTHON) setup.py build
	for i in build/lib*/rhn/rpclib.py build/lib*/rhn/transports.py ; do sed -i 's!\$$Revision\$$!$(VERSION)-$(RELEASE)!' $$i ; done

install: setup.py setup.cfg
	@$(PYTHON) setup.py install

$(TARBALL): clean setup.py setup.cfg
	@$(PYTHON) setup.py sdist $(DISTUTILS_OPT)
	@mv dist/$(TARBALL) $(WORKDIR)
	@rm -rf dist

tar:	$(TARBALL)

test-srpm: $(TARBALL)
	$(RPMBUILDCMD) --nodeps -ts $(TARBALL)

test-rpm: $(TARBALL)
	$(RPMBUILDCMD) --nodeps -ta $(TARBALL)

rpm: $(TARBALL)
	$(RPMBUILD) -ta $(TARBALL)

release: tar test-srpm

clean:
	@rm -rfv dist build MANIFEST setup.cfg setup.py *pyc *.tar.gz *.rpm $(NAME)-$(VERSION)
07070100000006000081B4000000000000000000000001666AB47A00000422000000000000000000000000000000000000000E00000000rhnlib/READMErhnlib - A collection of python modules used by the Spacewalk
(http://spacewalk.redhat.com) software.

Copyright (C) 2002--2015 Red Hat, Inc.

This library is distributed under the terms of the GNU Public License (GPL).


0. HISTORY

Traditionally, rhn used a patched version of Fredrik Lundh's xmlrpclib,
shipped with Red Hat 6.2, 7.0, 7.1, 7.2 and 7.3 in a package called
python-xmlrpc.

The additions included improved transport objects (proxy support, a
better SSL framework, encodings and transfers), most of them part of a module
called cgiwrap.

Since python 2.2 (and previous python 2 releases) ships Fredrik Lundh's
xmlrpclib as a standard library, maintaining a patched version of xmlrpclib
was no longer feasible. SSL support in python 2.2 is also hopelessly
incomplete, so using an external module seems to be a better approach. We
chose pyOpenSSL instead of m2crypto, partly because swig seems to change
rather quickly.

rhnlib tries to reuse as much as possible from the old cgiwrap, as well as
from what the standard python libraries provide.
07070100000007000081B4000000000000000000000001666AB47A00002986000000000000000000000000000000000000000E00000000rhnlib/SSL.py#  pylint: disable=invalid-name
#
# Higher-level SSL objects used by rpclib
#
# Copyright (c) 2002--2017 Red Hat, Inc.
#
# Author: Mihai Ibanescu <misa@redhat.com>
#
# In addition, as a special exception, the copyright holders give
# permission to link the code of portions of this program with the
# OpenSSL library under certain conditions as described in each
# individual source file, and distribute linked combinations
# including the two.
# You must obey the GNU General Public License in all respects
# for all of the code used other than OpenSSL.  If you modify
# file(s) with this exception, you may extend this exception to your
# version of the file(s), but you are not obligated to do so.  If you
# do not wish to do so, delete this exception statement from your
# version.  If you delete this exception statement from all source
# files in the program, then also delete it here.


"""
rhn.SSL builds an abstraction on top of the objects provided by pyOpenSSL
"""

# SSL.crypto is provided to other modules
# pylint: disable-next=unused-import
from OpenSSL import crypto
import ssl as SSL
import os

import socket
import select
from rhn.stringutils import bstr
import sys

DEFAULT_TIMEOUT = 120

if hasattr(socket, "sslerror"):
    socket_error = socket.sslerror
else:
    # pylint: disable-next=unused-import
    from ssl import socket_error

try:
    # pylint: disable-next=unused-import
    from ssl import CertificateError
except ImportError:
    # python 2.6
    # pylint: disable-next=unused-import
    from backports.ssl_match_hostname import match_hostname, CertificateError


class SSLSocket:
    """
    Class that wraps a pyOpenSSL Connection object, adding more methods
    """

    # pylint: disable-next=redefined-outer-name
    def __init__(self, socket, trusted_certs=None):
        # SSL.Context object
        self._ctx = None
        # SSL.Connection object
        self._connection = None
        self._sock = socket
        self._trusted_certs = []
        # convert None to empty list
        trusted_certs = trusted_certs or []
        for f in trusted_certs:
            self.add_trusted_cert(f)

        # Buffer size for reads
        self._buffer_size = 8192

        # Position, for tell()
        self._pos = 0
        # Buffer
        self._buffer = bstr("")

        # Flag to show if makefile() was called
        self._makefile_called = 0

        self._closed = None

    def add_trusted_cert(self, file):
        """
        Adds a trusted certificate to the certificate store of the SSL context
        object.
        """
        if not os.access(file, os.R_OK):
            # pylint: disable-next=consider-using-f-string
            raise ValueError("Unable to read certificate file %s" % file)
        self._trusted_certs.append(file.encode("utf-8"))

    def init_ssl(self, server_name=None):
        """
        Initializes the SSL connection.
        """
        self._check_closed()
        self._ctx = SSL.SSLContext(SSL.PROTOCOL_TLS_CLIENT)
        self._ctx.options |= SSL.OP_NO_TLSv1
        self._ctx.options |= SSL.OP_NO_TLSv1_1
        self._ctx.verify_mode = SSL.CERT_REQUIRED
        self._ctx.check_hostname = True
        self._ctx.load_default_certs(SSL.Purpose.SERVER_AUTH)
        if self._trusted_certs:
            # We have been supplied with trusted CA certs
            for f in self._trusted_certs:
                self._ctx.load_verify_locations(f)
        self._connection = self._ctx.wrap_socket(
            self._sock, server_hostname=server_name
        )

    # pylint: disable-next=unused-argument
    def makefile(self, mode, bufsize=None):
        """
        Returns self, since we are a file-like object already
        """
        if bufsize:
            self._buffer_size = bufsize

        # Increment the counter with the number of times we've called makefile
        # - we don't want close to actually close things until all the objects
        # that originally called makefile() are gone
        self._makefile_called = self._makefile_called + 1
        return self

    def close(self):
        """
        Closes the SSL connection
        """
        # XXX Normally sock.makefile does a dup() on the socket file
        # descriptor; httplib relies on this, but there is no dup for an ssl
        # connection; so we have to count how may times makefile() was called
        if self._closed:
            # Nothing to do
            return
        if not self._makefile_called:
            self._really_close()
            return
        self._makefile_called = self._makefile_called - 1

    # BZ 1464157 - Python 3 http attempts to call this method during close,
    # at least add it empty
    def flush(self):
        pass

    def _really_close(self):
        # No connection was established
        if self._connection is None:
            return
        self._connection.close()
        self._closed = 1

    def _check_closed(self):
        if self._closed:
            raise ValueError("I/O operation on closed file")

    def __getattr__(self, name):
        if hasattr(self._connection, name):
            return getattr(self._connection, name)
        raise AttributeError(name)

    # File methods
    def isatty(self):
        """
        Returns false always.
        """
        return 0

    def tell(self):
        return self._pos

    def seek(self, pos, mode=0):
        raise NotImplementedError("seek")

    def read(self, amt=None):
        """
        Reads up to amt bytes from the SSL connection.
        """
        self._check_closed()
        # Initially, the buffer size is the default buffer size.
        # Unfortunately, pending() does not return meaningful data until
        # recv() is called, so we only adjust the buffer size after the
        # first read
        buffer_size = self._buffer_size

        buffer_length = len(self._buffer)
        # Read only the specified amount of data
        while buffer_length < amt or amt is None:
            # if amt is None (read till the end), fills in self._buffer
            if amt is not None:
                buffer_size = min(amt - buffer_length, buffer_size)

            try:
                data = self._connection.recv(buffer_size)

                self._buffer = self._buffer + data
                buffer_length = len(self._buffer)

                # More bytes to read?
                pending = self._connection.pending()
                if pending == 0 and buffer_length == amt:
                    # we're done here
                    break
            except SSL.SSLError as err:
                if err.args[0] == SSL.SSL_ERROR_ZERO_RETURN:
                    # Nothing more to be read
                    break
                elif err.args[0] == SSL.SSL_ERROR_SYSCALL:
                    e = sys.exc_info()[1]
                    print("SSL exception", e.args)
                    break
                elif err.args[0] == SSL.SSL_ERROR_WANT_WRITE:
                    self._poll(select.POLLOUT, "read")
                elif err.args[0] == SSL.SSL_ERROR_WANT_READ:
                    self._poll(select.POLLIN, "read")

        if amt:
            ret = self._buffer[:amt]
            self._buffer = self._buffer[amt:]
        else:
            ret = self._buffer
            self._buffer = bstr("")

        self._pos = self._pos + len(ret)
        return ret

    def readinto(self, buf):
        buf[:] = self.read(len(buf))
        return len(buf)

    def _poll(self, filter_type, caller_name):
        poller = select.poll()
        poller.register(self._sock, filter_type)
        res = poller.poll(self._sock.gettimeout() * 1000)
        if res == []:
            # pylint: disable-next=consider-using-f-string
            raise TimeoutException("Connection timed out on %s" % caller_name)

    def write(self, data):
        """
        Writes to the SSL connection.
        """
        self._check_closed()

        # XXX Should use sendall
        # sent = self._connection.sendall(data)
        origlen = len(data)
        while True:
            try:
                sent = self._connection.send(data)
                if sent == len(data):
                    break
                data = data[sent:]
            except SSL.SSLError as err:
                if err.args[0] == SSL.SSL_ERROR_WANT_WRITE:
                    self._poll(select.POLLOUT, "write")
                elif err.args[0] == SSL.SSL_ERROR_WANT_READ:
                    self._poll(select.POLLIN, "write")

        return origlen

    def recv(self, amt):
        return self.read(amt)

    send = write

    sendall = write

    def readline(self, length=None):
        """
        Reads a single line (up to `length' characters long) from the SSL
        connection.
        """
        self._check_closed()
        while True:
            # charcount contains the number of chars to be outputted (or None
            # if none to be outputted at this time)
            charcount = None
            i = self._buffer.find(bstr("\n"))
            if i >= 0:
                # Go one char past newline
                charcount = i + 1
            elif length and len(self._buffer) >= length:
                charcount = length

            if charcount is not None:
                ret = self._buffer[:charcount]
                self._buffer = self._buffer[charcount:]
                self._pos = self._pos + len(ret)
                return ret

            # Determine the number of chars to be read next
            bufsize = self._buffer_size
            if length:
                # we know length > len(self._buffer)
                bufsize = min(self._buffer_size, length - len(self._buffer))

            try:
                data = self._connection.recv(bufsize)
                if len(data) == 0:
                    break
                self._buffer = self._buffer + data
            except SSL.SSLError as err:
                if err.args[0] == SSL.SSL_ERROR_ZERO_RETURN:
                    # Nothing more to be read
                    break
                elif err.args[0] == SSL.SSL_ERROR_WANT_WRITE:
                    self._poll(select.POLLOUT, "readline")
                elif err.args[0] == SSL.SSL_ERROR_WANT_READ:
                    self._poll(select.POLLIN, "readline")

        # We got here if we're done reading, so return everything
        ret = self._buffer
        self._buffer = ""
        self._pos = self._pos + len(ret)
        return ret


class TimeoutException(SSL.SSLError, socket.timeout):
    def __init__(self, *args):
        self.args = args

    def __str__(self):
        return "Timeout Exception"
07070100000008000081B4000000000000000000000001666AB47A000008DC000000000000000000000000000000000000001200000000rhnlib/SmartIO.py#  pylint: disable=invalid-name
#
# Smart IO class
#
# Copyright (c) 2002--2016 Red Hat, Inc.
#
# Author: Mihai Ibanescu <misa@redhat.com>

"""
This module implements the SmartIO class
"""

import os

try:  # python2
    from cStringIO import StringIO
except ImportError:  # python3
    from io import BytesIO as StringIO


class SmartIO:
    """
    The SmartIO class allows one to put a cap on the memory consumption.
    StringIO objects are very fast, because they are stored in memory, but
    if they are too big the memory footprint becomes noticeable.
    The write method of a SmartIO determines if the data that is to be added
    to the (initially) StrintIO object does not exceed a certain threshold; if
    it does, it switches the storage to a temporary disk file
    """

    def __init__(self, max_mem_size=16384, force_mem=0):
        self._max_mem_size = max_mem_size
        self._io = StringIO()
        # self._fixed is a flag to show if we're supposed to consider moving
        # the StringIO object into a tempfile
        # Invariant: if self._fixed == 0, we have a StringIO (if self._fixed
        # is 1 and force_mem was 0, then we have a file)
        if force_mem:
            self._fixed = 1
        else:
            self._fixed = 0

    def set_max_mem_size(self, max_mem_size):
        self._max_mem_size = max_mem_size

    def get_max_mem_size(self):
        return self._max_mem_size

    def write(self, data):
        if not self._fixed:
            # let's consider moving it to a file
            if len(data) + self._io.tell() > self._max_mem_size:
                # We'll overflow, change to a tempfile
                tmpfile = _tempfile()
                tmpfile.write(self._io.getvalue())
                self._fixed = 1
                self._io = tmpfile

        self._io.write(data)

    def __getattr__(self, name):
        return getattr(self._io, name)


# Creates a temporary file and passes back its file descriptor
def _tempfile():
    # pylint: disable-next=import-outside-toplevel
    import tempfile

    # pylint: disable-next=consider-using-f-string
    (fd, fname) = tempfile.mkstemp(prefix="_rhn_transports-%d-" % os.getpid())
    # tempfile, unlink it
    os.unlink(fname)
    return os.fdopen(fd, "wb+")
07070100000009000081B4000000000000000000000001666AB47A00000293000000000000000000000000000000000000000C00000000rhnlib/TODO
- Check nonblocking support for HTTPS as well.
- Allow for multi-valued headers
- Wrapper around the socket class, to allow for calling select() on the socket
  I/O
- Split this module intelligently
- Build an __init__ file to expose the desired API
- Fix the Input and Output objects: setting the encoding from the input object
  to the output object is difficult (there is a patch somewhere for this, I'll
  have to look it up -- misa)
- Clean up things
- More documentation
- Check compliance with python 1.5
- Figure out a better way to pass the client version
- Figure out what the version we pass in the headers should look like.
- Finish the TODO :-)
0707010000000A000081B4000000000000000000000001666AB47A00000CB8000000000000000000000000000000000000001700000000rhnlib/UserDictCase.py#  pylint: disable=missing-module-docstring,invalid-name
#
# Copyright (c) 2001--2016 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public License,
# version 2 (GPLv2). There is NO WARRANTY for this software, express or
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
# along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
#
# Red Hat trademarks are not licensed under GPLv2. No permission is
# granted to use or replicate Red Hat trademarks that are incorporated
# in this software or its documentation.
#
#
# This file implements a case insensitive dictionary on top of the
# UserDict standard python class
#


try:  # python2
    from UserDict import UserDict
    from types import StringType
except ImportError:  # python3
    from collections import UserDict

    StringType = str
    from functools import reduce


# A dictionary with case insensitive keys
# pylint: disable-next=missing-class-docstring
class UserDictCase(UserDict):
    def __init__(self, data=None):
        self.kcase = {}
        UserDict.__init__(self, data)

    def __lower_string(self, key):
        """Return the lower() of key if it is a string."""
        if isinstance(key, StringType):
            return key.lower()
        else:
            return key

    # some methods used to make the class work as a dictionary
    def __setitem__(self, key, value):
        lkey = self.__lower_string(key)
        self.data[lkey] = value
        self.kcase[lkey] = key

    def __getitem__(self, key):
        key = self.__lower_string(key)
        return self.data[key]

    get = __getitem__

    def __delitem__(self, key):
        key = self.__lower_string(key)
        del self.data[key]
        del self.kcase[key]

    def __contains__(self, key):
        key = self.__lower_string(key)
        return key in self.data

    def keys(self):
        return self.kcase.values()

    def items(self):
        return self.get_hash().items()

    def has_key(self, key):
        # obsoleted, left for compatibility with older python
        return key in self

    def clear(self):
        self.data.clear()
        self.kcase.clear()

    # return this data as a real hash
    def get_hash(self):
        # pylint: disable-next=used-before-assignment
        return reduce(
            lambda a, t, hc=self.kcase: a.update({hc[t[0]]: t[1]}) or a,
            self.data.items(),
            {},
        )

    # return the data for marshalling
    def __getstate__(self):
        return self.get_hash()

    # we need a setstate because of the __getstate__ presence screws up deepcopy
    def __setstate__(self, state):
        self.__init__(state)

    # get a dictionary out of this instance ({}.update doesn't get instances)
    def dict(self):
        return self.get_hash()

    # pylint: disable-next=redefined-builtin
    def update(self, dict):
        for k, v in dict.items():
            self[k] = v

    # Expose an iterator. This would normally fail if there is no iter()
    # function defined - but __iter__ will never be called on python 1.5.2
    def __iter__(self):
        return iter(self.data)
0707010000000B000081B4000000000000000000000001666AB47A00000083000000000000000000000000000000000000001300000000rhnlib/__init__.py#
# __init__.py
#
# Copyright (c) 2011--2013 Red Hat, Inc.
#
"""
rhn - A collection of modules used by Red Hat Network Classic
"""
0707010000000C000081FD000000000000000000000001666AB47A00000CC3000000000000000000000000000000000000001700000000rhnlib/buildpackage.sh#!/usr/bin/bash

# script to build a solaris package of up2date-list
#
#  Adrian Likins <alikins@redhat.com>
#

NAME=`cat $1 | grep PKG | cut -f2 -d'='| cut -f2 -d\"` 
TMP=/tmp
BUILDPREFIX=$TMP/build-$NAME
# er, lame
BUILDPREFIX_REL=tmp/build-$NAME
DESTPREFIX_REL=opt/redhat/rhn/solaris
DESTPREFIX=/${DESTPREFIX_REL}
DATADIR=${TMP}/${NAME}-pkginfo/
USERNAME=`whoami`


# path for solaris build stuff, will need to get tweaked for hpux/aix, but 
# theres some infof or that already un use_spec.sh

#PATH=/opt/redhat/rhn/solaris/bin:/opt/redhat/gnupro-03r1/H-sparc-sun-solaris2.5/bin:/opt/redhat/gnupro-03r1/contrib:/opt/redhat/rpm/solaris/bin:/home/cygnus/release/bin:/es/cst/bin:/bsp/bin:/usr/progressive/bin:/usr/unsupported/bin:/bin:/usr/ucb:/usr/sbin:/usr/local/bin:/sbin:/usr/kerberos/bin:/usr/local/bin:/usr/bin:/usr/X11R6/bin

PYTHONDIR=$DESTPREFIX/lib/python2.2/
PYTHONMODDIR=$PYTHONDIR/site-packages
BANGPATH=$DESTPREFIX/bin/python

# helper stuff
#INSTALL=/opt/redhat/gnupro-03r1/contrib/H-sparc-sun-solaris2.6/bin/install

#INSTALL=/es/unsupported/sparc-sun-solaris2.5/src/fileutils-4.1/src/ginstall
INSTALL=/usr/ucb/install

FILELIST=$TMP/filelist-$NAME

mkdir -p $BUILDPREFIX
# clean up the build root
rm -rf $BUILDPREFIX/*

# er, duh
#make
python setup.py build 

# install into the fake prefix
# make  PREFIX=$BUILDPREFIX INSTALL=$INSTALL PYTHONDIR=$PYTHONDIR PYTHONMOD_DIR=$PYTHONMODDIR BANGPATH=$BANGPATH install

python setup.py install --prefix=$BUILDPREFIX
# find the packages installed into the build root
find $BUILDPREFIX -print > $FILELIST

# create the package prototype
mkdir -p $DATADIR
cat $FILELIST | pkgproto > $DATADIR/prototype

# add the info about pkginfo file
echo "i pkginfo" > $DATADIR/prototype.tmp

cat $DATADIR/prototype >> $DATADIR/prototype.tmp

# write it back again
cp $DATADIR/prototype.tmp  $DATADIR/prototype

PROTOTYPE=$DATADIR/prototype

# cp our pkginfo file to the datafir
cp pkginfo $DATADIR/pkginfo

# okay, now the fun begins. The problem is that all the paths are
# wrong. You can't simply change the prototype, cause it will
# look for the new paths, where nothing exist. So you have to
# build the package, then tweak the package info directly. 

# build the actual package, pre munge
pkgmk -o -r / -d $TMP  -f $PROTOTYPE

ls -la $TMP

# the package is in filesystem format, so we
# need to go into that dir and start mucking
# with stuff
cd $TMP/${NAME}/root/

# since the package as is has a local
# equilvient of $(BUILDPREFIX), we need to
# create a dir structure representing what
# the dest version is

mkdir -p $DESTPREFIX_REL

# move the stuff to the new dir that we actually
# want...
echo $BUILDPREFIX_REL $DESTPREFIX_REL
echo
pwd
echo

mv ${BUILDPREFIX_REL}/* ${DESTPREFIX_REL}/

 
rm -rf $BUILDPREFIX_REL


# pkgmap is the manifest of files and what not
# we need to blip it about to sub in the new file
# paths


cd ..


# use perl cause I'm dumb and lazy
 perl -pi -e  "s|${BUILDPREFIX}|${DESTPREFIX}|g" pkgmap 

# fix the file ownership as well

perl -pi -e "s|${USERNAME}|root|g" pkgmap 
# now that we've got the file all munged up, lets
# go ahead and convert the file to a datastream format

pkgtrans -s $TMP  $TMP/$NAME.package $NAME

# probabaly want to compress the package at this
# point
0707010000000D000081B4000000000000000000000001666AB47A00002B36000000000000000000000000000000000000001600000000rhnlib/connections.py#  pylint: disable=missing-module-docstring
#
# Connection objects
#
# Copyright (c) 2002--2016 Red Hat, Inc.
# Copyright (c) 2022 SUSE, LLC
#
# Author: Mihai Ibanescu <misa@redhat.com>


import base64

# pylint: disable-next=unused-import
import encodings.idna
import socket
from platform import python_version
from rhn.stringutils import bstr, ustr, sstr
from rhn import SSL
from rhn import nonblocking

try:  # python2
    import httplib

    # Import into the local namespace some httplib-related names
    from httplib import _CS_REQ_SENT, _CS_IDLE, ResponseNotReady

    import xmlrpclib
except ImportError:  # python3
    import http.client as httplib

    # Import into the local namespace some httplib-related names
    from http.client import _CS_REQ_SENT, _CS_IDLE, ResponseNotReady

    import xmlrpc.client as xmlrpclib


class HTTPResponse(httplib.HTTPResponse):
    def set_callback(self, rs, ws, ex, user_data, callback):
        if not isinstance(self.fp, nonblocking.NonBlockingFile):
            self.fp = nonblocking.NonBlockingFile(self.fp)
        self.fp.set_callback(rs, ws, ex, user_data, callback)


# pylint: disable-next=missing-class-docstring
class HTTPConnection(httplib.HTTPConnection):
    response_class = HTTPResponse

    def __init__(self, host, port=None, timeout=SSL.DEFAULT_TIMEOUT):
        if python_version() >= "2.6.1":
            httplib.HTTPConnection.__init__(self, host, port, timeout=timeout)
        else:
            httplib.HTTPConnection.__init__(self, host, port)
        self._cb_rs = []
        self._cb_ws = []
        self._cb_ex = []
        self._cb_user_data = None
        self._cb_callback = None
        self._user_agent = "rhn.connections $Revision$ (python)"
        self.timeout = timeout

    def set_callback(self, rs, ws, ex, user_data, callback):
        # XXX check the params
        self._cb_rs = rs
        self._cb_ws = ws
        self._cb_ex = ex
        self._cb_user_data = user_data
        self._cb_callback = callback

    def set_user_agent(self, user_agent):
        self._user_agent = user_agent

    # XXX Had to copy the function from httplib.py, because the nonblocking
    # framework had to be initialized
    def getresponse(self):
        "Get the response from the server."

        # check if a prior response has been completed
        if self.__response and self.__response.isclosed():
            self.__response = None

        #
        # if a prior response exists, then it must be completed (otherwise, we
        # cannot read this response's header to determine the connection-close
        # behavior)
        #
        # note: if a prior response existed, but was connection-close, then the
        # socket and response were made independent of this HTTPConnection
        # object since a new request requires that we open a whole new
        # connection
        #
        # this means the prior response had one of two states:
        #   1) will_close: this connection was reset and the prior socket and
        #                  response operate independently
        #   2) persistent: the response was retained and we await its
        #                  isclosed() status to become true.
        #
        if self.__state != _CS_REQ_SENT or self.__response:
            raise ResponseNotReady()

        if self.debuglevel > 0:
            response = self.response_class(self.sock, self.debuglevel)
        else:
            response = self.response_class(self.sock)

        # The only modification compared to the stock HTTPConnection
        if self._cb_callback:
            response.set_callback(
                self._cb_rs,
                self._cb_ws,
                self._cb_ex,
                self._cb_user_data,
                self._cb_callback,
            )

        response.begin()
        # pylint: disable-next=protected-access
        assert response.will_close != httplib._UNKNOWN
        self.__state = _CS_IDLE

        if response.will_close:
            # this effectively passes the connection to the response
            self.close()
        else:
            # remember this, so we can tell when it is complete
            self.__response = response

        return response

    def connect(self):
        httplib.HTTPConnection.connect(self)
        self.sock.settimeout(self.timeout)


# pylint: disable-next=missing-class-docstring
class HTTPProxyConnection(HTTPConnection):
    def __init__(
        self,
        proxy,
        host,
        port=None,
        username=None,
        password=None,
        timeout=SSL.DEFAULT_TIMEOUT,
    ):
        # The connection goes through the proxy
        HTTPConnection.__init__(self, proxy, timeout=timeout)
        # save the proxy values
        self.__proxy, self.__proxy_port = self.host, self.port
        # self.host and self.port will point to the real host
        if hasattr(self, "_get_hostport"):
            self.host, self.port = self._get_hostport(host, port)
        else:
            self._set_hostport(host, port)
        # save the host and port
        self._host, self._port = self.host, self.port
        # Authenticated proxies support
        self.__username = username
        self.__password = password

    def connect(self):
        # We are actually connecting to the proxy
        if hasattr(self, "_get_hostport"):
            self.host, self.port = self._get_hostport(self.__proxy, self.__proxy_port)
        else:
            self._set_hostport(self.__proxy, self.__proxy_port)
        HTTPConnection.connect(self)
        # Restore the real host and port
        if hasattr(self, "_get_hostport"):
            self.host, self.port = self._get_hostport(self._host, self._port)
        else:
            self._set_hostport(self._host, self._port)

    def putrequest(self, method, url, skip_host=0):
        # The URL has to include the real host
        hostname = self._host
        if self._port != self.default_port:
            hostname = hostname + ":" + str(self._port)
        # pylint: disable-next=consider-using-f-string
        newurl = "http://%s%s" % (hostname, url)
        # Piggyback on the parent class
        HTTPConnection.putrequest(self, method, newurl, skip_host=skip_host)
        # Add proxy-specific headers
        self._add_proxy_headers()

    def _add_proxy_headers(self):
        if not self.__username:
            return
        # Authenticated proxy
        # pylint: disable-next=consider-using-f-string
        userpass = "%s:%s" % (self.__username, self.__password)
        enc_userpass = base64.encodestring(bstr(userpass)).replace(bstr("\n"), bstr(""))
        # pylint: disable-next=consider-using-f-string
        self.putheader("Proxy-Authorization", "Basic %s" % sstr(enc_userpass))

    def _set_hostport(self, host, port):
        (self.host, self.port) = self._get_hostport(host, port)


# pylint: disable-next=missing-class-docstring
class HTTPSConnection(HTTPConnection):
    response_class = HTTPResponse
    default_port = httplib.HTTPSConnection.default_port

    def __init__(
        self, host, port=None, trusted_certs=None, timeout=SSL.DEFAULT_TIMEOUT
    ):
        HTTPConnection.__init__(self, host, port, timeout=timeout)
        trusted_certs = trusted_certs or []
        self.trusted_certs = trusted_certs

    def connect(self):
        "Connect to a host on a given (SSL) port"
        results = socket.getaddrinfo(
            self.host, self.port, socket.AF_UNSPEC, socket.SOCK_STREAM
        )

        for r in results:
            # pylint: disable-next=unused-variable
            af, socktype, proto, canonname, sa = r
            try:
                sock = socket.socket(af, socktype, proto)
            except socket.error:
                sock = None
                continue

            try:
                sock.connect((self.host, self.port))
                sock.settimeout(self.timeout)
            except socket.error:
                sock.close()
                sock = None
                continue
            break

        if sock is None:
            raise socket.error("Unable to connect to the host and port specified")

        self.sock = SSL.SSLSocket(sock, self.trusted_certs)
        self.sock.init_ssl(self.host)


class HTTPSProxyResponse(HTTPResponse):
    def begin(self):
        HTTPResponse.begin(self)
        self.will_close = 0


# pylint: disable-next=missing-class-docstring
class HTTPSProxyConnection(HTTPProxyConnection):
    default_port = HTTPSConnection.default_port

    def __init__(
        self,
        proxy,
        host,
        port=None,
        username=None,
        password=None,
        trusted_certs=None,
        timeout=SSL.DEFAULT_TIMEOUT,
    ):
        HTTPProxyConnection.__init__(
            self, proxy, host, port, username, password, timeout=timeout
        )
        trusted_certs = trusted_certs or []
        self.trusted_certs = trusted_certs

    def connect(self):
        # Set the connection with the proxy
        HTTPProxyConnection.connect(self)
        # Use the stock HTTPConnection putrequest
        # pylint: disable-next=consider-using-f-string
        host = "%s:%s" % (self._host, self._port)
        HTTPConnection.putrequest(self, "CONNECT", host)
        # Add proxy-specific stuff
        self._add_proxy_headers()
        # And send the request
        HTTPConnection.endheaders(self)
        # Save the response class
        response_class = self.response_class
        # And replace the response class with our own one, which does not
        # close the connection after
        self.response_class = HTTPSProxyResponse
        response = HTTPConnection.getresponse(self)
        # Restore the response class
        self.response_class = response_class
        # Close the response object manually
        response.close()
        if response.status != 200:
            # Close the connection manually
            self.close()
            raise xmlrpclib.ProtocolError(
                host, response.status, response.reason, response.msg
            )
        self.sock = SSL.SSLSocket(self.sock, self.trusted_certs)
        self.sock.init_ssl(self.host)

    def putrequest(self, method, url, skip_host=0):
        return HTTPConnection.putrequest(self, method, url, skip_host=skip_host)

    def _add_proxy_headers(self):
        HTTPProxyConnection._add_proxy_headers(self)
        # Add a User-Agent header
        self.putheader("User-Agent", self._user_agent)


def idn_puny_to_unicode(hostname):
    """Convert Internationalized domain name from Punycode (RFC3492) to Unicode"""
    if hostname is None:
        return None
    else:
        hostname = bstr(hostname)
        return hostname.decode("idna")


def idn_ascii_to_puny(hostname):
    """Convert domain name to Punycode (RFC3492). Hostname can be instance of string or Unicode"""
    if hostname is None:
        return None
    else:
        hostname = ustr(hostname)
        return ustr(hostname.encode("idna"))


idn_pune_to_unicode = idn_puny_to_unicode
idn_ascii_to_pune = idn_ascii_to_puny
0707010000000E000081B4000000000000000000000001666AB47A000003D1000000000000000000000000000000000000001100000000rhnlib/copyrightRhnlib is copyright 2010 by Red Hat Inc.
Rhnlib is currently developed by Spacewalk Development Team <spacewalk-devel@redhat.com>.

License: GPLv2

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License.

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.

See /usr/share/common-licenses/GPL-2, or
<http://www.gnu.org/copyleft/gpl.txt> for the terms of the latest version
of the GNU General Public License.
0707010000000F000081B4000000000000000000000001666AB47A00000A58000000000000000000000000000000000000001600000000rhnlib/nonblocking.py#  pylint: disable=missing-module-docstring
#
#
#

import select
import fcntl
import os


# pylint: disable-next=missing-class-docstring
class NonBlockingFile:
    def __init__(self, fd):
        # Keep a copy of the file descriptor
        self.fd = fd
        fcntl.fcntl(self.fd.fileno(), fcntl.F_SETFL, os.O_NDELAY)
        # Set the callback-related stuff
        self.read_fd_set = []
        self.write_fd_set = []
        self.exc_fd_set = []
        self.user_data = None
        self.callback = None

    # pylint: disable-next=redefined-outer-name
    def set_callback(self, read_fd_set, write_fd_set, exc_fd_set, user_data, callback):
        self.read_fd_set = read_fd_set
        # Make the objects non-blocking
        # pylint: disable-next=redefined-outer-name
        for f in self.read_fd_set:
            fcntl.fcntl(f.fileno(), fcntl.F_SETFL, os.O_NDELAY)

        self.write_fd_set = write_fd_set
        self.exc_fd_set = exc_fd_set
        self.user_data = user_data
        self.callback = callback

    def read(self, amt=0):
        while 1:
            status_changed = 0
            readfds = self.read_fd_set + [self.fd]
            writefds = self.write_fd_set
            excfds = self.exc_fd_set
            print("Calling select", readfds)
            readfds, writefds, excfds = select.select(readfds, writefds, excfds)
            print("Select returned", readfds, writefds, excfds)
            if self.fd in readfds:
                # Our own file descriptor has changed status
                # Mark this, but also try to call the callback with the rest
                # of the file descriptors that changed status
                status_changed = 1
                readfds.remove(self.fd)
            if self.callback and (readfds or writefds or excfds):
                self.callback(readfds, writefds, excfds, self.user_data)
            if status_changed:
                break
        print("Returning")
        return self.fd.read(amt)

    def write(self, data):
        return self.fd.write(data)

    def __getattr__(self, name):
        return getattr(self.fd, name)


# pylint: disable-next=unused-argument
def callback(r, w, e, user_data):
    print("Callback called", r, w, e)
    print(r[0].read())


if __name__ == "__main__":
    import socket

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(("localhost", 5555))
    f = s.makefile()
    ss = NonBlockingFile(f)

    s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s2.connect(("localhost", 5556))
    f = s2.makefile()
    ss.set_callback([f], [], [], None, callback)

    xx = ss.read()
    print(len(xx))
07070100000010000081B4000000000000000000000001666AB47A00000EE7000000000000000000000000000000000000001600000000rhnlib/rhnLockfile.py#  pylint: disable=missing-module-docstring,invalid-name
#
# Copyright (c) 2008--2016 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public License,
# version 2 (GPLv2). There is NO WARRANTY for this software, express or
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
# along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
#
# Red Hat trademarks are not licensed under GPLv2. No permission is
# granted to use or replicate Red Hat trademarks that are incorporated
# in this software or its documentation.
#

import os
import sys
import fcntl
from errno import EWOULDBLOCK, EEXIST
from rhn.stringutils import bstr

# pylint: disable-next=reimported,ungrouped-imports
import fcntl


class LockfileLockedException(Exception):
    """thrown ONLY when pid file is locked."""

    pass


class Lockfile:

    """class that provides simple access to a PID-style lockfile.

    methods: __init__(lockfile), acquire(), and release()
    NOTE: currently acquires upon init
    The *.pid file will be acquired, or an LockfileLockedException is raised.
    """

    def __init__(self, lockfile, pid=None):
        """create (if need be), and acquire lock on lockfile

        lockfile example: '/var/run/up2date.pid'
        """

        # cleanup the path and assign it.
        self.lockfile = os.path.abspath(
            os.path.expanduser(os.path.expandvars(lockfile))
        )

        self.pid = pid
        if not self.pid:
            self.pid = os.getpid()

        # create the directory structure
        dirname = os.path.dirname(self.lockfile)
        if not os.path.exists(dirname):
            try:
                os.makedirs(dirname)
            except OSError:
                e = sys.exc_info()[1]
                if hasattr(e, "errno") and e.errno == EEXIST:
                    # race condition... dirname exists now.
                    pass
                else:
                    raise

        # open the file -- non-destructive read-write, unless it needs
        # to be created XXX: potential race condition upon create?
        self.f = os.open(self.lockfile, os.O_RDWR | os.O_CREAT | os.O_SYNC)
        self.acquire()

    def acquire(self):
        """acquire the lock; else raise LockfileLockedException."""

        try:
            fcntl.flock(self.f, fcntl.LOCK_EX | fcntl.LOCK_NB)
        except IOError:
            if sys.exc_info()[1].errno == EWOULDBLOCK:
                # pylint: disable-next=raise-missing-from
                raise LockfileLockedException(
                    # pylint: disable-next=consider-using-f-string
                    "cannot acquire lock on %s." % self.lockfile,
                    None,
                    sys.exc_info()[2],
                )
            else:
                raise
        # unlock upon exit
        fcntl.fcntl(self.f, fcntl.F_SETFD, 1)
        # truncate and write the pid
        os.ftruncate(self.f, 0)
        os.write(self.f, bstr(str(self.pid) + "\n"))

    def release(self):
        # Remove the lock file
        os.unlink(self.lockfile)
        fcntl.flock(self.f, fcntl.LOCK_UN)
        os.close(self.f)


def main():
    """test code"""

    try:
        L = Lockfile("./test.pid")
    except LockfileLockedException:
        # pylint: disable-next=consider-using-f-string
        sys.stderr.write("%s\n" % sys.exc_info()[1])
        sys.exit(-1)
    else:
        print("lock acquired ")
        print("...sleeping for 10 seconds")
        # pylint: disable-next=import-outside-toplevel
        import time

        time.sleep(10)
        L.release()
        print("lock released ")


if __name__ == "__main__":
    # test code
    sys.exit(main() or 0)
07070100000011000081B4000000000000000000000001666AB47A00002EFF000000000000000000000000000000000000001600000000rhnlib/rhnlib.changes-------------------------------------------------------------------
Thu Jun 13 10:57:19 CEST 2024 - marina.latini@suse.com

- version 5.0.3-1
  * Align changelog entries

-------------------------------------------------------------------
Mon Jan 29 11:43:30 CET 2024 - rosuna@suse.com

- version 5.0.2-1
  * Apply Black and Pylint to enforce Python style

-------------------------------------------------------------------
Tue Jan 16 08:20:29 CET 2024 - jgonzalez@suse.com

- version 5.0.1-1
  * Specify a packager for Debian like distros

-------------------------------------------------------------------
Fri Dec 15 16:54:45 CET 2023 - rosuna@suse.com

- version 4.4.6-1
  * Remove unused makefiles

-------------------------------------------------------------------
Wed Nov 01 20:56:07 CET 2023 - marina.latini@suse.com

- version 4.4.5-1
  *  Use bundle CA certificate in rhnpush (bsc#1222731)

-------------------------------------------------------------------
Mon Sep 18 14:13:24 CEST 2023 - rosuna@suse.com

- version 4.4.4-1
  * Only use TLSv1+ for SSL connections

-------------------------------------------------------------------
Tue Feb 21 14:04:09 CET 2023 - jgonzalez@suse.com

- version 4.4.3-1
  * Ensure installation of make for building

-------------------------------------------------------------------
Fri Nov 18 15:05:05 CET 2022 - jgonzalez@suse.com

- version 4.4.2-1
  * Don't get stuck at the end of SSL transfers (bsc#1204032)

-------------------------------------------------------------------
Wed Sep 28 11:06:09 CEST 2022 - jgonzalez@suse.com

- version 4.4.1-1
  * Bump version to 4.4.0

-------------------------------------------------------------------
Tue Apr 19 11:54:48 CEST 2022 - jgonzalez@suse.com

- version 4.3.4-1
  * Fix the condition for preventing building python 2 subpackage
    for SLE15

-------------------------------------------------------------------
Fri Mar 11 15:44:41 CET 2022 - jgonzalez@suse.com

- version 4.3.3-1
  * Reorganize python files

-------------------------------------------------------------------
Tue Jan 18 13:36:55 CET 2022 - jgonzalez@suse.com

- version 4.3.2-1
  * do not build python 2 package for SLE15

-------------------------------------------------------------------
Mon Aug 09 10:56:00 CEST 2021 - jgonzalez@suse.com

- version 4.3.1-1
- Bump version to 4.3.0

-------------------------------------------------------------------
Fri Apr 16 13:14:01 CEST 2021 - jgonzalez@suse.com

- version 4.2.3-1
- require missing python-backports.ssl_match_hostname on SLE 11 (bsc#1183959)

-------------------------------------------------------------------
Wed Feb 17 12:16:30 CET 2021 - jgonzalez@suse.com

- version 4.2.2-1
- change SSL implementation to python ssl for better SAN and
  hostname matching support (bsc#1181807)

-------------------------------------------------------------------
Fri Sep 18 11:29:26 CEST 2020 - jgonzalez@suse.com

- version 4.2.1-1
- Update package version to 4.2.0

-------------------------------------------------------------------
Wed Mar 11 10:47:57 CET 2020 - jgonzalez@suse.com

- version 4.1.2-1
- Fix building

-------------------------------------------------------------------
Wed Nov 27 16:57:04 CET 2019 - jgonzalez@suse.com

- version 4.1.1-1
- Fix malformed XML response when data contains non-ASCII chars (bsc#1154968)
- Bump version to 4.1.0 (bsc#1154940)
- fix initialize ssl connection (bsc#1144155)
- Add SNI support for clients
- Fix bootstrapping SLE11SP4 trad client with SSL enabled (bsc#1148177)

-------------------------------------------------------------------
Wed May 15 15:04:15 CEST 2019 - jgonzalez@suse.com

- version 4.0.8-1
- SPEC cleanup

-------------------------------------------------------------------
Mon Apr 22 12:05:29 CEST 2019 - jgonzalez@suse.com

- version 4.0.7-1
- Add makefile and pylint configuration for PyLint

-------------------------------------------------------------------
Fri Mar 29 15:08:26 CET 2019 - jgonzalez@suse.com

- version 4.0.6-1
- Add group to python*-rhnlib to fix building at SLE11

-------------------------------------------------------------------
Sat Mar 02 00:08:46 CET 2019 - jgonzalez@suse.com

- version 4.0.5-1
- Read SSL decoded buffer completely when no pending bytes on the
  underlying connection.

-------------------------------------------------------------------
Wed Feb 27 12:58:36 CET 2019 - jgonzalez@suse.com

- version 4.0.4-1
- Fix encoding issues after porting to Python 3. 

-------------------------------------------------------------------
Thu Jan 31 09:38:49 CET 2019 - jgonzalez@suse.com

- version 4.0.3-1
- Sync changes from Spacewalk
- 1652859 - python3 http.client does not contain _set_hostport()

-------------------------------------------------------------------
Fri Oct 26 10:02:33 CEST 2018 - jgonzalez@suse.com

- version 4.0.2-1
- use rpm for debian packaging

-------------------------------------------------------------------
Fri Aug 10 15:09:47 CEST 2018 - jgonzalez@suse.com

- version 4.0.1-1
- Bump version to 4.0.0 (bsc#1104034)
- Fix copyright for the package specfile (bsc#1103696)

-------------------------------------------------------------------
Mon May 07 14:57:08 CEST 2018 - jgonzalez@suse.com

- version 2.8.11.1-1
- Sync with upstream (bsc#1083294)

-------------------------------------------------------------------
Mon Mar 26 08:38:18 CEST 2018 - jgonzalez@suse.com

- version 2.8.10.1-1
- Sync with upstream (bsc#1083294)

-------------------------------------------------------------------
Mon Mar 05 08:40:18 CET 2018 - jgonzalez@suse.com

- version 2.8.7.2-1
- remove empty clean section from spec (bsc#1083294)

-------------------------------------------------------------------
Fri Feb 23 10:47:01 CET 2018 - jgonzalez@suse.com

- version 2.8.7.1-1
- Sync with upstream

-------------------------------------------------------------------
Fri Feb 02 11:56:38 CET 2018 - jgonzalez@suse.com

- version 2.8.5.3-1
- replace netstat with ss command

-------------------------------------------------------------------
Wed Jan 17 11:13:43 CET 2018 - jgonzalez@suse.com

- version 2.8.5.2-1
- Fix update mechanism when updating the updateservice (bsc#1073619)

-------------------------------------------------------------------
Fri Nov 10 16:25:29 CET 2017 - mc@suse.de

- version 2.8.5.1-1
- merge back upstream changes

-------------------------------------------------------------------
Thu Oct 26 16:44:28 CEST 2017 - mc@suse.de

- version 2.8.4.1-1
- build python3 package
- check a state of handshake before shutdown SSL connection
- Python's OpenSSL.SSL.Connection method for getting state was renamed.

-------------------------------------------------------------------
Wed May 03 16:38:11 CEST 2017 - michele.bologna@suse.com

- version 2.7.2.1-1

-------------------------------------------------------------------
Fri Mar 31 09:33:33 CEST 2017 - mc@suse.de

- version 2.7.1.3-1
- support all TLS versions in rpclib (bsc#1025312)

-------------------------------------------------------------------
Mon Mar 06 16:19:18 CET 2017 - mc@suse.de

- version 2.7.1.2-1
- Updated links to github in spec files

-------------------------------------------------------------------
Wed Jan 11 15:31:19 CET 2017 - michele.bologna@suse.com

- version 2.7.1.1-1
- Version 2.7.1-1

-------------------------------------------------------------------
Thu Oct 06 15:08:47 CEST 2016 - mc@suse.de

- version 2.5.84.3-1
- Add function aliases for backward compatibility (bsc#998185)

-------------------------------------------------------------------
Mon Mar 21 16:41:39 CET 2016 - mc@suse.de

- version 2.5.84.2-1
- use TLSv1_METHOD in SSL Context (bsc#970989)

-------------------------------------------------------------------
Wed Mar 02 11:13:30 CET 2016 - mc@suse.de

- version 2.5.84.1-1
- just set one required attribute to be compatible with all
  xmlrpclib versions
- fixed SyntaxError: invalid syntax ' .. = b ' to work in
  python 2.4

-------------------------------------------------------------------
Tue Jan 26 13:53:44 CET 2016 - mc@suse.de

- version 2.5.83.1-1
- change encoding for python 2
- os.write wants buffer
- xmlrpc can't convert map objects
- dict.iteritems() is not in python3
- perform decoding on byte string
- modified exception raising to work in python2/3
- fixed string module calls to work in python3

-------------------------------------------------------------------
Sat Jan 16 11:31:43 CET 2016 - mc@suse.de

- version 2.5.81.1-1
- make rhnlib compatible with python 2 and 3

-------------------------------------------------------------------
Mon Nov 30 10:48:24 CET 2015 - mc@suse.de

- version 2.5.77.2-1
- python 2.7 httplib changed function _set_hostport to _get_hostport

-------------------------------------------------------------------
Wed Oct 07 15:09:31 CEST 2015 - mc@suse.de

- version 2.5.77.1-1
- python 3 compatibility
- SmartIO: don't use tmpDir configuration from /etc/sysconfig/rhn/up2date

-------------------------------------------------------------------
Tue Mar 31 14:30:45 CEST 2015 - mc@suse.de

- version 2.5.69.7-1
- Documentation changes - fix name and refer to RFC.

-------------------------------------------------------------------
Tue Feb 03 12:42:14 CET 2015 - mc@suse.de

- version 2.5.69.6-1
- Getting rid of Tabs and trailing spaces

-------------------------------------------------------------------
Thu Dec 18 13:50:31 CET 2014 - mc@suse.de

- version 2.5.69.5-1

-------------------------------------------------------------------
Wed Sep  3 01:41:38 CEST 2014 - ro@suse.de

- sanitize release line in specfile

-------------------------------------------------------------------
Tue Jun 17 11:02:20 CEST 2014 - jrenner@suse.de

- version 2.5.69.4-1
- Ensure bytes strings are sent to pyOpenSSL (bnc#880388)

-------------------------------------------------------------------
Thu Apr 10 17:30:43 CEST 2014 - mc@suse.de

- version 2.5.69.3-1

-------------------------------------------------------------------
Thu Apr 10 17:30:28 CEST 2014 - mc@suse.de

- fix release in specfile for SLE12 (bnc#872970)

-------------------------------------------------------------------
Mon Jan 20 15:16:29 CET 2014 - mc@suse.de

- version 2.5.69.2-1
- change license to SPDX format

-------------------------------------------------------------------
Mon Dec 09 16:05:46 CET 2013 - mc@suse.de

- version 2.5.69.1-1
- switch to 2.1

-------------------------------------------------------------------
Thu Nov 28 16:27:53 CET 2013 - mc@suse.de

- version 2.5.51.6-1
- fixed UserDictCase behaviour when key is not found

-------------------------------------------------------------------
Wed Aug 21 15:37:30 CEST 2013 - mc@suse.de

- version 2.5.51.5-1
- timeout fixes

-------------------------------------------------------------------
Mon Apr 22 15:44:27 CEST 2013 - mc@suse.de

- version 2.5.51.4-1
- Make timeout of yum-rhn-plugin calls through rhn-client-tools
  configurable (bnc#815460)
- make Proxy timeouts configurable (bnc#815460)

-------------------------------------------------------------------
Thu Jun 21 11:34:34 CEST 2012 - jrenner@suse.de

- version 2.5.51.3-1
- allow linking against openssl

-------------------------------------------------------------------
Fri Apr 27 16:56:23 CEST 2012 - mc@suse.de

- version 2.5.51.2-1
- fixing typos

-------------------------------------------------------------------
Wed Mar 21 17:06:43 CET 2012 - mc@suse.de

- version 2.5.51.1-1
- Bumping package version

-------------------------------------------------------------------
Mon Sep 19 15:36:29 CEST 2011 - mc@suse.de

- require python-openssl on SUSE

-------------------------------------------------------------------
Sat Jan 22 12:39:39 CET 2011 - mc@suse.de

- fix macros

-------------------------------------------------------------------
Tue Sep 14 17:56:57 CEST 2010 - mantel@suse.de

- Initial release of rhnlib

-------------------------------------------------------------------
07070100000012000081B4000000000000000000000001666AB47A000000C6000000000000000000000000000000000000001600000000rhnlib/rhnlib.pkginfoPKG="RHATrhnl"
NAME="Red Hat update agent"
VERSION="1.0"
ARCH="sparc"
CLASSES="none"
CATEGORY="utility"
VENDOR="RHAT"
EMAIL="alikins@redhat.com"
ISTATES="S s 1 2 3"
RSTATES="S s 1 2 3"
BASEDIR="/"

07070100000013000081B4000000000000000000000001666AB47A00001782000000000000000000000000000000000000001300000000rhnlib/rhnlib.spec#
# spec file for package rhnlib
#
# Copyright (c) 2024 SUSE LLC
# Copyright (c) 2008-2018 Red Hat, Inc.
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.

# Please submit bugfixes or comments via https://bugs.opensuse.org/
#


%if 0%{?fedora} || 0%{?suse_version} > 1320 || 0%{?rhel} >= 8
%global build_py3   1
%{!?__python3:%global __python3 /usr/bin/python3}

%if %{undefined python3_sitelib}
%global python3_sitelib %(%{__python3} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")
%endif
%endif

%if !(0%{?rhel} >= 8 || 0%{?sle_version} >= 150000 )
%global build_py2   1
%{!?__python2:%global __python2 /usr/bin/python2}
%if %{undefined python2_sitelib}
%global python2_sitelib %(%{__python2} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")
%endif
%endif

%if "%{_vendor}" == "debbuild"
# For making sure we can set the right args for deb distros
%global is_deb 1
%endif

Summary:        Python libraries for the Spacewalk project
License:        GPL-2.0-only
Name:           rhnlib
Version:        5.0.3
Release:        1
%if "%{_vendor}" == "debbuild"
Group:          python
Packager:       Uyuni Project <devel@lists.uyuni-project.org>
%else
Group:          Development/Libraries
%endif
URL:            https://github.com/uyuni-project/uyuni
Source0:        %{name}-%{version}.tar.gz

BuildRoot:      %{_tmppath}/%{name}-%{version}-build

%if 0%{?fedora} || 0%{?rhel} || 0%{?suse_version} >= 1210
BuildArch:      noarch
%endif
BuildRequires:  make

%description
rhnlib is a collection of python modules used by the Spacewalk (http://spacewalk.redhat.com) software.

%if 0%{?build_py2}
%package -n python2-rhnlib
Summary:        Python libraries for the Spacewalk project
Group:          Development/Libraries

%if "%{_vendor}" != "debbuild"
%if 0%{?fedora} >= 28 || 0%{?rhel} >= 8
BuildRequires:  python2-devel
Requires:       python2-pyOpenSSL
%else
BuildRequires:  python-devel
%if 0%{?suse_version}
%if 0%{?suse_version} > 1200
Requires:       python-pyOpenSSL
%else
Requires:       python-backports.ssl_match_hostname
Requires:       python-openssl
%endif # 0{?suse_version} > 1200
%else
Requires:       pyOpenSSL
%endif # 0{?suse_version}
%endif # 0{?fedora} >= 28 || 0{?rhel} >= 8
%endif # {_vendor} != "debbuild"

%if "%{_vendor}" == "debbuild"
BuildRequires:  python-dev
BuildRequires:  rpm
Requires(preun):python-minimal
Requires(post): python-minimal
Requires:       python-openssl
Obsoletes:      python-rhn
Conflicts:      python-rhn
%endif

Conflicts:      rhn-client-tools < 1.3.3
Conflicts:      rhn-custom-info < 5.4.7
Conflicts:      rhncfg < 5.10.45
Conflicts:      rhnclient < 0.10
Conflicts:      rhnpush < 5.5.10
Conflicts:      spacewalk-proxy < 1.3.6
Conflicts:      spacewalk-proxy-installer < 1.3.2

Provides:       rhnlib = %{version}-%{release}
Obsoletes:      rhnlib < %{version}-%{release}

%description -n python2-rhnlib
rhnlib is a collection of python modules used by the Spacewalk software.

%endif # 0%{?build_py2}

%if 0%{?build_py3}
%package -n python3-rhnlib
Summary:        Python libraries for the Spacewalk project
Group:          python

%if "%{_vendor}" != "debbuild"
BuildRequires:  python3-devel
%if 0%{?suse_version}
BuildRequires:  python-rpm-macros
%endif
%endif
Requires:       python3-pyOpenSSL

%if "%{_vendor}" == "debbuild"
BuildRequires:  python3-dev
BuildRequires:  rpm
Requires(preun):python3-minimal
Requires(post): python3-minimal
Requires:       python3-openssl
%endif

Conflicts:      rhn-client-tools < 1.3.3
Conflicts:      rhn-custom-info < 5.4.7
Conflicts:      rhncfg < 5.10.45
Conflicts:      rhnclient < 0.10
Conflicts:      rhnpush < 5.5.10
Conflicts:      spacewalk-proxy < 1.3.6
Conflicts:      spacewalk-proxy-installer < 1.3.2

%description -n python3-rhnlib
rhnlib is a collection of python modules used by the Spacewalk software.

%endif # 0%{?build_py2}

%prep
%setup -q

# Recreate the rhn module
mkdir rhn
pushd rhn
for pyfile in $(ls ../*.py)
do
  ln -s $pyfile
done
popd

if [ ! -e setup.py ]; then
    sed -e 's/@VERSION@/%{version}/' -e 's/@NAME@/%{name}/' setup.py.in > setup.py
fi
if [ ! -e setup.cfg ]; then
    sed 's/@RELEASE@/%{release}/' setup.cfg.in > setup.cfg
fi

%build
%if 0%{?build_py2}
make -f Makefile.rhnlib PYTHON=%{__python2}
%endif
%if 0%{?build_py3}
make -f Makefile.rhnlib PYTHON=%{__python3}
%endif

%install
%if 0%{?build_py2}
%{__python2} setup.py install %{!?is_deb:-O1}%{?is_deb:--no-compile -O0} --skip-build --root $RPM_BUILD_ROOT %{?is_deb:--install-layout=deb} --prefix=%{_prefix}
%endif
%if 0%{?build_py3}
%{__python3} setup.py install %{!?is_deb:-O1}%{?is_deb:--no-compile -O0} --skip-build --root $RPM_BUILD_ROOT %{?is_deb:--install-layout=deb} --prefix=%{_prefix}
%endif

%if 0%{?build_py2}
%files -n python2-rhnlib
%defattr(-,root,root)
%doc ChangeLog COPYING README TODO
%{python2_sitelib}/*
%endif

%if 0%{?build_py3}
%files -n python3-rhnlib
%doc ChangeLog COPYING README TODO
%{python3_sitelib}/*
%endif

%if "%{_vendor}" == "debbuild"

%post -n python2-rhnlib
# Do late-stage bytecompilation, per debian policy
pycompile -p python2-rhnlib -V -3.0

%preun -n python2-rhnlib
# Ensure all *.py[co] files are deleted, per debian policy
pyclean -p python2-rhnlib

%if 0%{?build_py3}
%post -n python3-rhnlib
# Do late-stage bytecompilation, per debian policy
py3compile -p python3-rhnlib -V -4.0

%preun -n python3-rhnlib
# Ensure all *.py[co] files are deleted, per debian policy
py3clean -p python3-rhnlib
%endif
%endif

%changelog
07070100000014000081B4000000000000000000000001666AB47A00006A72000000000000000000000000000000000000001100000000rhnlib/rpclib.py#  pylint: disable=missing-module-docstring
#
# This module contains all the RPC-related functions the RHN code uses
#
# Copyright (c) 2005--2018 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public License,
# version 2 (GPLv2). There is NO WARRANTY for this software, express or
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
# along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
#
# Red Hat trademarks are not licensed under GPLv2. No permission is
# granted to use or replicate Red Hat trademarks that are incorporated
# in this software or its documentation.
#

__version__ = "$Revision$"

import socket
import re
import sys

from rhn import transports
from rhn.stringutils import sstr
from rhn.UserDictCase import UserDictCase

try:  # python2
    import xmlrpclib
    from types import (
        ListType,
        TupleType,
        StringType,
        UnicodeType,
        DictType,
        DictionaryType,
    )
    from urllib import splittype, splithost
except ImportError:  # python3
    import xmlrpc.client as xmlrpclib

    ListType = list
    TupleType = tuple
    StringType = bytes
    UnicodeType = str
    DictType = dict
    DictionaryType = dict
    # pylint: disable-next=ungrouped-imports
    from urllib.parse import splittype, splithost

# Redirection handling

MAX_REDIRECTIONS = 5


def check_ipv6(n):
    """Returns true if n is IPv6 address, false otherwise."""
    try:
        socket.inet_pton(socket.AF_INET6, n)
        return True
    # pylint: disable-next=bare-except
    except:
        return False


def split_host(hoststring):
    """Function used to split host information in an URL per RFC 2396
    handle full hostname like user:passwd@host:port
    """
    l = hoststring.split("@", 1)
    host = None
    port = None
    user = None
    passwd = None

    if len(l) == 2:
        hostport = l[1]
        # userinfo present
        userinfo = l[0].split(":", 1)
        user = userinfo[0]
        if len(userinfo) == 2:
            passwd = userinfo[1]
    else:
        hostport = l[0]

    # Now parse hostport
    if hostport[0] == "[":
        # IPv6 with port
        # pylint: disable-next=anomalous-backslash-in-string
        host, port = re.split("(?<=\]):", hostport, 1)
        host = host.lstrip("[").rstrip("]")
    elif check_ipv6(hostport):
        # just IPv6
        host = hostport
    else:
        # IPv4
        arr = hostport.split(":", 1)
        host = arr[0]
        if len(arr) == 2:
            port = arr[1]

    return (host, port, user, passwd)


def get_proxy_info(proxy):
    # pylint: disable-next=singleton-comparison
    if proxy == None:
        raise ValueError("Host string cannot be null")

    arr = proxy.split("://", 1)
    if len(arr) == 2:
        # scheme found, strip it
        proxy = arr[1]

    return split_host(proxy)


class MalformedURIError(IOError):
    pass


# Originaly taken from xmlrpclib.ServerProxy, now changed most of the code
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,
        # pylint: disable-next=invalid-name
        refreshCallback=None,
        # pylint: disable-next=invalid-name
        progressCallback=None,
        timeout=None,
    ):
        # establish a "logical" server connection

        #
        # First parse the proxy information if available
        #
        # pylint: disable-next=singleton-comparison
        if proxy != None:
            (ph, pp, pu, pw) = get_proxy_info(proxy)

            if pp is not None:
                # pylint: disable-next=consider-using-f-string
                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 = sstr(uri)
        # pylint: disable-next=invalid-name
        self._refreshCallback = None
        # pylint: disable-next=invalid-name
        self._progressCallback = None
        # pylint: disable-next=invalid-name
        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,
        # pylint: disable-next=redefined-builtin
        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

    # pylint: disable-next=invalid-name
    def set_refresh_callback(self, refreshCallback):
        self._refreshCallback = refreshCallback
        self._transport.set_refresh_callback(refreshCallback)

    # pylint: disable-next=invalid-name
    def set_buffer_size(self, bufferSize):
        self._bufferSize = bufferSize
        self._transport.set_buffer_size(bufferSize)

    # pylint: disable-next=invalid-name
    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 = list(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 "Accept-Ranges" in headers:
            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
        # pylint: disable-next=redefined-builtin
        type, uri = 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
        self._type = type.lower()
        if self._type not in ("http", "https"):
            raise IOError("unsupported XML-RPC protocol")
        self._host, self._handler = 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, "", sstr(item))
            elif item_type == TupleType:
                item = tuple(self._strip_characters(i) for i in item)
            elif item_type == ListType:
                item = [self._strip_characters(i) for i in item]
            elif item_type == DictType or item_type == DictionaryType:
                item = dict(
                    [(self._strip_characters(name, val)) for name, val in item.items()]
                )
            # 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",
                # pylint: disable-next=consider-using-f-string
                "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:
                if self.use_handler_path:
                    raise
                else:
                    save_response = sys.exc_info()[1].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:
                # pylint: disable-next=consider-using-f-string
                print("%s redirected to %s" % (self._uri, self._redirected))

            typ, uri = splittype(self._redirected)

            # pylint: disable-next=singleton-comparison
            if typ != None:
                typ = typ.lower()
            if typ not in ("http", "https"):
                raise InvalidRedirectionError(
                    # pylint: disable-next=consider-using-f-string
                    "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 = 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)

            # pylint: disable-next=use-implicit-booleaness-not-comparison
            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

    def __repr__(self):
        # pylint: disable-next=consider-using-f-string
        return "<%s for %s%s>" % (self.__class__.__name__, self._host, self._handler)

    __str__ = __repr__

    def __getattr__(self, name):
        # magic method dispatcher
        return _Method(self._request, name)

    # note: to call a remote object with an non-standard name, use
    # result getattr(server, "strange-python-name")(args)

    def set_transport_flags(self, transfer=0, encoding=0, **kwargs):
        if not self._transport:
            # Nothing to do
            return
        kwargs.update(
            {
                "transfer": transfer,
                "encoding": encoding,
            }
        )
        self._transport.set_transport_flags(**kwargs)

    def get_transport_flags(self):
        if not self._transport:
            # Nothing to do
            return {}
        return self._transport.get_transport_flags()

    def reset_transport_flags(self):
        # Does nothing
        pass

    # Allow user-defined additional headers.
    def set_header(self, name, arg):
        if type(arg) in [type([]), type(())]:
            # Multivalued header
            self._headers[name] = [str(a) for a in arg]
        else:
            self._headers[name] = str(arg)

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

    # Sets the i18n options
    def setlang(self, lang):
        self._lang = lang
        if self._transport and hasattr(self._transport, "setlang"):
            self._transport.setlang(lang)

    # Sets the CA chain to be used
    # pylint: disable-next=invalid-name
    def use_CA_chain(self, ca_chain=None):
        raise NotImplementedError("This method is deprecated")

    def add_trusted_cert(self, certfile):
        self._trusted_cert_files.append(certfile)
        if self._transport and hasattr(self._transport, "add_trusted_cert"):
            self._transport.add_trusted_cert(certfile)

    def close(self):
        if self._transport:
            self._transport.close()
            self._transport = None


# RHN GET server
# pylint: disable-next=missing-class-docstring
class GETServer(Server):
    # pylint: disable-next=dangerous-default-value
    def __init__(
        self,
        uri,
        transport=None,
        proxy=None,
        username=None,
        password=None,
        client_version=2,
        headers={},
        refreshCallback=None,
        progressCallback=None,
        timeout=None,
    ):
        Server.__init__(
            self,
            uri,
            proxy=proxy,
            username=username,
            password=password,
            transport=transport,
            refreshCallback=refreshCallback,
            progressCallback=progressCallback,
            timeout=timeout,
        )
        self._client_version = client_version
        self._headers = headers
        # Back up the original handler, since we mangle it
        self._orig_handler = self._handler
        # Download resumption
        self.set_range(offset=None, amount=None)

    def _req_body(self, params, methodname):
        if not params or len(params) < 1:
            # pylint: disable-next=broad-exception-raised
            raise Exception("Required parameter channel not found")
        # Strip the multiple / from the handler
        h_comps = list(filter(lambda x: x != "", self._orig_handler.split("/")))
        # Set the handler we are going to request
        hndl = h_comps + ["$RHN", params[0], methodname] + list(params[1:])
        self._handler = "/" + "/".join(hndl)

        # save the constructed handler in case of redirect
        self.send_handler = self._handler

        # Add headers
        # override the handler to replace /XMLRPC with pkg path
        if self._redirected and not self.use_handler_path:
            self._handler = self._new_req_body()

        for h, v in self._headers.items():
            self._transport.set_header(h, v)

        if self._offset is not None:
            if self._offset >= 0:
                brange = str(self._offset) + "-"
                if self._amount is not None:
                    brange = brange + str(self._offset + self._amount - 1)
            else:
                # The last bytes
                # amount is ignored in this case
                brange = "-" + str(-self._offset)

            self._transport.set_header("Range", "bytes=" + brange)
            # Flag that we allow for partial content
            self._transport.set_transport_flags(allow_partial_content=1)
        # GET requests have empty body
        return ""

    def _new_req_body(self):
        # pylint: disable-next=redefined-builtin,unused-variable
        type, tmpuri = splittype(self._redirected)
        # pylint: disable-next=unused-variable
        site, handler = splithost(tmpuri)
        return handler

    def set_range(self, offset=None, amount=None):
        if offset is not None:
            try:
                offset = int(offset)
            except ValueError:
                # Error
                # pylint: disable-next=raise-missing-from
                raise RangeError(
                    # pylint: disable-next=consider-using-f-string
                    "Invalid value `%s' for offset" % offset,
                    None,
                    sys.exc_info()[2],
                )

        if amount is not None:
            try:
                amount = int(amount)
            except ValueError:
                # Error
                # pylint: disable-next=raise-missing-from
                raise RangeError(
                    # pylint: disable-next=consider-using-f-string
                    "Invalid value `%s' for amount" % amount,
                    None,
                    sys.exc_info()[2],
                )

            if amount <= 0:
                # pylint: disable-next=consider-using-f-string
                raise RangeError("Invalid value `%s' for amount" % amount)

        self._amount = amount
        self._offset = offset

    def reset_transport_flags(self):
        self._transport.set_transport_flags(allow_partial_content=0)

    def __getattr__(self, name):
        # magic method dispatcher
        return SlicingMethod(self._request, name)

    def default_transport(
        self,
        # pylint: disable-next=redefined-builtin
        type,
        proxy=None,
        username=None,
        password=None,
        timeout=None,
    ):
        ret = Server.default_transport(
            self,
            type,
            proxy=proxy,
            username=username,
            password=password,
            timeout=timeout,
        )
        ret.set_method("GET")
        return ret


class RangeError(Exception):
    pass


class InvalidRedirectionError(Exception):
    pass


# pylint: disable-next=invalid-name
def getHeaderValues(headers, name):
    # pylint: disable-next=import-outside-toplevel
    import mimetools

    if not isinstance(headers, mimetools.Message):
        if name in headers:
            return [headers[name]]
        return []

    return [x.split(":", 1)[1].strip() for x in headers.getallmatchingheaders(name)]


class _Method:
    """some magic to bind an XML-RPC method to an RPC server.
    supports "nested" methods (e.g. examples.getStateName)
    """

    def __init__(self, send, name):
        self._send = send
        self._name = name

    def __getattr__(self, name):
        # pylint: disable-next=consider-using-f-string
        return _Method(self._send, "%s.%s" % (self._name, name))

    def __call__(self, *args):
        return self._send(self._name, args)

    def __repr__(self):
        # pylint: disable-next=consider-using-f-string
        return "<%s %s (%s)>" % (self.__class__.__name__, self._name, self._send)

    __str__ = __repr__


class SlicingMethod(_Method):
    """
    A "slicing method" allows for byte range requests
    """

    def __init__(self, send, name):
        _Method.__init__(self, send, name)
        self._offset = None

    def __getattr__(self, name):
        # pylint: disable-next=consider-using-f-string
        return SlicingMethod(self._send, "%s.%s" % (self._name, name))

    def __call__(self, *args, **kwargs):
        self._offset = kwargs.get("offset")
        self._amount = kwargs.get("amount")

        # im_self is a pointer to self, so we can modify the class underneath
        try:
            self._send.im_self.set_range(offset=self._offset, amount=self._amount)
        except AttributeError:
            pass

        result = self._send(self._name, args)

        # Reset "sticky" transport flags
        try:
            self._send.im_self.reset_transport_flags()
        except AttributeError:
            pass

        return result


# pylint: disable-next=invalid-name
def reportError(headers):
    """Reports the error from the headers."""
    errcode = 0
    errmsg = ""
    s = "X-RHN-Fault-Code"
    if s in headers:
        errcode = int(headers[s])
    s = "X-RHN-Fault-String"
    if s in headers:
        # pylint: disable-next=invalid-name
        _sList = getHeaderValues(headers, s)
        if _sList:
            # pylint: disable-next=invalid-name
            _s = "".join(_sList)
            # pylint: disable-next=import-outside-toplevel
            import base64

            # pylint: disable-next=consider-using-f-string
            errmsg = "%s" % base64.decodestring(_s)

    return errcode, errmsg
07070100000015000081B4000000000000000000000001666AB47A00000101000000000000000000000000000000000000001400000000rhnlib/setup.cfg.in# Configuration file for distutils
[bdist_rpm]
release = @RELEASE@
packager = Red Hat, Inc. <http://bugzilla.redhat.com/bugzilla>
requires = pyOpenSSL python
doc-files = ChangeLog COPYING README TODO
changelog = changelog.spec
build-requires = python-devel
07070100000016000081B4000000000000000000000001666AB47A000001E1000000000000000000000000000000000000001300000000rhnlib/setup.py.in#!/usr/bin/python
#
#

from distutils.core import setup

setup(name = "@NAME@",
      version = "@VERSION@",
      description = "Python libraries for the Spacewalk project",
      long_description = """\
rhnlib is a collection of python modules used by the
Spacewalk (http://spacewalk.redhat.com) software.""",

      author = 'Mihai Ibanescu',
      author_email = 'misa@redhat.com',
      url = 'http://rhn.redhat.com',
      packages = ["rhn"],
      license = "GPL",

      )
07070100000017000081B4000000000000000000000001666AB47A000007A4000000000000000000000000000000000000001600000000rhnlib/stringutils.py#  pylint: disable=missing-module-docstring
#
# This module contains all the RPC-related functions the RHN code uses
#
# Copyright (c) 2016 Red Hat, Inc.
# Copyright (c) 2022 SUSE, LLC
#
# This software is licensed to you under the GNU General Public License,
# version 2 (GPLv2). There is NO WARRANTY for this software, express or
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
# along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
#
# Red Hat trademarks are not licensed under GPLv2. No permission is
# granted to use or replicate Red Hat trademarks that are incorporated
# in this software or its documentation.
#

from sys import version_info

try:
    PY3 = version_info.major >= 3
except AttributeError:
    PY3 = False


def ustr(obj):
    # converts object to unicode like object
    if PY3:  # python3
        if isinstance(obj, str):
            return obj
        else:
            return str(obj, "utf8", errors="ignore")
    else:  # python2
        # pylint: disable-next=undefined-variable
        if isinstance(obj, unicode):
            return obj
        # pylint: disable-next=undefined-variable
        return unicode(obj, "utf8", "ignore")


def bstr(obj):
    # converts object to bytes like object
    if PY3:  # python3
        if isinstance(obj, bytes):
            return obj
        else:
            return bytes(obj, "utf8", errors="ignore")
    else:  # python2
        if isinstance(obj, str):
            return obj
        return str(obj.encode("utf8", "ignore"))


def sstr(obj):
    # converts object to string
    if PY3:  # python3
        if isinstance(obj, str):
            return obj
        else:
            return str(obj, "utf8", errors="ignore")
    else:  # python2
        if isinstance(obj, str):
            return obj
        return str(obj.encode("utf8", "ignore"))
07070100000018000081B4000000000000000000000001666AB47A000003A5000000000000000000000000000000000000000D00000000rhnlib/tb.py#  pylint: disable=missing-module-docstring
#
# Copyright (c) 2016 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public License,
# version 2 (GPLv2). There is NO WARRANTY for this software, express or
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
# along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
#
# Red Hat trademarks are not licensed under GPLv2. No permission is
# granted to use or replicate Red Hat trademarks that are incorporated
# in this software or its documentation.
#

import sys

try:
    PY3 = sys.version_info.major >= 3
except AttributeError:
    PY3 = False

if PY3:

    def raise_with_tb(e):
        raise e

else:
    # pylint: disable-next=exec-used
    exec(
        """
def raise_with_tb(e):
    raise e, None, sys.exc_info()[2]
"""
    )
07070100000019000081B4000000000000000000000001666AB47A00008B9E000000000000000000000000000000000000001500000000rhnlib/transports.py#  pylint: disable=missing-module-docstring
#
# Helper transport objects
#
# Copyright (c) 2002--2016 Red Hat, Inc.
#
# Author: Mihai Ibanescu <misa@redhat.com>
# Based on what was previously shipped as cgiwrap:
#   - Cristian Gafton <gafton@redhat.com>
#   - Erik Troan <ewt@redhat.com>


# Transport objects
import os
import sys
import time
from rhn import connections
from rhn.stringutils import sstr, bstr
from rhn.SmartIO import SmartIO
from rhn.UserDictCase import UserDictCase

try:  # python2
    import xmlrpclib
    from types import IntType, StringType, ListType
except ImportError:  # python3
    import xmlrpc.client as xmlrpclib

    IntType = int
    StringType = bytes
    ListType = list

__version__ = "$Revision$"

# XXX
COMPRESS_LEVEL = 6


# Exceptions
class NotProcessed(Exception):
    pass


# pylint: disable-next=missing-class-docstring
class Transport(xmlrpclib.Transport):
    # pylint: disable-next=consider-using-f-string
    user_agent = "rhn.rpclib.py/%s" % __version__

    # pylint: disable-next=super-init-not-called
    def __init__(
        self,
        transfer=0,
        encoding=0,
        refreshCallback=None,
        progressCallback=None,
        use_datetime=None,
        timeout=None,
    ):
        self._use_builtin_types = False
        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
        # pylint: disable-next=invalid-name
        self.refreshCallback = refreshCallback
        # pylint: disable-next=invalid-name
        self.progressCallback = progressCallback
        # pylint: disable-next=invalid-name
        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
    # pylint: disable-next=invalid-name
    def set_progress_callback(self, progressCallback, bufferSize=16384):
        self.progressCallback = progressCallback
        self.bufferSize = bufferSize

    # set the refresh callback
    # pylint: disable-next=invalid-name
    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
    # pylint: disable-next=invalid-name
    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"):
            # pylint: disable-next=consider-using-f-string
            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] = [str(a) for a in arg]
        else:
            self._headers[name] = str(arg)

    def add_header(self, name, arg):
        if name in self._headers:
            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:
            # pylint: disable-next=consider-using-f-string
            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
        # pylint: disable-next=unused-variable
        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)
        req.set_transport_flags(**self._transport_flags)

        # Add the extra headers
        req.set_header("User-Agent", self.user_agent)
        for header, value in list(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():
                # pylint: disable-next=consider-using-f-string
                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


# pylint: disable-next=missing-class-docstring
class SafeTransport(Transport):
    def __init__(
        self,
        transfer=0,
        encoding=0,
        refreshCallback=None,
        progressCallback=None,
        trusted_certs=None,
        timeout=None,
    ):
        Transport.__init__(
            self,
            transfer,
            encoding,
            refreshCallback=refreshCallback,
            progressCallback=progressCallback,
            timeout=timeout,
        )
        self.trusted_certs = []
        for certfile in trusted_certs or []:
            self.add_trusted_cert(certfile)

    def add_trusted_cert(self, certfile):
        if not os.access(certfile, os.R_OK):
            raise ValueError(
                # pylint: disable-next=consider-using-f-string
                "SafeTransport: Certificate file %s is not accessible"
                % certfile
            )
        self.trusted_certs.append(certfile)

    def get_connection(self, host):
        # implement BASIC HTTP AUTHENTICATION
        # pylint: disable-next=unused-variable
        host, extra_headers, x509 = self.get_host_info(host)
        if self.verbose:
            # pylint: disable-next=consider-using-f-string
            print("Connecting via https to %s" % (host,))
        if self.timeout:
            return connections.HTTPSConnection(
                host, trusted_certs=self.trusted_certs, timeout=self.timeout
            )
        else:
            return connections.HTTPSConnection(host, trusted_certs=self.trusted_certs)


# pylint: disable-next=missing-class-docstring
class ProxyTransport(Transport):
    def __init__(
        self,
        proxy,
        proxyUsername=None,
        proxyPassword=None,
        transfer=0,
        encoding=0,
        refreshCallback=None,
        progressCallback=None,
        timeout=None,
    ):
        Transport.__init__(
            self,
            transfer,
            encoding,
            refreshCallback=refreshCallback,
            progressCallback=progressCallback,
            timeout=timeout,
        )
        self._proxy = proxy
        self._proxy_username = proxyUsername
        self._proxy_password = proxyPassword

    def get_connection(self, host):
        if self.verbose:
            print(
                # pylint: disable-next=consider-using-f-string
                "Connecting via http to %s proxy %s, username %s, pass %s"
                % (host, self._proxy, self._proxy_username, self._proxy_password)
            )
        if self.timeout:
            return connections.HTTPProxyConnection(
                self._proxy,
                host,
                username=self._proxy_username,
                password=self._proxy_password,
                timeout=self.timeout,
            )
        else:
            return connections.HTTPProxyConnection(
                self._proxy,
                host,
                username=self._proxy_username,
                password=self._proxy_password,
            )


# pylint: disable-next=missing-class-docstring
class SafeProxyTransport(ProxyTransport):
    def __init__(
        self,
        proxy,
        proxyUsername=None,
        proxyPassword=None,
        transfer=0,
        encoding=0,
        refreshCallback=None,
        progressCallback=None,
        trusted_certs=None,
        timeout=None,
    ):
        ProxyTransport.__init__(
            self,
            proxy,
            proxyUsername=proxyUsername,
            proxyPassword=proxyPassword,
            transfer=transfer,
            encoding=encoding,
            refreshCallback=refreshCallback,
            progressCallback=progressCallback,
            timeout=timeout,
        )
        self.trusted_certs = []
        for certfile in trusted_certs or []:
            self.add_trusted_cert(certfile)

    def add_trusted_cert(self, certfile):
        if not os.access(certfile, os.R_OK):
            raise ValueError(
                # pylint: disable-next=consider-using-f-string
                "SafeProxyTransport:Certificate file %s is not accessible"
                % certfile
            )
        self.trusted_certs.append(certfile)

    def get_connection(self, host):
        if self.verbose:
            print(
                # pylint: disable-next=consider-using-f-string
                "Connecting via https to %s proxy %s, username %s, pass %s"
                % (host, self._proxy, self._proxy_username, self._proxy_password)
            )
        if self.timeout:
            return connections.HTTPSProxyConnection(
                self._proxy,
                host,
                username=self._proxy_username,
                password=self._proxy_password,
                trusted_certs=self.trusted_certs,
                timeout=self.timeout,
            )
        else:
            return connections.HTTPSProxyConnection(
                self._proxy,
                host,
                username=self._proxy_username,
                password=self._proxy_password,
                trusted_certs=self.trusted_certs,
            )


# ============================================================================
# Extended capabilities for transport
#
# We allow for the following possible headers:
#
# Content-Transfer-Encoding:
#       This header tells us how the POST data is encoded in what we read.
#       If it is not set, we assume plain text that can be passed along
#       without any other modification. If set, valid values are:
#       - binary : straight binary data
#       - base64 : will pass through base64 decoder to get the binary data
#
# Content-Encoding:
#       This header tells us what should we do with the binary data obtained
#       after acting on the Content-Transfer-Encoding header. Valid values:
#       - x-gzip : will need to pass through GNU gunzip-like to get plain
#                  text out
#       - x-zlib : this denotes the Python's own zlib bindings which are a
#                  datastream based on gzip, but not quite
#       - x-gpg : will need to pass through GPG to get out the text we want


# ============================================================================
# Input class to automate reading the posting from the network
# Having to work with environment variables blows, though
# pylint: disable-next=missing-class-docstring
class Input:
    def __init__(
        self,
        headers=None,
        # pylint: disable-next=invalid-name
        progressCallback=None,
        # pylint: disable-next=invalid-name
        bufferSize=1024,
        max_mem_size=16384,
    ):
        self.transfer = None
        self.encoding = None
        self.type = None
        self.length = 0
        self.lang = "C"
        self.name = ""
        # pylint: disable-next=invalid-name
        self.progressCallback = progressCallback
        # pylint: disable-next=invalid-name
        self.bufferSize = bufferSize
        self.max_mem_size = max_mem_size

        if not headers:
            # we need to get them from environment
            if "HTTP_CONTENT_TRANSFER_ENCODING" in os.environ:
                self.transfer = os.environ["HTTP_CONTENT_TRANSFER_ENCODING"].lower()
            if "HTTP_CONTENT_ENCODING" in os.environ:
                self.encoding = os.environ["HTTP_CONTENT_ENCODING"].lower()
            if "CONTENT-TYPE" in os.environ:
                self.type = os.environ["CONTENT-TYPE"].lower()
            if "CONTENT_LENGTH" in os.environ:
                self.length = int(os.environ["CONTENT_LENGTH"])
            if "HTTP_ACCEPT_LANGUAGE" in os.environ:
                self.lang = os.environ["HTTP_ACCEPT_LANGUAGE"]
            if "HTTP_X_PACKAGE_FILENAME" in os.environ:
                self.name = os.environ["HTTP_X_PACKAGE_FILENAME"]
        else:
            # The stupid httplib screws up the headers from the HTTP repsonse
            # and converts them to lowercase. This means that we have to
            # convert to lowercase all the dictionary keys in case somebody calls
            # us with sane values --gaftonc (actually mimetools is the culprit)
            for header in headers.keys():
                value = headers[header]
                h = header.lower()
                if h == "content-length":
                    try:
                        self.length = int(value)
                    except ValueError:
                        self.length = 0
                elif h == "content-transfer-encoding":
                    # RFC 2045 #6.1: case insensitive
                    self.transfer = value.lower()
                elif h == "content-encoding":
                    # RFC 2616 #3.5: case insensitive
                    self.encoding = value.lower()
                elif h == "content-type":
                    # RFC 2616 #3.7: case insensitive
                    self.type = value.lower()
                elif h == "accept-language":
                    # RFC 2616 #3.10: case insensitive
                    self.lang = value.lower()
                elif h == "x-package-filename":
                    self.name = value

        self.io = None

    def read(self, fd=sys.stdin):
        # The octet-streams are passed right back
        if self.type == "application/octet-stream":
            return

        if self.length:
            # Read exactly the amount of data we were told
            self.io = _smart_read(
                fd,
                self.length,
                bufferSize=self.bufferSize,
                progressCallback=self.progressCallback,
                max_mem_size=self.max_mem_size,
            )
        else:
            # Oh well, no clue; read until EOF (hopefully)
            self.io = _smart_total_read(fd)

        if not self.transfer or self.transfer == "binary":
            return
        elif self.transfer == "base64":
            # pylint: disable-next=import-outside-toplevel
            import base64

            old_io = self.io
            old_io.seek(0, 0)
            self.io = SmartIO(max_mem_size=self.max_mem_size)
            base64.decode(old_io, self.io)
        else:
            raise NotImplementedError(self.transfer)

    def decode(self, fd=sys.stdin):
        # The octet-stream data are passed right back
        if self.type == "application/octet-stream":
            return InputStream(fd, self.length, self.name, close=fd.close)

        if not self.io:
            self.read(fd)

        # At this point self.io exists (the only case when self.read() does
        # not initialize self.io is when content-type is
        # "application/octet-stream" - and we already dealt with that case

        # We can now close the file descriptor
        if hasattr(fd, "close"):
            fd.close()

        # Now we have the binary goo
        if not self.encoding or self.encoding == "__plain":
            # all is fine.
            pass
        elif self.encoding in ("x-zlib", "deflate"):
            # pylint: disable-next=import-outside-toplevel
            import zlib

            obj = zlib.decompressobj()
            self.io.seek(0, 0)
            data = obj.decompress(self.io.read()) + obj.flush()
            del obj
            self.length = len(data)
            self.io = SmartIO(max_mem_size=self.max_mem_size)
            self.io.write(data)
        elif self.encoding in ("x-gzip", "gzip"):
            # pylint: disable-next=import-outside-toplevel
            import gzip

            self.io.seek(0, 0)
            gz = gzip.GzipFile(mode="rb", compresslevel=COMPRESS_LEVEL, fileobj=self.io)
            data = gz.read()
            self.length = len(data)
            self.io = SmartIO(max_mem_size=self.max_mem_size)
            self.io.write(data)
        elif self.encoding == "x-gpg":
            # XXX: should be written
            raise NotImplementedError(self.transfer, self.encoding)
        else:
            raise NotImplementedError(self.transfer, self.encoding)

        # Play nicely and rewind the file descriptor
        self.io.seek(0, 0)
        return self.io

    def getlang(self):
        return self.lang


# Utility functions


# pylint: disable-next=invalid-name
def _smart_total_read(fd, bufferSize=1024, max_mem_size=16384):
    """
    Tries to read data from the supplied stream, and puts the results into a
    StmartIO object. The data will be in memory or in a temporary file,
    depending on how much it's been read
    Returns a SmartIO object
    """
    io = SmartIO(max_mem_size=max_mem_size)
    while 1:
        chunk = fd.read(bufferSize)
        if not chunk:
            # EOF reached
            break
        io.write(chunk)

    return io


# pylint: disable-next=invalid-name
def _smart_read(fd, amt, bufferSize=1024, progressCallback=None, max_mem_size=16384):
    # Reads amt bytes from fd, or until the end of file, whichever
    # occurs first
    # The function will read in memory if the amout to be read is smaller than
    # max_mem_size, or to a temporary file otherwise
    #
    # Unlike read(), _smart_read tries to return exactly the requested amount
    # (whereas read will return _up_to_ that amount). Reads from sockets will
    # usually reaturn less data, or the read can be interrupted
    #
    # Inspired by Greg Stein's httplib.py (the standard in python 2.x)
    #
    # support for progress callbacks added
    # pylint: disable-next=invalid-name
    startTime = time.time()
    # pylint: disable-next=invalid-name
    lastTime = startTime
    buf = SmartIO(max_mem_size=max_mem_size)

    origsize = amt
    while amt > 0:
        # pylint: disable-next=invalid-name
        curTime = time.time()
        l = min(bufferSize, amt)
        chunk = fd.read(l)
        # read guarantees that len(chunk) <= l
        l = len(chunk)
        if not l:
            # Oops. Most likely EOF
            break

        # And since the original l was smaller than amt, we know amt >= 0
        amt = amt - l
        buf.write(chunk)
        if progressCallback is None:
            # No progress callback, so don't do fancy computations
            continue
        # We update the progress callback if:
        #  we haven't updated it for more than a secord, or
        #  it's the last read (amt == 0)
        if curTime - lastTime >= 1 or amt == 0:
            # pylint: disable-next=invalid-name
            lastTime = curTime
            # use float() so that we force float division in the next step
            # pylint: disable-next=invalid-name
            bytesRead = float(origsize - amt)
            # if amt == 0, on a fast machine it is possible to have
            # curTime - lastTime == 0, so add an epsilon to prevent a division
            # by zero
            speed = bytesRead / ((curTime - startTime) + 0.000001)
            if origsize == 0:
                secs = 0
            else:
                # speed != 0 because bytesRead > 0
                # (if bytesRead == 0 then origsize == amt, which means a read
                # of 0 length; but that's impossible since we already checked
                # that l is non-null
                secs = amt / speed
            progressCallback(bytesRead, origsize, speed, secs)

    # Now rewind the SmartIO
    buf.seek(0, 0)
    return buf


class InputStream:
    def __init__(self, fd, length, name="<unknown>", close=None):
        self.fd = fd
        self.length = int(length)
        self.name = name
        # Close function
        self.close = close

    def __repr__(self):
        # pylint: disable-next=consider-using-f-string
        return "Input data is a stream of %d bytes for file %s.\n" % (
            self.length,
            self.name,
        )


# ============================================================================
# Output class that will be used to build the temporary output string
# pylint: disable-next=missing-class-docstring
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):
                # pylint: disable-next=broad-exception-raised
                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 name in self.headers:
            del self.headers[name]

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

        # Content-Encoding header
        if self.encoding == self.ENCODE_GZIP:
            # pylint: disable-next=import-outside-toplevel
            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)
            if sys.version_info[0] == 3:
                gz.write(bstr(data))
            else:
                gz.write(sstr(data))
            gz.close()
            self.data = f.getvalue()
            f.close()
        elif self.encoding == self.ENCODE_ZLIB:
            # pylint: disable-next=import-outside-toplevel
            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.encode()) + obj.flush()
        elif self.encoding == self.ENCODE_GPG:
            # XXX: fix me.
            raise NotImplementedError(self.transfer, self.encoding)

        # 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:
            # pylint: disable-next=import-outside-toplevel
            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).decode()

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

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

        # other headers
        self.set_header(
            "X-Transport-Info",
            # pylint: disable-next=consider-using-f-string
            "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:
            # pylint: disable-next=broad-exception-raised
            raise Exception("No connection object found")
        self._connection.connect()
        # wrap self data into binary object, otherwise HTTPConnection.request
        # will encode it as ISO-8859-1 https://docs.python.org/3/library/http.client.html#httpconnection-objects
        self._connection.request(
            self.method, handler, body=bstr(self.data), headers=self.headers
        )

        response = self._connection.getresponse()

        if not self.response_acceptable(response):
            raise xmlrpclib.ProtocolError(
                # pylint: disable-next=consider-using-f-string
                "%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


# pylint: disable-next=invalid-name
def lookupTransfer(transfer, strict=0):
    """Given a string or numeric representation of a transfer, return the
    transfer code"""
    if transfer is None:
        # Plain
        return 0
    if isinstance(transfer, IntType) and 0 <= transfer < len(Output.transfers):
        return transfer
    if isinstance(transfer, StringType):
        for i in range(len(Output.transfers)):
            if Output.transfers[i] == transfer.lower():
                return i
    if strict:
        # pylint: disable-next=consider-using-f-string
        raise ValueError("Unsupported transfer %s" % transfer)
    # Return default
    return 0


# pylint: disable-next=invalid-name
def lookupEncoding(encoding, strict=0):
    """Given a string or numeric representation of an encoding, return the
    encoding code"""
    if encoding is None:
        # Plain
        return 0
    if isinstance(encoding, IntType) and 0 <= encoding < len(Output.encodings):
        return encoding
    if isinstance(encoding, StringType):
        for i in range(len(Output.encodings)):
            if encoding.lower() in Output.encodings[i]:
                return i
    if strict:
        # pylint: disable-next=consider-using-f-string
        raise ValueError("Unsupported encoding %s" % encoding)
    # Return default
    return 0


Output = BaseOutput


# File object
# pylint: disable-next=missing-class-docstring
class File:
    def __init__(
        self,
        file_obj,
        length=0,
        name=None,
        # pylint: disable-next=invalid-name
        progressCallback=None,
        # pylint: disable-next=invalid-name
        bufferSize=16384,
    ):
        self.length = length
        self.file_obj = file_obj
        self.close = file_obj.close
        # pylint: disable-next=invalid-name
        self.bufferSize = bufferSize
        self.name = ""
        if name:
            self.name = name[name.rfind("/") + 1 :]
        # pylint: disable-next=invalid-name
        self.progressCallback = progressCallback

    def __len__(self):
        return self.length

    def read(self, amt=None):
        # If they want to read everything, use _smart_read
        if amt is None:
            fd = self._get_file()
            return fd.read()

        return self.file_obj.read(amt)

    def read_to_file(self, file):
        """Copies the contents of this File object into another file
        object"""
        fd = self._get_file()
        while 1:
            buf = fd.read(self.bufferSize)
            if not buf:
                break
            if sys.version_info[0] == 3:
                file.write(bstr(buf))
            else:
                file.write(sstr(buf))
        return file

    def _get_file(self):
        """Read everything into a temporary file and call the progress
        callbacks if the file length is defined, or just reads till EOF"""
        if self.length:
            io = _smart_read(
                self.file_obj,
                self.length,
                bufferSize=self.bufferSize,
                progressCallback=self.progressCallback,
            )
            io.seek(0, 0)
        else:
            # Read everuthing - no callbacks involved
            io = _smart_total_read(self.file_obj, bufferSize=self.bufferSize)
        io.seek(0, 0)
        return io

    def __del__(self):
        if self.close:
            self.close()
            self.close = None
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!
openSUSE Build Service is sponsored by