File uyuni-common-libs-git-0.916ff00.obscpio of Package uyuni-common-libs.24170

07070100000000000041FD000000000000000000000001625E8C0600000000000000000000000000000000000000000000001200000000uyuni-common-libs07070100000001000081B4000000000000000000000001625E8C06000046AC000000000000000000000000000000000000001A00000000uyuni-common-libs/LICENSE                    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.
07070100000002000081B4000000000000000000000001625E8C0600000280000000000000000000000000000000000000002700000000uyuni-common-libs/Makefile.common-libs# Makefile for the common libs directory
#
SPACEWALK_FILES = __init__
CODE_DIRS = common 
PYLINT_DIRS = common
CONF_DIRS =

# We look for config files in "well known" locations (rhn-conf,
# httpd-conf, logrotate)
EXTRA_DIRS =


all :: all-code all-conf

%-code : Makefile.common-libs
	@$(foreach d,$(CODE_DIRS), $(MAKE) -C $(d) $* || exit 1; )

%-conf : Makefile.common-libs
	@$(foreach d,$(CONF_DIRS), $(MAKE) -C $(d) $* || exit 1; )

# now include some Macros
include Makefile.defs

install :: install-code install-conf

clean :: clean-code clean-conf

test ::
	mkdir -p $(CURDIR)/reports
	cd ..; $(PYTHON_BIN) -m pytest test/unit/uyuni/
07070100000003000081B4000000000000000000000001625E8C06000006F6000000000000000000000000000000000000002000000000uyuni-common-libs/Makefile.defs# Common pathnames and programs for the Spacewalk project
#

# if not defined, definit as a noop
TOP		?= .

# global defines which control this build and where we deploy files
ROOT		?= /usr/share/rhn
export ROOT

ifeq ("$(PYTHON_BIN)", "")
    PYTHON_BIN = "python"
endif

SPACEWALK_ROOT	?= $(shell $(PYTHON_BIN) -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")/uyuni
export SPACEWALK_ROOT

PREFIX		?=
export PREFIX

# Compilation stuff
CC		= gcc
PYTHON_INCLUDE	= -I/usr/include/python$(PythonVersion)
CFLAGS		= -Wall -O2 -fomit-frame-pointer $(PYTHON_INCLUDE) -fPIC
SOFLAGS		= -shared -fPIC

# Installation stuff
INSTALL		= /usr/bin/install -c --verbose
INSTALL_BIN	= $(INSTALL) -m 755
INSTALL_DATA	= $(INSTALL) -m 644
INSTALL_DIR	= $(INSTALL) -m 755 -d

# This is for the subdir part
PYFILES		= $(addsuffix .py,$(FILES))

SPACEWALK_PYFILES		= $(addsuffix .py,$(SPACEWALK_FILES))
SPACEWALK_DEST	?= $(SPACEWALK_ROOT)/$(SUBDIR)

# what do we need to install and where
INSTALL_FILES	+= $(PYFILES)
INSTALL_DEST	?= $(ROOT)/$(SUBDIR)

SPACEWALK_INSTALL_FILES	+= $(SPACEWALK_PYFILES)

DIRS		+= $(addprefix $(PREFIX), \
			$(sort $(EXTRA_DIRS)) $(INSTALL_DEST) $(SPACEWALK_ROOT) $(SPACEWALK_DEST))

all :: $(INSTALL_FILES)

install :: all $(DIRS) $(INSTALL_FILES) $(SPACEWALK_INSTALL_FILES)
	@$(foreach f,$(INSTALL_FILES), \
		$(INSTALL_DATA) $(f) $(PREFIX)$(INSTALL_DEST)/$(f) ; )
	@$(foreach f,$(SPACEWALK_INSTALL_FILES), \
		$(INSTALL_DATA) $(f) $(PREFIX)$(SPACEWALK_DEST)/$(f) ; )

$(DIRS):
	$(INSTALL_DIR) $@

clean ::
	@rm -fv *~ *.pyc *.pyo .??*~
	@rm -fv .\#*
	@rm -fv core

# useful macro
descend-subdirs = @$(foreach d,$(SUBDIRS), $(MAKE) -C $(d) $@ || exit 1; )

# subdirs are treated at the end
all install clean:: $(SUBDIRS)
	$(descend-subdirs)


07070100000004000081B4000000000000000000000001625E8C06000001A4000000000000000000000000000000000000001E00000000uyuni-common-libs/__init__.py#
# Copyright (c) 2019 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.
#
07070100000005000041FD000000000000000000000001625E8C0600000000000000000000000000000000000000000000001900000000uyuni-common-libs/common07070100000006000081B4000000000000000000000001625E8C0600000308000000000000000000000000000000000000002200000000uyuni-common-libs/common/Makefile# Makefile for spacewalk backend
#

# what is the backend top dir
TOP     = ..

# Specific stuff
SUBDIR  = common
SPACEWALK_FILES	=   __init__ \
		checksum \
		cli \
		context_managers \
		fileutils \
		notificationUtils \
		rhnLib \
		rhn_deb \
		rhn_mpm \
		rhn_pkg \
		rhn_rpm \
		timezone_utils \
		usix

SCRIPTS =

# check if we can build man pages
DOCBOOK = $(wildcard /usr/bin/docbook2man)

SGMLS   = $(wildcard *.sgml)
MANS    = $(patsubst %.sgml,%.8,$(SGMLS))

MANDIR  ?= /usr/man

EXTRA_DIRS =

include $(TOP)/Makefile.defs

# install scripts

ifneq ($(DOCBOOK),)
# install man pages
all     :: $(MANS)
install :: $(MANS) $(PREFIX)/$(MANDIR)
	$(INSTALL_DATA) $(MANS) $(PREFIX)/$(MANDIR)/man8
endif

%.8 : %.sgml
	$(DOCBOOK) $<

clean :: 
	@rm -fv $(MANS) manpage.*

07070100000007000081B4000000000000000000000001625E8C06000001D3000000000000000000000000000000000000002500000000uyuni-common-libs/common/__init__.py#
# Copyright (c) 2019 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.
#
# Initialization file for the common module
#

07070100000008000081B4000000000000000000000001625E8C0600000BA6000000000000000000000000000000000000002500000000uyuni-common-libs/common/checksum.py#
# Copyright (c) 2009--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

try:
    import hashlib
    import inspect

    hashlib_has_usedforsecurity = 'usedforsecurity' in inspect.getargspec(hashlib.new)[0]
except ImportError:
    import md5
    import sha
    # pylint: disable=F0401
    # pylint can't find Crypto.Hash here, but it is present on older systems.
    from Crypto.Hash import SHA256 as sha256

    hashlib_has_usedforsecurity = False

    class hashlib(object):

        @staticmethod
        def new(checksum):
            if checksum == 'md5':
                return md5.new()
            elif checksum == 'sha1':
                return sha.new()
            elif checksum == 'sha256':
                return sha256.new()
            else:
                raise ValueError("Incompatible checksum type")

def getHashlibInstance(hash_type, used_for_security):
    """Get an instance of a hashlib object.
    """
    if hashlib_has_usedforsecurity:
        return hashlib.new(hash_type, usedforsecurity=used_for_security)
    else:
        return hashlib.new(hash_type)


def getFileChecksum(hashtype, filename=None, fd=None, file_obj=None, buffer_size=None, used_for_security=False):
    """ Compute a file's checksum
        Used by rotateFile()
    """

    # python's md5 lib sucks
    # there's no way to directly import a file.
    if buffer_size is None:
        buffer_size = 65536

    if hashtype == 'sha':
        hashtype = 'sha1'

    if filename is None and fd is None and file_obj is None:
        raise ValueError("no file specified")
    if file_obj:
        f = file_obj
    elif fd is not None:
        f = os.fdopen(os.dup(fd), "rb")
    else:
        f = open(filename, "rb")
    # Rewind it
    f.seek(0, 0)
    m = getHashlibInstance(hashtype, used_for_security)
    while 1:
        try:
            buf = f.read(buffer_size)
        except:  # pylint: disable=W0702,W0703
            # No need to know exact root cause of the exception.
            # Will produce checksum other than expected for such case.
            break
        if not buf:
            break
        m.update(buf)

    # cleanup time
    if file_obj is not None:
        file_obj.seek(0, 0)
    else:
        f.close()
    return m.hexdigest()


def getStringChecksum(hashtype, s):
    """ compute checksum of an arbitrary string """
    h = getHashlibInstance(hashtype, False)
    h.update(s)
    return h.hexdigest()
07070100000009000081B4000000000000000000000001625E8C06000009B3000000000000000000000000000000000000002000000000uyuni-common-libs/common/cli.py#
# Copyright (c) 2012--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
import getpass
import io
try:
    #  python 2
    import xmlrpclib
except ImportError:
    #  python3
    import xmlrpc.client as xmlrpclib  # pylint: disable=F0401


def getUsernamePassword(cmdlineUsername, cmdlinePassword):
    """
     Returns a username and password (either by returning the ones passed as
     args, or the user's input
    """
    if cmdlineUsername and cmdlinePassword:
        return cmdlineUsername, cmdlinePassword

    username = cmdlineUsername
    password = cmdlinePassword

    # Read the username, if not already specified
    tty = io.TextIOWrapper(open("/dev/tty", "r+b", buffering=0))
    while not username:
        tty.write("SUSE Manager username: ")
        try:
            username = tty.readline()
        except KeyboardInterrupt:
            tty.write("\n")
            sys.exit(0)
        if username is None:
            # EOF
            tty.write("\n")
            sys.exit(0)
        username = username.strip()
        if username:
            break

    # Now read the password
    while not password:
        try:
            password = getpass.getpass("SUSE Manager password: ")
        except KeyboardInterrupt:
            tty.write("\n")
            sys.exit(0)
        tty.close()
    return username, password


def xmlrpc_login(client, username, password, verbose=0):
    """
     Authenticate Session call
    """
    if verbose:
        print("...logging in to server...")

    try:
        sessionkey = client.auth.login(username, password)
    except xmlrpclib.Fault:
        e = sys.exc_info()[1]
        sys.stderr.write("Error: %s\n" % e.faultString)
        sys.exit(-1)

    return sessionkey


def xmlrpc_logout(client, session_key, verbose=0):
    """
     End Authentication call
    """
    if verbose:
        print("...logging out of server...")

    client.auth.logout(session_key)
0707010000000A000081B4000000000000000000000001625E8C060000041A000000000000000000000000000000000000002D00000000uyuni-common-libs/common/context_managers.py"""Collection of context managers for Uyuni."""
from contextlib import contextmanager
from spacewalk.common.rhnConfig import CFG, initCFG

@contextmanager
def cfg_component(component, root=None, filename=None):
    """Context manager for rhnConfig.

    :param comp: The configuration component to use in this context
    :param root: Root directory location of configuration files, optional
    :param filename: Configuration file, optional

    There is a common pattern when using rhnConfig that consists of the following steps:
    1. save current component: old = CFG.getComponent()
    2. set CFG to another component: initCFG('my_component')
    3. Read / Set configuration values
    4. set CFG back to the previous component

    This pattern can now be expressed using the ``with`` statement:

    with cfg_component('my_component') as CFG:
        print(CFG.my_value)
    """
    previous = CFG.getComponent()
    initCFG(component=component, root=root, filename=filename)
    try:
        yield CFG
    finally:
        initCFG(previous)
0707010000000B000081B4000000000000000000000001625E8C06000042D9000000000000000000000000000000000000002600000000uyuni-common-libs/common/fileutils.py#
# 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 codecs
import os
import sys
import bz2
import gzip
import pwd
import grp
import shutil
import subprocess
import select
import stat
import tempfile
import io
from uyuni.common.checksum import getFileChecksum
from uyuni.common.rhnLib import isSUSE
from uyuni.common.usix import ListType, TupleType, MaxInt

try:
    import lzma
    HAS_LZMA = True
except ImportError:
    HAS_LZMA = False

def cleanupAbsPath(path):
    """ take ~taw/../some/path/$MOUNT_POINT/blah and make it sensible.

        Path returned is absolute.
        NOTE: python 2.2 fixes a number of bugs with this and eliminates
              the need for os.path.expanduser
    """

    if path is None:
        return None
    return os.path.abspath(
        os.path.expanduser(
            os.path.expandvars(path)))


def cleanupNormPath(path, dotYN=0):
    """ take ~taw/../some/path/$MOUNT_POINT/blah and make it sensible.

        Returned path may be relative.
        NOTE: python 2.2 fixes a number of bugs with this and eliminates
              the need for os.path.expanduser
    """
    if path is None:
        return None
    path = os.path.normpath(
        os.path.expanduser(
            os.path.expandvars(path)))
    if dotYN and not (path and path[0] == '/'):
        dirs = path.split('/')
        if dirs[:1] not in (['.'], ['..']):
            dirs = ['.'] + dirs
        path = '/'.join(dirs)
    return path


def rotateFile(filepath, depth=5, suffix='.', verbosity=0):
    """ backup/rotate a file
        depth (-1==no limit) refers to num. of backups (rotations) to keep.

        Behavior:
          (1)
            x.txt (current)
            x.txt.1 (old)
            x.txt.2 (older)
            x.txt.3 (oldest)
          (2)
            all file stats preserved. Doesn't blow away original file.
          (3)
            if x.txt and x.txt.1 are identical (size or checksum), None is
            returned
    """

    # check argument sanity (should really be down outside of this function)
    if not filepath or not isinstance(filepath, type('')):
        raise ValueError("filepath '%s' is not a valid arguement" % filepath)
    if not isinstance(depth, type(0)) or depth < -1 \
            or depth > MaxInt - 1 or depth == 0:
        raise ValueError("depth must fall within range "
                         "[-1, 1...%s]" % (MaxInt - 1))

    # force verbosity to be a numeric value
    verbosity = verbosity or 0
    if not isinstance(verbosity, type(0)) or verbosity < -1 \
            or verbosity > MaxInt - 1:
        raise ValueError('invalid verbosity value: %s' % (verbosity))

    filepath = cleanupAbsPath(filepath)
    if not os.path.isfile(filepath):
        raise ValueError("filepath '%s' does not lead to a file" % filepath)

    pathNSuffix = filepath + suffix
    pathNSuffix1 = pathNSuffix + '1'

    if verbosity > 1:
        sys.stderr.write("Working dir: %s\n"
                         % os.path.dirname(pathNSuffix))

    # is there anything to do? (existence, then size, then checksum)
    checksum_type = 'sha1'
    if os.path.exists(pathNSuffix1) and os.path.isfile(pathNSuffix1) \
            and os.stat(filepath)[6] == os.stat(pathNSuffix1)[6] \
            and getFileChecksum(checksum_type, filepath) == \
            getFileChecksum(checksum_type, pathNSuffix1):
        # nothing to do
        if verbosity:
            sys.stderr.write("File '%s' is identical to its rotation. "
                             "Nothing to do.\n" % os.path.basename(filepath))
        return None

    # find last in series (of rotations):
    last = 0
    while os.path.exists('%s%d' % (pathNSuffix, last + 1)):
        last = last + 1

    # percolate renames:
    for i in range(last, 0, -1):
        os.rename('%s%d' % (pathNSuffix, i), '%s%d' % (pathNSuffix, i + 1))
        if verbosity > 1:
            filename = os.path.basename(pathNSuffix)
            sys.stderr.write("Moving file: %s%d --> %s%d\n" % (filename, i,
                                                               filename, i + 1))

    # blow away excess rotations:
    if depth != -1:
        last = last + 1
        for i in range(depth + 1, last + 1):
            path = '%s%d' % (pathNSuffix, i)
            os.unlink(path)
            if verbosity:
                sys.stderr.write("Rotated out: '%s'\n" % (
                    os.path.basename(path)))

    # do the actual rotation
    shutil.copy2(filepath, pathNSuffix1)
    if os.path.exists(pathNSuffix1) and verbosity:
        sys.stderr.write("Backup made: '%s' --> '%s'\n"
                         % (os.path.basename(filepath),
                            os.path.basename(pathNSuffix1)))

    # return the full filepath of the backed up file
    return pathNSuffix1


def rhn_popen(cmd, progressCallback=None, bufferSize=16384, outputLog=None):
    """ popen-like function, that accepts execvp-style arguments too (i.e. an
        array of params, thus making shell escaping unnecessary)

        cmd can be either a string (like "ls -l /dev"), or an array of
        arguments ["ls", "-l", "/dev"]

        Returns the command's error code, a stream with stdout's contents
        and a stream with stderr's contents

        progressCallback --> progress bar twiddler
        outputLog --> optional log file file object write method
    """

    cmd_is_list = isinstance(cmd, (ListType, TupleType))
    if cmd_is_list:
        cmd = list(map(str, cmd))
    # pylint: disable=E1101
    c = subprocess.Popen(cmd, bufsize=0, stdin=subprocess.PIPE,
                         stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                         close_fds=True, shell=(not cmd_is_list))

    # We don't write to the child process
    c.stdin.close()

    # Create two temporary streams to hold the info from stdout and stderr
    child_out = tempfile.TemporaryFile(prefix='/tmp/my-popen-', mode='r+b')
    child_err = tempfile.TemporaryFile(prefix='/tmp/my-popen-', mode='r+b')

    # Map the input file descriptor with the temporary (output) one
    fd_mappings = [(c.stdout, child_out), (c.stderr, child_err)]
    exitcode = None
    count = 1

    while 1:
        # Is the child process done?
        status = c.poll()
        if status is not None:
            if status >= 0:
                # Save the exit code, we still have to read from the pipes
                exitcode = status
            else:
                # Some signal sent to this process
                if outputLog is not None:
                    outputLog("rhn_popen: Signal %s received\n" % (-status))
                exitcode = status
                break

        fd_set = [x[0] for x in fd_mappings]
        readfds = select.select(fd_set, [], [])[0]

        for in_fd, out_fd in fd_mappings:
            if in_fd in readfds:
                # There was activity on this file descriptor
                output = os.read(in_fd.fileno(), bufferSize)
                if output:
                    # show progress
                    if progressCallback:
                        count = count + len(output)
                        progressCallback(count)

                    if outputLog is not None:
                        outputLog(output)

                    # write to the output buffer(s)
                    out_fd.write(output)
                    out_fd.flush()

        if exitcode is not None:
            # Child process is done
            break

    for f_in, f_out in fd_mappings:
        f_in.close()
        f_out.seek(0, 0)

    return exitcode, child_out, child_err


def makedirs(path,  mode=int('0755', 8), user=None, group=None):
    "makedirs function that also changes the owners"

    dirs_to_create = []
    dirname = path

    uid, gid = getUidGid(user, group)

    while 1:
        if os.path.isdir(dirname):
            # We're done with this step
            break
        # We have to create this directory
        dirs_to_create.append(dirname)
        dirname, last = os.path.split(dirname)
        if not last:
            # We reached the top directory
            break

    # Now create the directories
    while dirs_to_create:
        dirname = dirs_to_create.pop()
        try:
            os.mkdir(dirname, mode)
        except OSError:
            e = sys.exc_info()[1]
            if e.errno != 17:  # File exists
                raise
            # Ignore the error
        try:
            os.chown(dirname, uid, gid)
        except OSError:
            # Changing permissions failed; ignore the error
            sys.stderr.write("Changing owner for %s failed\n" % dirname)

def createPath(path, user=None, group=None, chmod=int('0755', 8)):
    """advanced makedirs

    Will create the path if necessary.
    Will chmod, and chown that path properly.
    Defaults for user/group to the apache user

    Uses the above makedirs() function.
    """
    if isSUSE():
        if user is None:
            user = 'wwwrun'
        if group is None:
            group = 'www'
    else:
        if user is None:
            user = 'apache'
        if group is None:
            group = 'apache'

    path = cleanupAbsPath(path)
    if not os.path.exists(path):
        makedirs(path, mode=chmod, user=user, group=group)
    elif not os.path.isdir(path):
        raise ValueError("ERROR: createPath('%s'): path doesn't lead to a directory" % str(path))
    else:
        os.chmod(path, chmod)
        uid, gid = getUidGid(user, group)
        try:
            os.chown(path, uid, gid)
        except OSError:
            # Changing permissions failed; ignore the error
            sys.stderr.write("Changing owner for %s failed\n" % path)


def setPermsPath(path, user=None, group='root', chmod=int('0750', 8)):
    """chown user.group and set permissions to chmod"""
    if isSUSE() and user is None:
        user = 'wwwrun'
    elif user is None:
        user = 'apache'

    if not os.path.exists(path):
        raise OSError("*** ERROR: Path doesn't exist (can't set permissions): %s" % path)

    # If non-root, don't bother to change owners
    if os.getuid() != 0:
        return

    gc = GecosCache()
    uid = gc.getuid(user)
    if uid is None:
        raise OSError("*** ERROR: user '%s' doesn't exist. Cannot set permissions properly." % user)

    gid = gc.getgid(group)
    if gid is None:
        raise OSError("*** ERROR: group '%s' doesn't exist. Cannot set permissions properly." % group)

    uid_, gid_ = os.stat(path)[4:6]
    if uid_ != uid or gid_ != gid:
        os.chown(path, uid, gid)
    os.chmod(path, chmod)


class GecosCache:

    "Cache getpwnam() and getgrnam() calls"
    __shared_data = {}

    def __init__(self):
        self.__dict__ = self.__shared_data
        if not list(self.__shared_data.keys()):
            # Not initialized
            self._users = {}
            self._groups = {}

    def getuid(self, name):
        "Return the UID of the user by name"
        if name in self._users:
            return self._users[name]
        try:
            uid = pwd.getpwnam(name)[2]
        except KeyError:
            # XXX misa: gripe? taw: I think we need to do something!
            sys.stderr.write("XXX: User %s does not exist\n" % name)
            return None
        self._users[name] = uid
        return uid

    def getgid(self, name):
        "Return the GID of the group by name"
        if name in self._groups:
            return self._groups[name]
        try:
            gid = grp.getgrnam(name)[2]
        except KeyError:
            # XXX misa: gripe?
            sys.stderr.write("XXX: Group %s does not exist\n" % name)
            return None
        self._groups[name] = gid
        return gid

    def reset(self):
        self.__shared_data.clear()
        self.__init__()


def getUidGid(user=None, group=None):
    "return uid, gid given user and group"

    gc = GecosCache()
    uid = os.getuid()
    if uid != 0:
        # Don't bother to change the owner, it will fail anyway
        # group ownership may work though
        user = None
    else:
        uid = gc.getuid(user)

    if group:
        gid = gc.getgid(group)
    else:
        gid = None

    if gid is None:
        gid = os.getgid()
    return uid, gid

# Duplicated in client/tools/rhncfg/config_common/file_utils.py to remove dependency
# requirement. If making changes make them there too.
FILETYPE2CHAR = {
    'file': '-',
    'directory': 'd',
    'symlink': 'l',
    'chardev': 'c',
    'blockdev': 'b',
}

# Duplicated in client/tools/rhncfg/config_common/file_utils.py to remove dependency
# requirement. If making changes make them there too.


def _ifelse(cond, thenval, elseval):
    if cond:
        return thenval
    else:
        return elseval

# Duplicated in client/tools/rhncfg/config_common/file_utils.py to remove dependency
# requirement. If making changes make them there too.


def ostr_to_sym(octstr, ftype):
    """ Convert filemode in octets (like '644') to string like "ls -l" ("-rwxrw-rw-")
        ftype is one of: file, directory, symlink, chardev, blockdev.
    """
    mode = int(str(octstr), 8)

    symstr = FILETYPE2CHAR.get(ftype, '?')

    symstr += _ifelse(mode & stat.S_IRUSR, 'r', '-')
    symstr += _ifelse(mode & stat.S_IWUSR, 'w', '-')
    symstr += _ifelse(mode & stat.S_IXUSR,
                      _ifelse(mode & stat.S_ISUID, 's', 'x'),
                      _ifelse(mode & stat.S_ISUID, 'S', '-'))
    symstr += _ifelse(mode & stat.S_IRGRP, 'r', '-')
    symstr += _ifelse(mode & stat.S_IWGRP, 'w', '-')
    symstr += _ifelse(mode & stat.S_IXGRP,
                      _ifelse(mode & stat.S_ISGID, 's', 'x'),
                      _ifelse(mode & stat.S_ISGID, 'S', '-'))
    symstr += _ifelse(mode & stat.S_IROTH, 'r', '-')
    symstr += _ifelse(mode & stat.S_IWOTH, 'w', '-')
    symstr += _ifelse(mode & stat.S_IXOTH,
                      _ifelse(mode & stat.S_ISVTX, 't', 'x'),
                      _ifelse(mode & stat.S_ISVTX, 'T', '-'))
    return symstr

# Duplicated in client/tools/rhncfg/config_common/file_utils.py to remove dependency
# requirement. If making changes make them there too.


def f_date(dbiDate):
    return "%04d-%02d-%02d %02d:%02d:%02d" % (dbiDate.year, dbiDate.month,
                                              dbiDate.day, dbiDate.hour, dbiDate.minute, dbiDate.second)


class payload:

    """ this class implements simple file like object usable for reading payload
        from rpm, mpm, etc.
        it skips first 'skip' bytes of header
    """

    def __init__(self, filename, skip=0):
        self.fileobj = open(filename, 'r')
        self.skip = skip
        self.seek(0)

    def seek(self, offset, whence=0):
        if whence == 0:
            offset += self.skip
        return self.fileobj.seek(offset, whence)

    def tell(self):
        return self.fileobj.tell() - self.skip

    @staticmethod
    def truncate(size=-1):
        # pylint: disable=W0613
        raise AttributeError("'Payload' object do not implement this method")

    @staticmethod
    def write(_s):
        raise AttributeError("'Payload' object do not implement this method")

    @staticmethod
    def writelines(_seq):
        raise AttributeError("'Payload' object do not implement this method")

    def __getattr__(self, x):
        return getattr(self.fileobj, x)


def decompress_open(filename):
    file_obj = None
    if filename.endswith('.gz'):
        file_obj = gzip.open(filename, 'rb')
    elif filename.endswith('.bz2'):
        file_obj = bz2.BZ2File(filename, 'rb')
    elif filename.endswith('.xz'):
        if HAS_LZMA:
            file_obj = lzma.LZMAFile(filename, 'rb')
        else:
            file_obj = subprocess.Popen(["xz", "-d", "-k", filename],
                                        stdout=subprocess.PIPE,
                                        stderr=subprocess.DEVNULL).stdout
    elif filename.endswith('.zck'):
        file_obj = subprocess.Popen(["unzck", "-c", filename],
                                    stdout=subprocess.PIPE,
                                    stderr=subprocess.DEVNULL).stdout
    elif filename.endswith('.zst'):
        file_obj = subprocess.Popen(["zstd", "-d", "-c", filename],
                                    stdout=subprocess.PIPE,
                                    stderr=subprocess.DEVNULL).stdout
    else:
        file_obj = codecs.open(filename, 'r', encoding="utf8")
    if filename.endswith(('.gz', '.bz2', '.xz', '.zck')):
        return io.TextIOWrapper(file_obj, encoding="utf8")
    return file_obj
0707010000000C000081B4000000000000000000000001625E8C0600000AA8000000000000000000000000000000000000002E00000000uyuni-common-libs/common/notificationUtils.pyfrom spacewalk.common.rhnConfig import initCFG, CFG
from spacewalk.server import rhnSQL
import json

def getNotificationsTypeDisabled():
    """Return list of types which are disabled"""
    disabledTypes = []
    comp = CFG.getComponent()
    initCFG("java")
    if CFG.notifications_type_disabled:
        disabledTypes = CFG.notifications_type_disabled.split(",")
    initCFG(comp)
    return disabledTypes

class CreateBootstrapRepoFailed:

    def __init__(self, ident, detail=""):
        self.identifier = ident
        self.details = detail
        self.type = "CreateBootstrapRepoFailed"

    def store(self):
        if self.type in getNotificationsTypeDisabled():
            return

        i = rhnSQL.prepare("""
          SELECT users.id
            FROM (
                  SELECT wc.id,
                         (CASE WHEN (SELECT 1
                                       FROM rhnwebcontactchangelog wccl
                                       JOIN (
                                             SELECT web_contact_id, max(id) current_state
                                               FROM rhnwebcontactchangelog uch
                                           GROUP BY web_contact_id
                                            ) X ON X.current_state = wccl.id
                                       JOIN rhnwebcontactchangestate wcsc ON wccl.change_state_id = wcsc.id
                                      WHERE wcsc.label = 'disabled'
                                        AND X.web_contact_id = wc.id) = 1
                               THEN 1
                               ELSE 0
                           END) disabled
                    FROM rhnusergrouptype ugt
                    JOIN rhnusergroup ug ON ug.group_type = ugt.id
                    JOIN rhnusergroupmembers ugm ON ugm.user_group_id = ug.id
                    JOIN web_contact wc ON wc.id = ugm.user_id
                   WHERE ugt.label = 'satellite_admin'
                 ) users
           WHERE users.disabled = 0;
        """)
        i.execute()
        affected_user = i.fetchall_dict() or None
        if not affected_user:
            return

        mid = rhnSQL.Sequence('suse_notif_message_id_seq')()

        h = rhnSQL.prepare("""
          INSERT INTO suseNotificationMessage (id, type, data)
          VALUES (:mid, :type, :data)
        """)
        h.execute(mid=mid, type=self.type, data=json.dumps(self.__dict__))

        j = rhnSQL.prepare("""
          INSERT INTO suseUserNotification (id, user_id, message_id)
          VALUES (sequence_nextval('suse_user_notif_id_seq'), :uid, :mid)
        """)

        for user in affected_user:
            j.execute(uid=user['id'], mid=mid)
        rhnSQL.commit()
0707010000000D000081B4000000000000000000000001625E8C0600001D49000000000000000000000000000000000000002300000000uyuni-common-libs/common/rhnLib.py#
# Copyright (c) 2008--2017 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 re
import time
from datetime import tzinfo, timedelta
try:
    #  python 2
    import urlparse
except ImportError:
    #  python3
    import urllib.parse as urlparse # pylint: disable=F0401,E0611

from uyuni.common import usix


def setHeaderValue(mp_table, name, values):
    """
    Function that correctly sets headers in an Apache-like table
    The values may be a string (which are set as for a dictionary),
    or an array.
    """
    # mp_table is an Apache mp_table (like headers_in or headers_out)
    # Sets the header name to the values
    if isinstance(values, (usix.ListType, usix.TupleType)):
        for v in values:
            mp_table.add(name, str(v))
    else:
        mp_table[name] = str(values)


rfc822_days = ('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun')
rfc822_mons = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
               'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')


def rfc822time(arg):
    """
    Return time as a string formatted such as: 'Wed, 23 Jun 2001 23:08:35 GMT'.
    We must not use locale-specific functions such as strftime here because
    the RFC explicitly requires C-locale only formatting.  To satisfy this
    requirement, we declare our own days and months here and do the formatting
    manually.

    This function accepts a single argument.  If it is a List or Tuple type,
    it is assumed to be of the form struct_time, as specified in the Python
    time module reference.  If the argument is a float, it is expected to be
    the number of seconds from the epoch.

    NOTE:  In all cases, the argument is assumed to be in local time.  It will
           be translated to GMT in the return value.
    """


    if isinstance(arg, (usix.ListType, usix.TupleType)):
        # Convert to float.
        arg = time.mktime(tuple(arg))

    # Now, the arg must be a float.

    (tm_year, tm_mon, tm_mday, tm_hour, tm_min,
     tm_sec, tm_wday, _tm_yday_, _tm_isdst_) = time.gmtime(arg)

    return \
        "%s, %02d %s %04d %02d:%02d:%02d %s" % \
        (rfc822_days[tm_wday], tm_mday, rfc822_mons[tm_mon - 1], tm_year,
         tm_hour, tm_min, tm_sec, "GMT")


def timestamp(s):
    """
    Converts the string in format YYYYMMDDHHMISS to seconds from the epoch
    """
    if isinstance(s, (usix.IntType, usix.FloatType)):
        # Presumably already a timestamp
        return s
    if len(s) == 14:
        format_string = "%Y%m%d%H%M%S"
    elif len(s) == 19:
        format_string = "%Y-%m-%d %H:%M:%S"
    else:
        raise TypeError("String '%s' is not a YYYYMMDDHHMISS" % s)
    # Get the current DST setting
    timeval = list(time.strptime(s, format_string))
    # No daylight information available
    timeval[8] = -1
    return time.mktime(tuple(timeval))


def checkValue(val, *args):
    """ A type/value checker
        Check value against the list of acceptable values / types
    """

    for a in args:
        if isinstance(a, usix.TypeType):
            # Is val of type a?
            if isinstance(val, a):
                return 1
        else:
            # This is an actual value we allow
            if val == a:
                return 1
    return 0


def parseUrl(url):
    """ urlparse is more complicated than what we need.

        We make the assumption that the URL has real URL information.
        NOTE: http/https ONLY for right now.

        The normal behavior of urlparse:
            - if no {http[s],file}:// then the string is considered everything
              that normally follows the URL, e.g. /XMLRPC
            - if {http[s],file}:// exists, anything between that and the next /
              is the URL.

        The behavior of *this* function:
            - if no {http[s],file}:// then the string is simply assumed to be a
              URL without the {http[s],file}:// attached. The parsed info is
              reparsed as one would think it would be:

            - returns: (addressing scheme, network location, path,
                        parameters, query, fragment identifier).

              NOTE: netloc (or network location) can be HOSTNAME:PORT
    """
    schemes = ('http', 'https')
    if url is None:
        return None
    parsed = list(urlparse.urlparse(url))
    if not parsed[0] or parsed[0] not in schemes:
        url = 'https://' + url
        parsed = list(urlparse.urlparse(url))
        parsed[0] = ''
    return tuple(parsed)


def hash_object_id(object_id, factor):
    """Given an object id (assumed to be <label>-<number>), returns the
    last few digits for the number. For instance, (812345, 3) should
    return 345"""
    # Grab the digits after -
    num_id = object_id.split('-')[-1]
    # get last 'factor' numbers
    num_id = num_id[-factor:]
    return num_id.rjust(factor, '0')


# reg exp for splitting rpm package names.
re_rpmName = re.compile("^(.*)-([^-]*)-([^-]*)$")


def parseRPMName(pkgName):
    """ IN:  Package string in, n-n-n-v.v.v-r.r_r, format.
        OUT: Four strings (in a tuple): name, epoch, version, release.
    """
    reg = re_rpmName.match(pkgName)
    if reg is None:
        return None, None, None, None
    n, v, r = reg.group(1, 2, 3)
    e = None
    ind = r.find(':')
    if ind >= 0:  # epoch found
        e = r[ind + 1:]
        r = r[0:ind]
    return str(n), e, str(v), str(r)


def parseDEBName(pkgName):
    """ IN:  Package string in, n-n_v.v-v.v-r.r, format.
        OUT: Four strings (in a tuple): name, epoch, version, release.
    """
    if pkgName.find('_') == -1:
        return None, None, None, None
    e = None
    n, version = pkgName.split('_')
    if version.find(':') != -1:
        e, version = version.split(':')
    version_tmpArr = version.split('-')
    v = '-'.join(version_tmpArr[:-1])
    r = version_tmpArr[-1]
    return str(n), e, str(v), str(r)


def isSUSE():
    """Return true if this is a SUSE system, otherwise false"""

    if not os.path.exists('/etc/os-release'):
        return False

    cpe_name = ''
    try:
        lines = open('/etc/os-release', 'r').readlines()
        for line in lines:
            # Skip empty and comment-only lines
            if re.match(r'[ \t]*(#|$)', line):
                continue

            # now split it into keys and values. We allow for max one
            # split/cut (the first one)
            (key, val) = [c.strip() for c in line.split('=', 1)]
            if key == 'CPE_NAME':
                cpe_name = val
                break
    except (IOError, OSError):
        pass

    if 'cpe:/o:opensuse:' in cpe_name or 'cpe:/o:suse:' in cpe_name:
        return True
    return False


class UTC(tzinfo):
    """Used for creating offset-aware datetime objects in Python 2."""
    # pylint: disable=W0613

    def utcoffset(self, dt):
        return timedelta(0)

    def tzname(self, dt):
        return "UTC"

    def dst(self, dt):
        return timedelta(0)

utc = UTC()
0707010000000E000081B4000000000000000000000001625E8C060000139B000000000000000000000000000000000000002400000000uyuni-common-libs/common/rhn_deb.py#
# Copyright (c) 2010--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.
#
#
# Meta-package manager
#
# Author: Lukas Durfina <lukas.durfina@gmail.com>

import sys
import tempfile

from debian import debfile

from uyuni.common.usix import raise_with_tb
from uyuni.common import checksum
from uyuni.common.rhn_pkg import A_Package, InvalidPackageError

# bare-except and broad-except
# pylint: disable=W0702,W0703

DEB_CHECKSUM_TYPE = 'sha256'       # FIXME: this should be a configuration option


class deb_Header:

    "Wrapper class for an deb header - we need to store a flag is_source"

    def __init__(self, stream):
        self.packaging = 'deb'
        self.signatures = []
        self.is_source = 0
        self.deb = None

        try:
            self.deb = debfile.DebFile(stream.name)
        except Exception:
            e = sys.exc_info()[1]
            raise_with_tb(InvalidPackageError(e), sys.exc_info()[2])

        try:
            # Fill info about package
            debcontrol = self.deb.debcontrol()
            self.hdr = {
                'name': debcontrol.get_as_string('Package'),
                'arch': debcontrol.get_as_string('Architecture') + '-deb',
                'summary': debcontrol.get_as_string('Description').splitlines()[0],
                'epoch':   '',
                'version': 0,
                'release': 0,
                'description': debcontrol.get_as_string('Description'),
            }
            for hdr_k, deb_k in [('requires', 'Depends'),
                                 ('provides', 'Provides'),
                                 ('conflicts', 'Conflicts'),
                                 ('obsoletes', 'Replaces'),
                                 ('recommends', 'Recommends'),
                                 ('suggests', 'Suggests'),
                                 ('breaks', 'Breaks'),
                                 ('predepends', 'Pre-Depends'),
                                 ('payload_size', 'Installed-Size'),
                                 ('maintainer', 'Maintainer')]:
                if deb_k in debcontrol:
                    self.hdr[hdr_k] = debcontrol.get_as_string(deb_k)
            for k in list(debcontrol.keys()):
                if k not in self.hdr:
                    self.hdr[k] = debcontrol.get_as_string(k)

            version = debcontrol.get_as_string('Version')
            if version.find(':') != -1:
                self.hdr['epoch'], version = version.split(':')
                self.hdr['version'] = version
            if version.find('-') != -1:
                version_tmpArr = version.split('-')
                self.hdr['version'] = '-'.join(version_tmpArr[:-1])
                self.hdr['release'] = version_tmpArr[-1]
            else:
                self.hdr['version'] = version
                self.hdr['release'] = 'X'
        except Exception:
            e = sys.exc_info()[1]
            raise_with_tb(InvalidPackageError(e), sys.exc_info()[2])

    @staticmethod
    def checksum_type():
        return DEB_CHECKSUM_TYPE

    @staticmethod
    def is_signed():
        return 0

    def __getitem__(self, name):
        return self.hdr.get(str(name))

    def __setitem__(self, name, item):
        self.hdr[name] = item

    def __delitem__(self, name):
        del self.hdr[name]

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

    def __len__(self):
        return len(self.hdr)


class DEB_Package(A_Package):

    def __init__(self, input_stream=None):
        A_Package.__init__(self, input_stream)
        self.header_data = tempfile.NamedTemporaryFile()
        self.checksum_type = DEB_CHECKSUM_TYPE

    def read_header(self):
        self._stream_copy(self.input_stream, self.header_data)
        self.header_end = self.header_data.tell()
        try:
            self.header_data.seek(0, 0)
            self.header = deb_Header(self.header_data)
        except:
            e = sys.exc_info()[1]
            raise_with_tb(InvalidPackageError(e), sys.exc_info()[2])

    def save_payload(self, output_stream):
        c_hash = checksum.getHashlibInstance(self.checksum_type, False)
        if output_stream:
            output_start = output_stream.tell()
        self._stream_copy(self.header_data, output_stream, c_hash)
        self.checksum = c_hash.hexdigest()
        if output_stream:
            self.payload_stream = output_stream
            self.payload_size = output_stream.tell() - output_start
0707010000000F000081B4000000000000000000000001625E8C060000241F000000000000000000000000000000000000002400000000uyuni-common-libs/common/rhn_mpm.py#
# 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 gzip
try:
    #  python 2
    import cStringIO
except ImportError:
    #  python3
    import io as cStringIO
import tempfile
try:
    #  python 2
    import xmlrpclib
except ImportError:
    #  python3
    import xmlrpc.client as xmlrpclib # pylint: disable=F0401
import struct
import sys
from uyuni.common import fileutils

from uyuni.common.usix import ListType, TupleType

from uyuni.common.usix import raise_with_tb
from uyuni.common import checksum
from uyuni.common.rhn_pkg import A_Package, InvalidPackageError

# bare-except and broad-except
# pylint: disable=W0702,W0703

MPM_CHECKSUM_TYPE = 'md5'       # FIXME: this should be a configuration option


def labelCompare(l1, l2):
    try:
        from uyuni.common import rhn_rpm
    except ImportError:
        # rhn_rpm not avalable; return a dummy comparison function
        return -1
    return rhn_rpm.labelCompare(l1, l2)


def get_package_header(filename=None, file_obj=None, fd=None):
    return load(filename=filename, file_obj=file_obj, fd=fd)[0]


def load(filename=None, file_obj=None, fd=None):
    """ Loads an MPM and returns its header and its payload """
    if (filename is None and file_obj is None and fd is None):
        raise ValueError("No parameters passed")

    if filename is not None:
        f = open(filename)
    elif file_obj is not None:
        f = file_obj
    else:  # fd is not None
        f = os.fdopen(os.dup(fd), "r")

    f.seek(0, 0)

    p = MPM_Package()
    try:
        p.load(f)
    except InvalidPackageError:
        e = sys.exc_info()[1]
        try:
            return load_rpm(f)
        except InvalidPackageError:
            raise_with_tb(e, sys.exc_info()[2])
        except:
            raise_with_tb(e, sys.exc_info()[2])

    return p.header, p.payload_stream


def load_rpm(stream):
    # Hmm, maybe an rpm

    try:
        from uyuni.common import rhn_rpm
    except ImportError:
        raise_with_tb(InvalidPackageError, sys.exc_info()[2])

    # Dup the file descriptor, we don't want it to get closed before we read
    # the payload
    newfd = os.dup(stream.fileno())
    stream = os.fdopen(newfd, "r")

    stream.flush()
    stream.seek(0, 0)

    try:
        header = rhn_rpm.get_package_header(file_obj=stream)
    except InvalidPackageError:
        e = sys.exc_info()[1]
        raise_with_tb(InvalidPackageError(*e.args), sys.exc_info()[2])
    except rhn_rpm.error:
        e = sys.exc_info()[1]
        raise_with_tb(InvalidPackageError(e), sys.exc_info()[2])
    except:
        raise_with_tb(InvalidPackageError, sys.exc_info()[2])
    stream.seek(0, 0)

    return header, stream


class MPM_Header:

    "Wrapper class for an mpm header - we need to store a flag is_source"

    def __init__(self, hdr):
        self.hdr = hdr
        self.is_source = hdr.get('is_source')
        self.packaging = 'mpm'
        self.signatures = []

    def __getitem__(self, name):
        return self.hdr.get(name)

    def __setitem__(self, name, item):
        self.hdr[name] = item

    def __delitem__(self, name):
        del self.hdr[name]

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

    def __len__(self):
        return len(self.hdr)

    @staticmethod
    def is_signed():
        return 0

    @staticmethod
    def checksum_type():
        return MPM_CHECKSUM_TYPE

    @staticmethod
    def unload():
        return None

MPM_HEADER_COMPRESSED_GZIP = 1
MPM_PAYLOAD_COMPRESSED_GZIP = 1


class MPM_Package(A_Package):
    # pylint: disable=R0902
    _lead_format = '!16sB3s4L92s'
    _magic = 'mpmpackage012345'

    def __init__(self, input_stream=None):
        A_Package.__init__(self, input_stream)
        self.header_flags = MPM_HEADER_COMPRESSED_GZIP
        self.header_size = 0
        self.payload_flags = 0
        assert(len(self._magic) == 16)
        self._buffer_size = 16384
        self.file_size = 0

    def read_header(self):
        arr = self._read_lead(self.input_stream)
        magic = arr[0]
        if magic != self._magic:
            raise InvalidPackageError()
        header_len, payload_len = int(arr[5]), int(arr[6])
        self.header_flags, self.payload_flags = arr[3], arr[4]
        self.file_size = 128 + header_len + payload_len
        header_data = self._read_bytes(self.input_stream, header_len)
        self._read_header(header_data, self.header_flags)
        self.checksum_type = self.header.checksum_type()

    def _read_lead(self, stream):
        # Lead has the following format:
        # 16 bytes  magic
        #  1 bytes  version
        #  3 bytes  unused
        #  4 bytes  header flags
        #  4 bytes  payload flags
        #  4 bytes  header length
        #  4 bytes  payload length
        # 92 bytes  padding to 128 bytes
        lead = self._read_bytes(stream, 128)
        if len(lead) != 128:
            raise InvalidPackageError()

        arr = struct.unpack(self._lead_format, lead)
        return arr

    def load(self, input_stream):
        # Clean up
        self.__init__()
        self.input_stream = input_stream
        # Read the header
        self.read_header()

        payload_stream = fileutils.payload(input_stream.name, input_stream.tell())
        input_stream.seek(self.file_size)
        if self.file_size != input_stream.tell():
            raise InvalidPackageError()

        self._read_payload(payload_stream, self.payload_flags)

    def _read_header(self, header_data, header_flags):
        if header_flags & MPM_HEADER_COMPRESSED_GZIP:
            t = cStringIO.StringIO(header_data)
            g = gzip.GzipFile(None, "r", 0, t)
            header_data = g.read()
            g.close()
            t.close()

        try:
            params, _x = xmlrpclib.loads(header_data)
        except:
            # XXX
            raise

        self.header = MPM_Header(params[0])

    def _read_payload(self, payload_stream, payload_flags):
        payload_stream.seek(0, 0)
        if payload_flags & MPM_PAYLOAD_COMPRESSED_GZIP:
            g = gzip.GzipFile(None, "r", 0, payload_stream)
            t = tempfile.TemporaryFile()
            self._stream_copy(g, t)
            g.close()
            payload_stream = t

        self.payload_stream = payload_stream

    def write(self, output_stream):
        if self.header is None:
            raise Exception()

        output_stream.seek(128, 0)
        self._encode_header(output_stream)
        self._encode_payload(output_stream)

        # pylint: disable=E0012,W1401
        # now we know header and payload size so rewind back and write lead
        lead_arr = (self._magic, 1, "\0" * 3, self.header_flags,
                    self.payload_flags, self.header_size, self.payload_size, '\0' * 92)
        # lead
        lead = struct.pack(self._lead_format, *lead_arr)
        output_stream.seek(0, 0)
        output_stream.write(lead)
        output_stream.seek(0, 2)

    def _encode_header(self, stream):
        assert(self.header is not None)
        data = xmlrpclib.dumps((_replace_null(self.header), ))
        start = stream.tell()
        if self.header_flags & MPM_HEADER_COMPRESSED_GZIP:
            f = gzip.GzipFile(None, "wb", 9, stream)
            f.write(data)
            f.close()
        else:
            stream.write(data)
        stream.flush()
        self.header_size = stream.tell() - start

    def _encode_payload(self, stream, c_hash=None):
        assert(self.payload_stream is not None)
        if stream:
            start = stream.tell()
        if stream and self.payload_flags & MPM_PAYLOAD_COMPRESSED_GZIP:
            f = gzip.GzipFile(None, "wb", 9, stream)
            self._stream_copy(self.payload_stream, f, c_hash)
            f.close()
        else:
            self._stream_copy(self.payload_stream, stream, c_hash)
        if stream:
            self.payload_size = stream.tell() - start

    def save_payload(self, output_stream):
        self.payload_stream = self.input_stream
        c_hash = checksum.getHashlibInstance(self.header.checksum_type(), False)
        self._encode_payload(output_stream, c_hash)
        self.checksum = c_hash.hexdigest()
        if output_stream:
            self.payload_stream = output_stream


def _replace_null(obj):
    if obj is None:
        return ''
    if isinstance(obj, ListType):
        return list(map(_replace_null, obj))
    if isinstance(obj, TupleType):
        return tuple(_replace_null(list(obj)))
    if hasattr(obj, 'items'):
        obj_dict = {}
        for k, v in list(obj.items()):
            obj_dict[_replace_null(k)] = _replace_null(v)
        return obj_dict
    return obj
07070100000010000081B4000000000000000000000001625E8C06000011E9000000000000000000000000000000000000002400000000uyuni-common-libs/common/rhn_pkg.py#
# 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
from rhn.stringutils import bstr
from uyuni.common import checksum

def get_package_header(filename=None, file_obj=None, fd=None):
    # pylint: disable=E1103
    if filename is not None:
        stream = open(filename, mode='rb')
        need_close = True
    elif file_obj is not None:
        stream = file_obj
    else:
        stream = os.fdopen(os.dup(fd), "r")
        need_close = True

    if stream.name.endswith('.deb'):
        packaging = 'deb'
    elif stream.name.endswith('.rpm'):
        packaging = 'rpm'
    else:
        packaging = 'mpm'

    a_pkg = package_from_stream(stream, packaging)
    a_pkg.read_header()
    if need_close:
        stream.close()
    return a_pkg.header


def package_from_stream(stream, packaging):
    if packaging == 'deb':
        from uyuni.common import rhn_deb
        a_pkg = rhn_deb.DEB_Package(stream)
    elif packaging == 'rpm':
        from uyuni.common import rhn_rpm
        a_pkg = rhn_rpm.RPM_Package(stream)
    elif packaging == 'mpm':
        from uyuni.common import rhn_mpm
        a_pkg = rhn_mpm.MPM_Package(stream)
    else:
        a_pkg = None
    return a_pkg


def package_from_filename(filename):
    if filename.endswith('.deb'):
        packaging = 'deb'
    elif filename.endswith('.rpm') or filename.endswith('.hdr'):
        packaging = 'rpm'
    else:
        packaging = 'mpm'
    stream = open(filename, mode='rb')
    return package_from_stream(stream, packaging)

BUFFER_SIZE = 8388608
DEFAULT_CHECKSUM_TYPE = 'md5'


class A_Package:

    """virtual class that implements shared methods for RPM/MPM/DEB package object"""
    # pylint: disable=R0902

    def __init__(self, input_stream=None):
        self.header = None
        self.header_start = 0
        self.header_end = 0
        self.input_stream = input_stream
        self.checksum_type = DEFAULT_CHECKSUM_TYPE
        self.checksum = None
        self.payload_stream = None
        self.payload_size = None

    def read_header(self):
        """reads header from self.input_file"""
        pass

    def save_payload(self, output_stream):
        """saves payload to output_stream"""
        c_hash = checksum.getHashlibInstance(self.checksum_type, False)
        if output_stream:
            output_start = output_stream.tell()
        self._stream_copy(self.input_stream, output_stream, c_hash)
        self.checksum = c_hash.hexdigest()
        if output_stream:
            self.payload_stream = output_stream
            self.payload_size = output_stream.tell() - output_start

    def payload_checksum(self):
        # just read and compute checksum
        start = self.input_stream.tell()
        self.save_payload(None)
        self.payload_size = self.input_stream.tell() - start + self.header_end
        self.payload_stream = self.input_stream

    def set_checksum_type(self, ctype=None):
        """set ctype as checksum type. If ctype is None; reset to default"""
        if self.checksum_type == ctype:
            return
        if ctype == None and DEFAULT_CHECKSUM_TYPE != self.checksum_type:
            self.checksum_type = DEFAULT_CHECKSUM_TYPE
            self.payload_checksum()
        elif self.checksum_type != ctype:
            self.checksum_type = ctype
            self.payload_checksum()

    @staticmethod
    def _stream_copy(source, dest, c_hash=None):
        """copies data from the source stream to the destination stream"""
        while True:
            buf = source.read(BUFFER_SIZE)
            if not buf:
                break
            if dest:
                dest.write(buf)
            if c_hash:
                c_hash.update(buf)

    @staticmethod
    def _read_bytes(stream, amt):
        ret = bstr('')
        while amt:
            buf = stream.read(min(amt, BUFFER_SIZE))
            if not buf:
                return ret
            ret = ret + buf
            amt = amt - len(buf)
        return ret


class InvalidPackageError(Exception):
    pass
07070100000011000081B4000000000000000000000001625E8C0600003C25000000000000000000000000000000000000002400000000uyuni-common-libs/common/rhn_rpm.py#
# Copyright (c) 2008--2017 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 struct
import tempfile
import functools

import rpm

from uyuni.common.usix import raise_with_tb
from uyuni.common.usix import next as usix_next
from uyuni.common import checksum
from uyuni.common.rhn_pkg import A_Package, InvalidPackageError
from rhn.stringutils import sstr

# bare-except and broad-except
# pylint: disable=W0702,W0703

if not hasattr(tempfile, 'SpooledTemporaryFile'):
    # RHEL5
    tempfile.SpooledTemporaryFile = tempfile.NamedTemporaryFile

# pylint: disable=E1101
# Expose a bunch of useful constants from rpm
error = rpm.error

sym, val = None, None
for sym, val in list(rpm.__dict__.items()):
    if sym[:3] == 'RPM':
        # A constant, probably - import it into our namespace
        globals()[sym] = val
del sym, val

# need this for rpm-pyhon < 4.6 (e.g. on RHEL5)
rpm.RPMTAG_FILEDIGESTALGO = 5011
rpm.RPMTAG_MODULARITYLABEL = 5096

# these values are taken from /usr/include/rpm/rpmpgp.h
# PGPHASHALGO_MD5             =  1,   /*!< MD5 */
# PGPHASHALGO_SHA1            =  2,   /*!< SHA1 */
# PGPHASHALGO_RIPEMD160       =  3,   /*!< RIPEMD160 */
# PGPHASHALGO_MD2             =  5,   /*!< MD2 */
# PGPHASHALGO_TIGER192        =  6,   /*!< TIGER192 */
# PGPHASHALGO_HAVAL_5_160     =  7,   /*!< HAVAL-5-160 */
# PGPHASHALGO_SHA256          =  8,   /*!< SHA256 */
# PGPHASHALGO_SHA384          =  9,   /*!< SHA384 */
# PGPHASHALGO_SHA512          = 10,   /*!< SHA512 */
PGPHASHALGO = {
    1: 'md5',
    2: 'sha1',
    3: 'ripemd160',
    5: 'md2',
    6: 'tiger192',
    7: 'haval-5-160',
    8: 'sha256',
    9: 'sha384',
    10: 'sha512',
}


class RPM_Header:

    "Wrapper class for an rpm header - we need to store a flag is_source"

    def __init__(self, hdr, is_source=None):
        self.hdr = hdr
        self.is_source = is_source
        self.packaging = 'rpm'
        self.signatures = []
        self._extract_signatures()

    def __getitem__(self, name):
        item = self.hdr[name]
        if isinstance(item, bytes):
            item = sstr(item)
        elif isinstance(item, list):
            item = [sstr(i) if isinstance(i, bytes) else i for i in item]
        return item

    def __contains__(self, name):
        return True if name in self.hdr else False

    def __setitem__(self, name, item):
        self.hdr[name] = item

    def __delitem__(self, name):
        del self.hdr[name]

    def __getattr__(self, name):
        item = getattr(self.hdr, name)
        if isinstance(item, bytes):
            item = sstr(item)
        elif isinstance(item, list):
            item = [sstr(i) if isinstance(i, bytes) else i for i in item]
        return item

    def __len__(self):
        return len(self.hdr)

    def __nonzero__(self):
        return bool(self.hdr)

    __bool__ = __nonzero__

    def modularity_label(self):
        """
        Get modularity label tag.
        Returns string of modularity label or None if tag is not there.

        Fix: Some distributions use the DISTTAG (1155) tag instead of
        MODULARITYLABEL (5096) to label modular packages in the format:
        `module(n:s:v:c:a)`. We need to check this tag as a fallback
        (bsc#1192487).
        """
        mtag = None
        if rpm.RPMTAG_MODULARITYLABEL in self.hdr.keys():
            mtag = self.hdr[rpm.RPMTAG_MODULARITYLABEL]
        elif rpm.RPMTAG_DISTTAG in self.hdr.keys() \
                and self.hdr[rpm.RPMTAG_DISTTAG].startswith(b"module("):
            # Strip away 'module(...)' wrap
            mtag = self.hdr[rpm.RPMTAG_DISTTAG][7:-1]
        return mtag

    def checksum_type(self):
        if self.hdr[rpm.RPMTAG_FILEDIGESTALGO] \
                and self.hdr[rpm.RPMTAG_FILEDIGESTALGO] in PGPHASHALGO:
            checksum_type = PGPHASHALGO[self.hdr[rpm.RPMTAG_FILEDIGESTALGO]]
        else:
            checksum_type = 'md5'
        return checksum_type

    def is_signed(self):
        if hasattr(rpm, "RPMTAG_DSAHEADER"):
            dsaheader = self.hdr["dsaheader"]
        else:
            dsaheader = 0
        if self.hdr["siggpg"] or self.hdr["sigpgp"] or dsaheader:
            return 1
        return 0

    def _extract_signatures(self):
        header_tags = [
            [rpm.RPMTAG_DSAHEADER, "dsa"],
            [rpm.RPMTAG_RSAHEADER, "rsa"],
            [rpm.RPMTAG_SIGGPG, "gpg"],
            [rpm.RPMTAG_SIGPGP, 'pgp'],
        ]
        for ht, sig_type in header_tags:
            ret = self.hdr[ht]
            if not ret:
                continue
            ret_len = len(ret)
            if ret_len < 17:
                continue
            # Get the key id - hopefully we get it right
            elif ret_len <= 65:  # V3 DSA signature
                key_id = ret[9:17]
            elif ret_len <= 72:  # V4 DSA signature
                key_id = ret[18:26]
            elif ret_len <= 280:  # V3 RSA/8 signature
                key_id = ret[10:18]
            elif ret_len <= 287:  # V4 RSA/SHA1 signature
                key_id = ret[19:27]
            elif ret_len <= 536:  # V3 RSA/SHA256 signature
                key_id = ret[10:18]
            else:  # ret_len > 543 # V4 RSA/SHA signature
                key_id = ret[19:27]

            key_id_len = len(key_id)
            fmt = "%dB" % key_id_len
            t = struct.unpack(fmt, key_id)
            fmt = "%02x" * key_id_len
            key_id = fmt % t
            self.signatures.append({
                'signature_type': sig_type,
                'key_id': key_id,
                'signature': ret,
            })


class RPM_Package(A_Package):
    # pylint: disable=R0902

    def __init__(self, input_stream=None):
        A_Package.__init__(self, input_stream)
        self.header = None
        self.header_data = tempfile.SpooledTemporaryFile()
        self.header_start = None
        self.header_end = None
        self.checksum_type = None
        self.checksum = None
        self.payload_stream = None
        self.payload_size = None

    def read_header(self):
        self._get_header_byte_range()
        try:
            self.header = get_package_header(file_obj=self.header_data)
        except InvalidPackageError:
            e = sys.exc_info()[1]
            raise_with_tb(InvalidPackageError(*e.args), sys.exc_info()[2])
        except error:
            e = sys.exc_info()[1]
            raise_with_tb(InvalidPackageError(e), sys.exc_info()[2])
        except:
            raise_with_tb(InvalidPackageError, sys.exc_info()[2])
        self.checksum_type = self.header.checksum_type()

    def _get_header_byte_range(self):
        """
        Return the start and end bytes of the rpm header object.
        Raw header data are then stored in self.header_data.

        For details of the rpm file format, see:
        http://www.rpm.org/max-rpm/s1-rpm-file-format-rpm-file-format.html
        """

        lead_size = 96
        struct_lead_size = 16
        # Move past the rpm lead
        buf = self._read_bytes(self.input_stream, lead_size)
        self.header_data.write(buf)

        buf = self._read_bytes(self.input_stream, struct_lead_size)
        self.header_data.write(buf)

        sig_size = self._get_header_struct_size(buf)

        # Now we can find the start of the actual header.
        self.header_start = lead_size + sig_size

        buf = self._read_bytes(self.input_stream, sig_size - struct_lead_size)
        self.header_data.write(buf)

        buf = self._read_bytes(self.input_stream, struct_lead_size)
        self.header_data.write(buf)

        header_size = self._get_header_struct_size(buf)
        self.header_end = self.header_start + header_size

        buf = self._read_bytes(self.input_stream, header_size - struct_lead_size)
        self.header_data.write(buf)

    @staticmethod
    def _get_header_struct_size(struct_lead):
        """
        Compute the size in bytes of the rpm header struct starting at the current
        position in package_file.
        """
        try:
            # Read the number of index entries
            header_index = struct_lead[8:12]
            (header_index_value, ) = struct.unpack('>I', header_index)

            # Read the the size of the header data store
            header_store = struct_lead[12:16]
            (header_store_value, ) = struct.unpack('>I', header_store)
        except:
            raise InvalidPackageError

        # The total size of the header. Each index entry is 16 bytes long.
        header_size = 8 + 4 + 4 + header_index_value * 16 + header_store_value

        # Headers end on an 8-byte boundary. Round out the extra data.
        round_out = header_size % 8
        if round_out != 0:
            header_size = header_size + (8 - round_out)

        return header_size

    def save_payload(self, output_stream):
        c_hash = checksum.getHashlibInstance(self.checksum_type, False)
        if output_stream:
            output_start = output_stream.tell()
        self.header_data.seek(0, 0)
        self._stream_copy(self.header_data, output_stream, c_hash)
        self._stream_copy(self.input_stream, output_stream, c_hash)
        self.checksum = c_hash.hexdigest()
        self.header_data.close()
        if output_stream:
            self.payload_stream = output_stream
            self.payload_size = output_stream.tell() - output_start


def get_header_byte_range(package_file):
    """
    Return the start and end bytes of the rpm header object.

    For details of the rpm file format, see:
    http://www.rpm.org/max-rpm/s1-rpm-file-format-rpm-file-format.html
    """

    lead_size = 96

    # Move past the rpm lead
    package_file.seek(lead_size)

    sig_size = get_header_struct_size(package_file)

    # Now we can find the start of the actual header.
    header_start = lead_size + sig_size

    package_file.seek(header_start)

    header_size = get_header_struct_size(package_file)

    header_end = header_start + header_size

    return (header_start, header_end)


def get_header_struct_size(package_file):
    """
    Compute the size in bytes of the rpm header struct starting at the current
    position in package_file.
    """
    # Move past the header preamble
    package_file.seek(8, 1)

    try:
        # Read the number of index entries
        header_index = package_file.read(4)
        (header_index_value, ) = struct.unpack('>I', header_index)

        # Read the the size of the header data store
        header_store = package_file.read(4)
        (header_store_value, ) = struct.unpack('>I', header_store)
    except:
        raise InvalidPackageError

    # The total size of the header. Each index entry is 16 bytes long.
    header_size = 8 + 4 + 4 + header_index_value * 16 + header_store_value

    # Headers end on an 8-byte boundary. Round out the extra data.
    round_out = header_size % 8
    if round_out != 0:
        header_size = header_size + (8 - round_out)

    return header_size

SHARED_TS = None


def get_package_header(filename=None, file_obj=None, fd=None):
    """ Loads the package header from a file / stream / file descriptor
        Raises rpm.error if an error is found, or InvalidPacageError if package is
        busted
    """
    global SHARED_TS
    # XXX Deal with exceptions better
    if (filename is None and file_obj is None and fd is None):
        raise ValueError("No parameters passed")

    if filename is not None:
        f = open(filename, 'r')
    elif file_obj is not None:
        f = file_obj
        f.seek(0, 0)
    else:  # fd is not None
        f = None

    if f is None:
        os.lseek(fd, 0, 0)
        file_desc = fd
    else:
        file_desc = f.fileno()

    # don't try to use rpm.readHeaderFromFD() here, it brokes signatures
    # see commit message
    if not SHARED_TS:
        SHARED_TS = rpm.ts()
    SHARED_TS.setVSFlags(-1)

    rpm.addMacro('_dbpath', '/var/cache/rhn/rhnpush-rpmdb')
    try:
        hdr = SHARED_TS.hdrFromFdno(file_desc)
        rpm.delMacro('_dbpath')
    except:
        rpm.delMacro('_dbpath')
        raise

    if hdr is None:
        raise InvalidPackageError
    is_source = hdr[rpm.RPMTAG_SOURCEPACKAGE]

    return RPM_Header(hdr, is_source)


class MatchIterator:

    def __init__(self, tag_name=None, value=None):
        # Query by name, by default
        if not tag_name:
            tag_name = "name"

        # rpm 4.1 or later
        self.ts = rpm.TransactionSet()
        self.ts.setVSFlags(8)

        m_args = (tag_name,)
        if value:
            m_args += (value,)
        # pylint: disable=E1101
        self.mi = self.ts.dbMatch(*m_args)

    def pattern(self, tag_name, mode, pattern):
        self.mi.pattern(tag_name, mode, pattern)

    def next(self):
        try:
            hdr = usix_next(self.mi)
        except StopIteration:
            hdr = None

        if hdr is None:
            return None
        is_source = hdr[rpm.RPMTAG_SOURCEPACKAGE]
        return RPM_Header(hdr, is_source)


def headerLoad(data):
    hdr = rpm.headerLoad(data)
    is_source = hdr[rpm.RPMTAG_SOURCEPACKAGE]
    return RPM_Header(hdr, is_source)


def labelCompare(t1, t2):
    return rpm.labelCompare(t1, t2)


def nvre_compare(t1, t2):
    def build_evr(p):
        evr = [p[3], p[1], p[2]]
        evr = list(map(str, evr))
        if evr[0] == "":
            evr[0] = None
        return evr
    if t1[0] != t2[0]:
        raise ValueError("You should only compare packages with the same name")
    evr1, evr2 = (build_evr(t1), build_evr(t2))
    return rpm.labelCompare(evr1, evr2)


def hdrLabelCompare(hdr1, hdr2):
    """ take two RPMs or headers and compare them for order """

    if hdr1['name'] == hdr2['name']:
        hdr1 = [hdr1['epoch'] or None, hdr1['version'], hdr1['release']]
        hdr2 = [hdr2['epoch'] or None, hdr2['version'], hdr2['release']]
        if hdr1[0]:
            hdr1[0] = str(hdr1[0])
        if hdr2[0]:
            hdr2[0] = str(hdr2[0])
        return rpm.labelCompare(hdr1, hdr2)
    elif hdr1['name'] < hdr2['name']:
        return -1
    return 1


def sortRPMs(rpms):
    """ Sorts a list of RPM files. They *must* exist.  """

    assert isinstance(rpms, type([]))

    # Build a list of (header, rpm)
    helper = [(get_package_header(x), x) for x in rpms]

    # Sort the list using the headers as a comparison
    sort_cmp=lambda x, y: hdrLabelCompare(x[0], y[0])
    try:
        helper.sort(sort_cmp)
    except TypeError:
        helper.sort(key=functools.cmp_to_key(sort_cmp))

    # Extract the rpm names now
    return [x[1] for x in helper]


def getInstalledHeader(rpmName):
    """ quieries the RPM DB for a header matching rpmName. """

    matchiter = MatchIterator("name")
    matchiter.pattern("name", rpm.RPMMIRE_STRCMP, rpmName)
    return matchiter.next()


if __name__ == '__main__':
    pass
07070100000012000081B4000000000000000000000001625E8C06000004C8000000000000000000000000000000000000002B00000000uyuni-common-libs/common/timezone_utils.py"""
Copyright (C) 2017 Oracle and/or its affiliates. All rights reserved.

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, version 2


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.

Utility to get system UTC offset and format as needed by DBs.
"""
import time


def get_utc_offset():
    """Return the UTC offset, allowing for DST."""
    is_dst = time.daylight and time.localtime().tm_isdst > 0
    utc_offset = - time.timezone
    if is_dst:
        utc_offset = - time.altzone
    mins = divmod(utc_offset, 60)[0]
    hours, mins = divmod(mins, 60)
    return '{0:+03d}:{1:02d}'.format(hours, mins)


if __name__ == "__main__":
    print("UTC offset (allowing for DST if in effect): %s" % get_utc_offset())
07070100000013000081B4000000000000000000000001625E8C0600000966000000000000000000000000000000000000002100000000uyuni-common-libs/common/usix.py#
# Copyright (c) 2013--2017 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.
#
"""
Usix is a library to bring compatibility between Python2 and Python3.
"""

# pylint: disable=E1101

import sys
import types

PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] == 3


# Common data types
# Common data types
if PY3:
    BufferType = bytes
    UnicodeType = bytes
    StringType = str
    DictType = dict
    IntType = int
    LongType = int
    ListType = list
    ClassType = type
    FloatType = float
    TupleType = tuple
    TypeType = type
    InstanceType = object
else:
    BufferType = types.BufferType
    UnicodeType = unicode  # pylint: disable=E0602
    StringType = types.StringType
    DictType = types.DictType
    IntType = types.IntType
    LongType = types.LongType
    ListType = types.ListType
    ClassType = types.BufferType
    FloatType = types.FloatType
    TupleType = types.TupleType
    TypeType = types.TypeType
    InstanceType = types.InstanceType

# Common limits

if PY3:
    MaxInt = sys.maxsize
else:
    MaxInt = sys.maxint


# Common methods

# raise exception with traceback
# pylint: disable=W0122
if PY3:
    def raise_with_tb(value, tb=None):
        """
        Re-raise an exception with the traceback.
        """
        try:
            if value.__traceback__ is not tb:
                raise value.with_traceback(tb)
            raise value
        finally:
            value = None
            tb = None
else:
    exec("""
def raise_with_tb(value, tb=None):
    try:
        raise value, None, tb
    finally:
        tb = None
""")


# code from original 'six' module
# added for compatibility with Python 2.4
try:
    advance_iterator = next
except NameError:
    def advance_iterator(it):
        """
        Iterator invocation.
        """
        return it.next()
next = advance_iterator  # pylint: disable=W0622
07070100000014000081B4000000000000000000000001625E8C0600000D38000000000000000000000000000000000000001B00000000uyuni-common-libs/pylintrc# spacewalk pylint configuration

[MASTER]

# Profiled execution.
profile=no

# Pickle collected data for later comparisons.
persistent=no
suggestion-mode=no

[MESSAGES CONTROL]

# No disabled messages in usix
# disable=

[REPORTS]

# Set the output format. Available formats are text, parseable, colorized, msvs
# (visual studio) and html
output-format=parseable

# Include message's id in output
include-ids=yes

# Tells whether to display a full report or only the messages
reports=yes

# Template used to display messages. This is a python new-style format string
# used to format the message information. See doc for all details
msg-template="{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}"

[VARIABLES]

# A regular expression matching names used for dummy variables (i.e. not used).
dummy-variables-rgx=_|dummy


[BASIC]

# Regular expression which should only match correct module names
#module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
module-rgx=([a-zA-Z_][a-zA-Z0-9_]+)$

# Regular expression which should only match correct module level names
const-rgx=(([a-zA-Z_][a-zA-Z0-9_]*)|(__.*__))$

# Regular expression which should only match correct class names
class-rgx=[a-zA-Z_][a-zA-Z0-9_]+$

# Regular expression which should only match correct function names
function-rgx=[a-z_][a-zA-Z0-9_]{,42}$

# Regular expression which should only match correct method names
method-rgx=[a-z_][a-zA-Z0-9_]{,42}$

# Regular expression which should only match correct instance attribute names
attr-rgx=[a-z_][a-zA-Z0-9_]{,30}$

# Regular expression which should only match correct argument names
argument-rgx=[a-z_][a-zA-Z0-9_]{,30}$

# Regular expression which should only match correct variable names
variable-rgx=[a-z_][a-zA-Z0-9_]{,30}$

# Regular expression which should only match correct list comprehension /
# generator expression variable names
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$

# Regular expression which should only match correct class sttribute names
class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,42}|(__.*__))$

# Good variable names which should always be accepted, separated by a comma
good-names=i,j,k,ex,Run,_

# Bad variable names which should always be refused, separated by a comma
bad-names=foo,bar,baz,toto,tutu,tata

# List of builtins function names that should not be used, separated by a comma
bad-functions=apply,input


[DESIGN]

# Maximum number of arguments for function / method
max-args=10

# Maximum number of locals for function / method body
max-locals=20

# Maximum number of return / yield for function / method body
max-returns=6

# Maximum number of branch for function / method body
max-branchs=20

# Maximum number of statements in function / method body
max-statements=50

# Maximum number of parents for a class (see R0901).
max-parents=7

# Maximum number of attributes for a class (see R0902).
max-attributes=7

# Minimum number of public methods for a class (see R0903).
min-public-methods=1

# Maximum number of public methods for a class (see R0904).
max-public-methods=20


[CLASSES]


[FORMAT]

# Maximum number of characters on a single line.
max-line-length=120

# Maximum number of lines in a module
max-module-lines=1000

# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
indent-string='    '


[MISCELLANEOUS]

# List of note tags to take in consideration, separated by a comma.
notes=
07070100000015000081B4000000000000000000000001625E8C0600000C4A000000000000000000000000000000000000002C00000000uyuni-common-libs/uyuni-common-libs.changes-------------------------------------------------------------------
Tue Apr 19 12:14:45 CEST 2022 - jgonzalez@suse.com

- version 4.3.4-1
  * implement more decompression algorithms for reposync (bsc#1196704)

-------------------------------------------------------------------
Fri Mar 11 15:46:23 CET 2022 - jgonzalez@suse.com

- version 4.3.3-1
  * Reorganize python files

-------------------------------------------------------------------
Tue Jan 18 14:09:04 CET 2022 - jgonzalez@suse.com

- version 4.3.2-1
  * Read modularity data from DISTTAG tag as fallback (bsc#1192487)
  * Add decompression of zck files to fileutils
  * require python macros for building

-------------------------------------------------------------------
Mon Aug 09 11:12:24 CEST 2021 - jgonzalez@suse.com

- version 4.3.1-1
- Handle broken RPM packages to prevent exceptions
  causing fails on repository synchronization (bsc#1186650)

-------------------------------------------------------------------
Fri May 28 09:11:32 CEST 2021 - jgonzalez@suse.com

- version 4.2.4-1
- Maintainer field in debian packages are only recommended (bsc#1186508)

-------------------------------------------------------------------
Wed Jan 27 13:17:30 CET 2021 - jgonzalez@suse.com

- version 4.2.3-1
- Section in Debian packages in now treated as optional (bsc#1179555)

-------------------------------------------------------------------
Wed Nov 25 12:37:09 CET 2020 - jgonzalez@suse.com

- version 4.2.2-1
- Cleaning up unused Python 2 build leftovers.
- Disabled debug package build.

-------------------------------------------------------------------
Fri Sep 18 12:32:52 CEST 2020 - jgonzalez@suse.com

- version 4.2.1-1
- Fix issues importing RPM packages with long RPM headers (bsc#1174965)
- Update package version to 4.2.0

-------------------------------------------------------------------
Wed May 20 11:08:34 CEST 2020 - jgonzalez@suse.com

- version 4.1.5-1
- uyuni-common-libs obsoletes python3-spacewalk-usix and
  python3-spacewalk-backend-libs (bsc#1170684)
- reposync speedup fixes

-------------------------------------------------------------------
Mon Apr 13 09:38:18 CEST 2020 - jgonzalez@suse.com

- version 4.1.4-1
- implement notification utility
- Do not break when syncing Oracle 7 yum channel (bsc#1158463)

-------------------------------------------------------------------
Thu Mar 19 12:18:16 CET 2020 - jgonzalez@suse.com

- version 4.1.3-1
- Add MODULARITYLABEL tag support

-------------------------------------------------------------------
Wed Jan 22 12:26:04 CET 2020 - jgonzalez@suse.com

- version 4.1.2-1
- remove conflicts to spacewalk-backend-libs packages

-------------------------------------------------------------------
Wed Nov 27 17:12:01 CET 2019 - jgonzalez@suse.com

- version 4.1.1-1
- Fix building for CentOS6/RHEL6/Expanded Support 6
- add conflicts to spacewalk-backend-libs and spacewalk-usix to
  remove them on update

-------------------------------------------------------------------
Fri Nov  1 11:20:21 UTC 2019 - Michael Calmer <mc@suse.com>

- initial release
- replaces spacewalk-backend-libs and spacewalk-usix
07070100000016000081B4000000000000000000000001625E8C0600000FDE000000000000000000000000000000000000002900000000uyuni-common-libs/uyuni-common-libs.spec#
# spec file for package uyuni-common-libs
#
# Copyright (c) 2021 SUSE LLC
#
# 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/
#


%global debug_package %{nil} 

%if 0%{?fedora} || 0%{?suse_version} >= 1500 || 0%{?rhel} >= 8
%{!?python3_sitelib: %global python3_sitelib %(%{__python3} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")}
%global python3root %{python3_sitelib}/uyuni
%global build_py3 1
%endif

%if ( 0%{?rhel} && 0%{?rhel} < 8 ) || 0%{?suse_version}
%global build_py2   1
%endif

%define pythonX %{?build_py3:python3}%{!?build_py3:python}

%if 0%{?suse_version} >= 1500
%global python_prefix python3
%else
%if  0%{?fedora} >= 28  || 0%{?rhel} >= 8
%global python_prefix python2
%else
%global python_prefix python
%endif
%endif

%{!?python2_sitelib: %global python2_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")}
%global python2root %{python2_sitelib}/uyuni

Name:           uyuni-common-libs
Summary:        Uyuni server and client libs
License:        GPL-2.0-only
Group:          Development/Languages/Python
Version:        4.3.4
Release:        1
URL:            https://github.com/uyuni-project/uyuni
Source0:        %{name}-%{version}.tar.gz
BuildRequires:  fdupes
BuildRoot:      %{_tmppath}/%{name}-%{version}-build

%description
Uyuni server and client libs

%if 0%{?build_py2}
%package -n python2-%{name}
Summary:        Uyuni server and client tools libraries for python2
Group:          Development/Languages/Python
Requires:       python
%if 0%{?suse_version}
BuildRequires:  python-devel
%else
BuildRequires:  python2-devel
%endif
%if 0%{?suse_version} || 0%{?rhel} >= 8
Recommends:     zchunk
Recommends:     zstd
%endif

%description -n python2-%{name}
Python 2 libraries required by both Uyuni server and client tools.
%endif

%if 0%{?build_py3}
%package -n python3-%{name}
Summary:        Uyuni server and client tools libraries for python3
Group:          Development/Languages/Python
BuildRequires:  python3-devel
BuildRequires:  python3-rpm-macros
Conflicts:      %{name} < 1.7.0
%if 0%{?suse_version}
Requires:       python3-base
%else
Requires:       python3-libs
%endif
%if 0%{?suse_version} || 0%{?rhel} >= 8
Recommends:     zchunk
Recommends:     zstd
%endif

Obsoletes:      python3-spacewalk-backend-libs
Obsoletes:      python3-spacewalk-usix

%description -n python3-%{name}
Python 3 libraries required by both Uyuni server and client tools.
%endif

%prep
%setup -q

%build
make -f Makefile.common-libs all PYTHON_BIN=%{pythonX}

%install
make -f Makefile.common-libs install PREFIX=$RPM_BUILD_ROOT \
    MANDIR=%{_mandir} PYTHON_BIN=%{pythonX}

%if 0%{?build_py3}
install -d $RPM_BUILD_ROOT%{python2root}/common
cp $RPM_BUILD_ROOT%{python3root}/__init__.py \
    $RPM_BUILD_ROOT%{python2root}/
cp $RPM_BUILD_ROOT%{python3root}/common/*.py \
    $RPM_BUILD_ROOT%{python2root}/common
%endif

%if 0%{?suse_version}
%if 0%{?build_py2}
%py_compile -O %{buildroot}/%{python2root}
%fdupes %{buildroot}/%{python2root}
%endif
%if 0%{?build_py3}
%py3_compile -O %{buildroot}/%{python3root}
%fdupes %{buildroot}/%{python3root}
%endif
%endif

%if !(0%{?build_py2})
rm -Rf $RPM_BUILD_ROOT%{python2root}
%endif

%if 0%{?build_py2}
%files -n python2-%{name}
%defattr(-,root,root)
%{!?_licensedir:%global license %doc}
%license LICENSE
%{python2root}
%endif

%if 0%{?build_py3}
%files -n python3-%{name}
%defattr(-,root,root)
%license LICENSE
%{python3root}
%endif

%changelog
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!
openSUSE Build Service is sponsored by