File mgr-push-git-0.08c49ac.obscpio of Package mgr-push.15445

07070100000000000041FD0000000000000000000000015DDE99FC00000000000000000000000000000000000000000000000900000000mgr-push07070100000001000081B40000000000000000000000015DDE99FC000046AC000000000000000000000000000000000000001100000000mgr-push/COPYING                    GNU GENERAL PUBLIC LICENSE
                       Version 2, June 1991

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

                            Preamble

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                            NO WARRANTY

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

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

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

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

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

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

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

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

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

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

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

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

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

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

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

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

This General Public License does not permit incorporating your program into
proprietary programs.  If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
07070100000002000081B40000000000000000000000015DDE99FC00000026000000000000000000000000000000000000001200000000mgr-push/Makefileinclude ../../../rel-eng/Makefile.git
07070100000003000081B40000000000000000000000015DDE99FC00000344000000000000000000000000000000000000001900000000mgr-push/Makefile.pythonTHIS_MAKEFILE := $(realpath $(lastword $(MAKEFILE_LIST)))
CURRENT_DIR := $(dir $(THIS_MAKEFILE))
include $(CURRENT_DIR)../../../rel-eng/Makefile.python

# Docker tests variables
DOCKER_CONTAINER_BASE = uyuni-master
DOCKER_REGISTRY       = registry.mgr.suse.de
DOCKER_RUN_EXPORT     = "PYTHONPATH=$PYTHONPATH"
DOCKER_VOLUMES        = -v "$(CURDIR)/../../../:/manager"

__pylint ::
	$(call update_pip_env)
	pylint --rcfile=pylintrc $(shell find -name '*.py') > reports/pylint.log || true

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

docker_shell ::
	docker run -t -i --rm -e $(DOCKER_RUN_EXPORT) $(DOCKER_VOLUMES) $(DOCKER_REGISTRY)/$(DOCKER_CONTAINER_BASE)-pgsql /bin/bash
07070100000004000081B40000000000000000000000015DDE99FC00000508000000000000000000000000000000000000001A00000000mgr-push/Makefile.rhnpush# Makefile for the rhnpush module
#

INSTALL         = install -D -p --verbose
INSTALL_BIN     = $(INSTALL) -m 755
INSTALL_DIR     = $(INSTALL) -m 755 -d
INSTALL_DATA    = $(INSTALL) -m 644

SUBDIR		= rhnpush
FILES		= rhnpush_main rhnpush_v2 uploadLib __init__ connection \
              rpm2mpm utils rhnpush_cache \
			  rhnpush_config rhnpush_confmanager archive

RHN_CONF_DIR    = /etc/sysconfig/rhn
RHNPUSHRC       = rhnpushrc

PYFILES 	:= $(addsuffix .py, $(FILES))
OBJECTS		:= $(PYFILES)

ROOT		?= /usr/share/rhn
PYTHON_VERSION	?= 2

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

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

BINDIR	= /usr/bin
MANDIR	?= /usr/man

EXTRA_DIRS = $(MANDIR)/man8 $(BINDIR) $(RHN_CONF_DIR)

install-conf : $(RHNPUSHRC)
	$(INSTALL_DATA) $(RHNPUSHRC) $(PREFIX)$(RHN_CONF_DIR)/$(RHNPUSHRC)

%.inst: rhnpush.inst

all	: $(MANS) $(OBJECTS)
install : all install-conf
	$(INSTALL_DIR) $(PREFIX)$(MANDIR)/man8
	$(INSTALL_DATA) $(MANS) $(PREFIX)$(MANDIR)/man8

	$(INSTALL_DIR) $(PREFIX)$(ROOT)/rhnpush
	$(INSTALL_DATA) $(PYFILES) $(PREFIX)$(ROOT)/rhnpush

	$(INSTALL_BIN) rhnpush $(PREFIX)$(BINDIR)/rhnpush-$(PYTHON_VERSION)
	@ln -sfv rhnpush $(PREFIX)$(BINDIR)/rpm2mpm

%.8 : %.sgml
	/usr/bin/docbook2man $<
07070100000005000081B40000000000000000000000015DDE99FC00000265000000000000000000000000000000000000001500000000mgr-push/__init__.py#
# Copyright (c) 2008--2015 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.
#
07070100000006000081B40000000000000000000000015DDE99FC000033C4000000000000000000000000000000000000001400000000mgr-push/archive.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.
#

"""Archive Parsing module"""

import os
import subprocess
import shutil
import tempfile
import select
import zipfile
import tarfile
import sys

if not hasattr(zipfile, 'ZIP64_LIMIT'):
    sys.stderr.write("%s requires zipfile with ZIP64 support.\n" % sys.argv[0])
    sys.exit(3)

# exceptions -------------------------------------------------------------


class ArchiveException(Exception):
    pass


class DecompressionError(ArchiveException):
    pass


class UnknownArchiveError(ArchiveException):
    pass


class InvalidArchiveError(ArchiveException):
    pass

# base archive parsing class ---------------------------------------------


class ArchiveParser(object):

    """Explode an zip or (compressed) tar archive and parse files and
    directories contained therein"""

    # constructor --------------------------------------------------------

    def __init__(self, archive, tempdir="/tmp/"):
        """Initialize an archive parser"""
        assert os.path.exists(archive)

        self._archive = archive
        self._archive_dir = None

        # bug 164756: allow optional working directory
        self._parent_dir = tempdir

        # bug: 171086: support for older versions of tempfile (ie python 2.2)
        tempfile.tempdir = tempdir
        self._temp_dir = tempfile.mktemp()
        os.mkdir(self._temp_dir, int('0700', 8))

        self._explode()

    # destructor ---------------------------------------------------------

    def __del__(self):
        """Cleanup temporary files and directories"""

        if hasattr(self, "_temp_dir") and os.path.isdir(self._temp_dir):
            shutil.rmtree(self._temp_dir, ignore_errors=True)

    # methods called by constructor --------------------------------------

    def _get_archive_dir(self):
        """[internal] find the archive's top level directory name"""

        raise NotImplementedError("ArchiveParser: abstract base class method '_get_archive_dir'")

    def _explode_cmd(self):
        """[internal] find the appropriate command to open the archive"""

        raise NotImplementedError("ArchiveParser: abstract base class method '_explode_cmd'")

    def _explode(self):
        """[internal] Explode a archive for neutral parsing"""

        cmd = self._explode_cmd()

        assert self._archive is not None        # assigned in _copy_archive
        assert self._archive_dir is not None    # assigned in _explode_cmd

        if cmd:
            _my_popen(cmd)

            if os.path.isdir(self._archive_dir):
                return

            raise InvalidArchiveError("Archive did not expand to %s" % self._archive_dir)

        raise InvalidArchiveError("Could not find command to open archive: %s" % self._archive)

    # private helper methods ---------------------------------------------

    def _find(self, filename):
        """[internal] Returns the relative path to a file in the archive"""

        file_path = None
        contents = os.listdir(self._archive_dir)

        while contents:
            entry = contents.pop()
            path = os.path.join(self._archive_dir, entry)

            if os.path.isdir(path):
                p_contents = os.listdir(path)
                e_contents = [os.path.join(entry, e) for e in p_contents]
                # this really is something of a hack, the newest contents will
                # 'prepended' to the queue instead of 'appended' changing the
                # search into depth-first when I think breadth-first would be
                # the expected behavior
                # that's what we get for programming in python which doesn't
                # supply a nice way of adding real data-structure support
                # I already tried extending e_contents with contents and then
                # reassigning the contents reference to e_contents, but the
                # damn while loop still had a hold of the original reference
                contents.extend(e_contents)
            else:
                if entry.endswith(filename):
                    file_path = entry
                    break

        else:
            #            if __degug__: sys.stderr.write("[_find] '%s' not found\n" % file)
            pass

        return file_path

    # public api ---------------------------------------------------------

    def list(self, prefix=""):
        """Return a tuple of directories and files in the archive at the given
        directory: prefix"""

        dirname = os.path.join(self._archive_dir, prefix)
        assert os.path.isdir(dirname)

        l = os.listdir(dirname)

        d = []
        f = []

        for i in l:
            if os.path.isdir(os.path.join(dirname, i)):
                d.append(i)
            else:
                f.append(i)

        return (d, f)

    def contains(self, filename):
        """Returns true iff the file is contained in the archive"""
        return self._find(filename) is not None

    def read(self, filename):
        """Returns the contents of the file, or None on error
           First occurence of that file in archive is returned
        """

        f = self._find(filename)
        if f:
            return self.direct_read(f)

        return None

    def direct_read(self, filename):
        """ Returns the contens of the file, file is relative path in archive.
            Top most level (_get_archive_dir) is automaticaly added.
         """
        # pylint: disable=W0703
        f = os.path.join(os.path.abspath(self._archive_dir), filename)
        contents = None

        if os.path.isfile(f) and os.access(f, os.R_OK):
            try:
                fd = open(f)
                contents = fd.read()
                fd.close()
            except Exception:
                contents = None

        return contents

    def zip(self, prefix=""):
        """Create a zip archive of a (sub-)directory of the archive"""

        dirname = os.path.join(self._archive_dir, prefix)
        zip_dir = os.path.basename(dirname)
        parent_dir = os.path.dirname(dirname)

        cwd = os.getcwd()
        os.chdir(parent_dir)

        zip_file = os.path.join(self._parent_dir, "%s.zip" % zip_dir)
        fd = zipfile.ZipFile(zip_file, 'w', zipfile.ZIP_DEFLATED)
        for base, _dirs, files in os.walk(zip_dir):
            fd.write(base)
            for f in files:
                fd.write(os.path.join(base, f))

        os.chdir(cwd)

        if os.path.isfile(zip_file):
            return zip_file

        return None

    def cpio(self, prefix):
        """Create a cpio archive of a (sub-)directory of the archive"""

        cpio_file = os.path.join(self._temp_dir, "%s.pkg" % prefix)

        cmd = "pkgtrans -s %s %s %s" % (self._archive_dir, cpio_file, prefix)
        _my_popen(cmd)

        if os.path.isfile(cpio_file):
            return cpio_file

        return None

# parser for zip archives ------------------------------------------------


class ZipParser(ArchiveParser):

    def __init__(self, archive, tempdir="/tmp/"):
        self.zip_file = zipfile.ZipFile(archive, 'r')
        ArchiveParser.__init__(self, archive, tempdir)

    def _get_archive_dir(self):
        return self.zip_file.namelist()[0]

    def _explode(self):
        """Explode zip archive"""
        self._archive_dir = os.path.join(self._temp_dir,
                                         self._get_archive_dir()).rstrip('/')

        try:
            self.zip_file.extractall(self._temp_dir)
        except Exception:
            e = sys.exc_info()[1]
            raise InvalidArchiveError("Archive did not expand to %s: %s" %
                                      (self._archive_dir, str(e)))
        return

    def _explode_cmd(self):
        pass

# parser for tar archives ------------------------------------------------


class TarParser(ArchiveParser):

    def __init__(self, archive, tempdir="/tmp/"):
        self.tar_file = tarfile.open(archive, 'r')
        ArchiveParser.__init__(self, archive, tempdir)

    def _get_archive_dir(self):
        return self.tar_file.getnames()[0]

    def _explode(self):
        """Explode tar archive"""
        self._archive_dir = os.path.join(self._temp_dir, self._get_archive_dir())

        try:
            self.tar_file.extractall(path=self._temp_dir)
        except Exception:
            e = sys.exc_info()[1]
            raise InvalidArchiveError("Archive did not expand to %s: %s" %
                                      (self._archive_dir, str(e)))
        return

    def _explode_cmd(self):
        pass

# parser for cpio archives -----------------------------------------------


class CpioParser(ArchiveParser):

    def _get_archive_dir(self):
        return os.path.basename(self._archive)[0:5]  # arbitrary

    def _explode_cmd(self):
        """Return the appropriate command for exploding a cpio archive"""

        self._archive_dir = os.path.join(self._temp_dir, self._get_archive_dir())

        if not _has_executable("pkgtrans"):
            raise ArchiveException("cannot open %s, 'pkgtrans' not found" % self._archive)

        return "cd %s; mkdir %s; pkgtrans %s %s all" % \
               (self._temp_dir, self._archive_dir, self._archive, self._archive_dir)

# internal helper methods ------------------------------------------------


def _has_executable(exc):
    """Return true if the executable is found in the $PATH"""

    # flag the error condition, this will evaluate to False
    if "PATH" not in os.environ:
        return None

    # this is posix specific
    dirs = os.environ["PATH"].split(':')

    for dirname in dirs:
        path = os.path.join(dirname, exc)
        if os.access(path, os.X_OK):
            return True

    return False


def _my_popen(cmd):
    """Execute a command as a subprocess and return its exit status"""

    # pylint: disable=E1101
    popen = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE, close_fds=True, shell=True)
    popen.stdin.close()

    txt = ""
    while 1:
        rd, _wr, ex = select.select([popen.stdout, popen.stderr], [], [popen.stdout, popen.stderr], 5)
        if ex:
            txt += popen.stdout.read()
            txt += popen.stderr.read()
            break
        if rd:
            txt += rd[0].read()
        break

    status = popen.wait()
    if status != 0:
        raise Exception("%s exited with status %s and error\n%s" % (cmd, status, txt))

    return

# NOTE these next two functions rely on file magic to determine the compression
# and archive types. some file magic information can be found here:
# http://www.astro.keele.ac.uk/oldusers/rno/Computing/File_magic.html


def _decompress(archive):
    """[internal] Decompress compressed archives and return the new archive name"""

    cmd = ""
    sfx_list = None

    # determine which type of compression we're dealing with, if any
    fd = open(archive, 'r')
    magic = fd.read(2)
    fd.close()

    if magic == "BZ":
        cmd = "bunzip2"
        sfx_list = (".bz2", ".bz")

    elif magic == "\x1F\x9D":
        cmd = "uncompress"
        sfx_list = (".Z",)

    elif magic == "\x1F\x8B":
        cmd = "gunzip"
        sfx_list = (".gz",)

    # decompress the archive if it is compressed
    if cmd:

        if not _has_executable(cmd):
            raise ArchiveException("Cannot decompress %s, '%s' not found" % (archive, cmd))

        print("Decompressing archive")

        _my_popen("%s %s" % (cmd, archive))

        # remove the now invalid suffix from the archive name
        for sfx in sfx_list:
            if archive[-len(sfx):] == sfx:
                archive = archive[:-len(sfx)]
                break

    return archive

# archive parser factory -------------------------------------------------


def get_archive_parser(archive, tempdir="/tmp/"):
    """Factory function that returns an ArchiveParser object for the given archive"""

    # decompress the archive
    archive = _decompress(archive)
    parserClass = None
    fd = open(archive, 'r')

    magic = fd.read(4)
    if magic == "PK\x03\x04":
        parserClass = ZipParser

    fd.seek(0)
    magic = fd.read(20)
    if magic == "# PaCkAgE DaTaStReAm":
        parserClass = CpioParser

    fd.seek(257)
    magic = fd.read(5)
    if magic == "ustar":
        parserClass = TarParser

    # pre-posix tar doesn't have any standard file magic
    if archive.endswith(".tar"):
        parserClass = TarParser

    fd.close()

    if parserClass is None:
        raise UnknownArchiveError("Wasn't able to identify: '%s'" % archive)

    return parserClass(archive, tempdir)
07070100000007000081B40000000000000000000000015DDE99FC000028FC000000000000000000000000000000000000001700000000mgr-push/connection.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 socket
import base64
import sys
# pylint: disable=F0401,E0611,W0632
from rhn import connections, rpclib

from uyuni.common.usix import ListType, TupleType, IntType, raise_with_tb
from uyuni.common.rhn_pkg import InvalidPackageError, package_from_filename
from rhnpush.utils import tupleify_urlparse

if sys.version_info[0] == 3:
    from urllib.parse import splitport
    from urllib.parse import urlparse
else:
    from urlparse import urlparse
    from urllib import splitport # pylint: disable=C0412

# pylint: disable=W0622
class ConnectionError(Exception):
    pass

# pylint: disable=R0902


class BaseConnection:

    def __init__(self, uri, proxy=None):
        self._scheme, (self._host, self._port), self._path = parse_url(uri)[:3]

        if proxy:
            arr = rpclib.get_proxy_info(proxy)
            self._proxy_host = arr[0]
            self._proxy_port = arr[1]
            self._proxy_username = arr[2]
            self._proxy_password = arr[3]
        else:
            self._proxy_host = None

        self._trusted_certs = None
        self._connection = None
        self._timeout = None

    def set_timeout(self, timeout):
        self._timeout = timeout

    def get_connection(self):
        if self._scheme not in ['http', 'https']:
            raise ValueError("Unsupported scheme", self._scheme)
        params = {}
        if self._timeout is not None:
            params['timeout'] = self._timeout
        if self._proxy_host:
            params.update({
                'host': self._host,
                'port': self._port,
                'proxy': "%s:%s" % (self._proxy_host, self._proxy_port),
                'username': self._proxy_username,
                'password': self._proxy_password,
            })
            if self._scheme == 'http':
                return connections.HTTPProxyConnection(**params)
            params['trusted_certs'] = self._trusted_certs
            return connections.HTTPSProxyConnection(**params)
        else:
            if self._scheme == 'http':
                return connections.HTTPConnection(self._host, self._port, **params)
            params['trusted_certs'] = self._trusted_certs
            return connections.HTTPSConnection(self._host, self._port, **params)

    def connect(self):
        self._connection = self.get_connection()
        self._connection.connect()

    def putrequest(self, method, url=None, skip_host=0):
        if url is None:
            url = self._path
        return self._connection.putrequest(method, url=url,
                                           skip_host=skip_host)

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


class PackageUpload:
    header_prefix = "X-RHN-Upload"
    user_agent = "rhn-package-upload"

    def __init__(self, url, proxy=None):
        self.connection = BaseConnection(url, proxy)
        self.headers = {}
        self.package_name = None
        self.package_epoch = None
        self.package_version = None
        self.package_release = None
        self.package_arch = None
        self.checksum = None
        self.checksum_type = None
        self.nvra = None
        self._resp_headers = None
        self.packaging = None
        self._response = None

    def set_header(self, name, value):
        if name not in self.headers:
            vlist = self.headers[name] = []
        else:
            vlist = self.headers[name]
            if not isinstance(vlist, (ListType, TupleType)):
                vlist = [vlist]
        vlist.append(value)

    def send_http_headers(self, method, content_length=None):
        try:
            self.connection.connect()
        except socket.error:
            e = sys.exc_info()[1]
            raise_with_tb(ConnectionError("Error connecting", str(e)), sys.exc_info()[2])

        # Add content_length
        if 'Content-Length' not in self.headers and \
                content_length is not None:
            self.set_header('Content-Length', content_length)
        self.connection.putrequest(method)

        # Additional headers
        for hname, hval in self.headers.items():
            if not isinstance(hval, (ListType, TupleType)):
                hval = [hval]

            for v in hval:
                self.connection.putheader(str(hname), str(v))

        self.connection.endheaders()

    def send_http_body(self, stream_body):
        if stream_body is None:
            return
        stream_body.seek(0, 0)
        buffer_size = 16384
        while 1:
            buf = stream_body.read(buffer_size)
            if not buf:
                break
            try:
                self.connection.send(buf)
            except IOError:
                e = sys.exc_info()[1]
                raise_with_tb(ConnectionError("Error sending body", str(e)), sys.exc_info()[2])

    def send_http(self, method, stream_body=None):
        if stream_body is None:
            content_length = 0
        else:
            stream_body.seek(0, 2)
            content_length = stream_body.tell()
        self.send_http_headers(method, content_length=content_length)
        self.send_http_body(stream_body)
        self._response = self.connection.getresponse()
        self._resp_headers = self._response.msg

        return self._response

    def upload(self, filename, fileChecksumType, fileChecksum):
        """
        Uploads a file.
        Returns (http_error_code, error_message)
        Sets:
            self.package_name
            self.package_epoch
            self.package_version
            self.package_release
            self.package_arch
        """
        try:
            a_pkg = package_from_filename(filename)
            a_pkg.read_header()
        except InvalidPackageError:
            return -1, "Not an RPM: %s" % filename

        # Set some package data members
        self.package_name = a_pkg.header['name']
        self.package_epoch = a_pkg.header['epoch']
        self.package_version = a_pkg.header['version']
        self.package_release = a_pkg.header['release']
        if a_pkg.header.is_source:
            if 1051 in a_pkg.header.keys():
                self.package_arch = 'nosrc'
            else:
                self.package_arch = 'src'
        else:
            self.package_arch = a_pkg.header['arch']
        self.packaging = a_pkg.header.packaging

        nvra = [self.package_name, self.package_version, self.package_release,
                self.package_arch]

        if isinstance(nvra[3], IntType):
            # Old rpm format
            return -1, "Deprecated RPM format: %s" % filename

        self.nvra = nvra

        # use the precomputed passed checksum
        self.checksum_type = fileChecksumType
        self.checksum = fileChecksum

        # Set headers
        self.set_header("Content-Type", "application/x-rpm")
        self.set_header("User-Agent", self.user_agent)
        # Custom RHN headers
        prefix = self.header_prefix
        self.set_header("%s-%s" % (prefix, "Package-Name"), nvra[0])
        self.set_header("%s-%s" % (prefix, "Package-Version"), nvra[1])
        self.set_header("%s-%s" % (prefix, "Package-Release"), nvra[2])
        self.set_header("%s-%s" % (prefix, "Package-Arch"), nvra[3])
        self.set_header("%s-%s" % (prefix, "Packaging"), self.packaging)
        if self.checksum_type == 'md5':
            self.set_header("%s-%s" % (prefix, "File-MD5sum"), self.checksum)
        else:
            self.set_header("%s-%s" % (prefix, "File-Checksum-Type"), self.checksum_type)
            self.set_header("%s-%s" % (prefix, "File-Checksum"), self.checksum)

        a_pkg.input_stream.seek(0, 0)
        self._response = self.send_http('POST', stream_body=a_pkg.input_stream)
        a_pkg.input_stream.close()

        retval = self.process_response()
        self.connection.close()
        return retval

    def process_response(self):
        status = self._response.status
        reason = self._response.reason
        if status == 200:
            # OK
            return status, "OK"
        if status == 201:
            # Created
            return (status, "%s %s: %s-%s-%s.%s.rpm already uploaded" % (
                self.checksum_type, self.checksum,
                self.nvra[0], self.nvra[1], self.nvra[2], self.nvra[3]))
        if status in (404, 409):
            # Conflict
            errstring = self.get_error_message(self._resp_headers)
            return status, errstring
        data = self._response.read()
        if status == 403:
            # In this case Authentication is no longer valid on server
            # client needs to re-authenticate itself.
            errstring = self.get_error_message(self._resp_headers)
            return status, errstring
        if status == 500:
            print("Internal server error", status, reason)
            errstring = self.get_error_message(self._resp_headers)
            return status, data + errstring

        return status, data

    def get_error_message(self, headers):
        prefix = self.header_prefix + '-Error'
        text = [x[1] for x in headers.getaddrlist(prefix + '-String')]
        # text is a list now, convert it to a string
        text = '\n'.join(text)
        # pylint: disable=W1505
        text = base64.decodestring(text)
        return text


def parse_url(url, scheme="http", path='/'):
    _scheme, netloc, _path, params, query, fragment = tupleify_urlparse(
        urlparse(url))
    if not netloc:
        # No scheme - trying to patch it up ourselves?
        url = scheme + "://" + url
        _scheme, netloc, _path, params, query, fragment = tupleify_urlparse(
            urlparse(url))

    if not netloc:
        # XXX
        raise Exception()

    (host, port) = splitport(netloc)

    if not _path:
        _path = path

    return (_scheme, (host, port), _path, params, query, fragment)
07070100000008000081B40000000000000000000000015DDE99FC0000003C000000000000000000000000000000000000001C00000000mgr-push/mgr-push-rpmlintrcaddFilter("suse-filelist-forbidden-sysconfig .*/sysconfig")
07070100000009000081B40000000000000000000000015DDE99FC000021C2000000000000000000000000000000000000001A00000000mgr-push/mgr-push.changes-------------------------------------------------------------------
Wed Nov 27 16:42:09 CET 2019 - jgonzalez@suse.com

- version 4.1.1-1
- replace spacewalk-usix and spacewalk-backend-libs with uyuni-common-libs
- Bump version to 4.1.0 (bsc#1154940)

-------------------------------------------------------------------
Wed May 15 20:08:24 CEST 2019 - jgonzalez@suse.com

- version 4.0.6-1
- Obsolete newer versions of rhnpush

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

- version 4.0.5-1
- SPEC cleanup
- Fix wrong bugzilla entry at changelog

-------------------------------------------------------------------
Mon Apr 22 12:02:26 CEST 2019 - jgonzalez@suse.com

- version 4.0.4-1
- add makefile and configuration for pylint

-------------------------------------------------------------------
Wed Jan 16 12:16:04 CET 2019 - jgonzalez@suse.com

- version 4.0.3-1
- Fix dependencies to spacewalk-backend-libs

-------------------------------------------------------------------
Fri Oct 26 09:56:12 CEST 2018 - jgonzalez@suse.com

- version 4.0.2-1
- Add Uyuni URL to package

-------------------------------------------------------------------
Fri Aug 10 16:02:45 CEST 2018 - jgonzalez@suse.com

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

-------------------------------------------------------------------
Mon Apr 23 08:53:06 CEST 2018 - jgonzalez@suse.com

- version 5.5.113.2-1
- Sync with upstream (bsc#1083294)
- rhnpush is needed on python2 due to spacewalk-proxy

-------------------------------------------------------------------
Mon Mar 26 08:40:29 CEST 2018 - jgonzalez@suse.com

- version 5.5.113.1-1
- Sync with upstream (bsc#1083294)
- Build python2 on SUSE

-------------------------------------------------------------------
Mon Mar 05 09:16:14 CET 2018 - jgonzalez@suse.com

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

-------------------------------------------------------------------
Fri Feb 23 10:48:18 CET 2018 - jgonzalez@suse.com

- version 5.5.111.1-1
- Sync with upstream

-------------------------------------------------------------------
Thu Oct 26 17:16:51 CEST 2017 - mc@suse.de

- version 5.5.108.1-1
- move rhnpush files into proper python2/python3 subpackages

-------------------------------------------------------------------
Wed Aug 09 12:11:07 CEST 2017 - fkobzik@suse.de

- version 5.5.104.3-1
- disable pylint check during build also for RHEL7

-------------------------------------------------------------------
Mon May 29 15:40:35 CEST 2017 - mc@suse.de

- version 5.5.104.2-1
- fixed pylint warnings disabled python3 pylint on Fedora 26+ for now
- removed outdated solaris2mpm script

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

- version 5.5.104.1-1
- Pylint fixes in rhnpush

-------------------------------------------------------------------
Tue Mar 07 16:00:37 CET 2017 - mc@suse.de

- version 5.5.102.1-1
- Updated links to github in spec files
- require spacewalk-usix indead of spacewalk-backend-usix

-------------------------------------------------------------------
Wed Jan 11 15:34:37 CET 2017 - michele.bologna@suse.com

- version 5.5.101.1-1
- Version 5.5.101-1

-------------------------------------------------------------------
Wed Apr 06 08:49:39 CEST 2016 - mc@suse.de

- version 5.5.91.2-1
- don't count on having newest rhn-client-tools

-------------------------------------------------------------------
Sat Jan 16 11:29:10 CET 2016 - mc@suse.de

- version 5.5.91.1-1
- alow to use existing rpcServer when creating RhnServer

-------------------------------------------------------------------
Mon Nov 30 10:59:38 CET 2015 - mc@suse.de

- version 5.5.90.1-1
- rhn-satellite-activate: manual references removed

-------------------------------------------------------------------
Wed Oct 07 15:56:48 CEST 2015 - mc@suse.de

- version 5.5.89.1-1
- Wire in timeout for rhnpush

-------------------------------------------------------------------
Mon Jun 22 15:30:09 CEST 2015 - jrenner@suse.de

- version 5.5.71.8-1
- fix --ca-chain option for rhnpush (bsc#931503, bsc#895869)

-------------------------------------------------------------------
Tue Feb 03 13:08:35 CET 2015 - mc@suse.de

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

-------------------------------------------------------------------
Thu Dec 18 13:33:42 CET 2014 - mc@suse.de

- version 5.5.71.6-1
- rhnpush: fix pylint checks and code quality

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

- sanitize release line in specfile

-------------------------------------------------------------------
Fri Jun 13 16:19:07 CEST 2014 - jrenner@suse.de

- version 5.5.71.5-1
- Add default path structure to proxy lookaside that avoids collisions
- Make rhnpush backwards-compatible with old spacewalk-proxy

-------------------------------------------------------------------
Thu Apr 10 17:32:20 CEST 2014 - mc@suse.de

- version 5.5.71.4-1

-------------------------------------------------------------------
Thu Apr 10 17:32:04 CEST 2014 - mc@suse.de

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

-------------------------------------------------------------------
Thu Mar 27 10:07:33 CET 2014 - fcastelli@suse.com

- version 5.5.71.3-1
- correcting exception type

-------------------------------------------------------------------
Thu Feb 13 15:03:40 CET 2014 - mc@suse.de

- version 5.5.71.2-1
- use SUSE package name in requires
- pylint check only on SLE11

-------------------------------------------------------------------
Tue Dec 10 14:11:22 CET 2013 - mc@suse.de

- version 5.5.71.1-1
- switch to 2.1

-------------------------------------------------------------------
Thu Aug 02 16:23:54 CEST 2012 - mc@suse.de

- version 5.5.42.7-1
- code cleanup
- fixed man page
- removed dead --no-cache option
- fixed --no-session-caching option

-------------------------------------------------------------------
Mon Jul 16 15:17:44 CEST 2012 - ug@suse.de

- version 5.5.42.6-1
- solaris2mpm on RHEL5 is not supported
- solaris2mpm needs zipfile with ZIP64 extension
- remove trailing '/' from from archive dir
- reuse UploadError from uploadLib
- simplified authentication code

-------------------------------------------------------------------
Mon Jun 25 10:22:27 CEST 2012 - mc@suse.de

- version 5.5.42.5-1
- removed commented out code and obsoleted comments

-------------------------------------------------------------------
Thu Jun 21 10:52:17 CEST 2012 - jrenner@suse.de

- version 5.5.42.4-1
- add COPYING file (bnc#764854)

-------------------------------------------------------------------
Thu May 31 10:55:28 CEST 2012 - mc@suse.de

- version 5.5.42.3-1
- Use the correct a_pkg variable.

-------------------------------------------------------------------
Fri Apr 27 16:54:01 CEST 2012 - mc@suse.de

- version 5.5.42.2-1
- removed unused get_header_struct_size()
- removed unused function get_header_byte_range()

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

- version 5.5.42.1-1
- Bumping package version

-------------------------------------------------------------------
Tue Feb 22 16:23:06 CET 2011 - mantel@suse.de

- add missing "import string" (bnc#672643)

-------------------------------------------------------------------
Thu Feb 17 14:06:01 CET 2011 - mantel@suse.de

- fix build: create symlink only after binary has been installed

-------------------------------------------------------------------
Thu Feb 17 13:39:50 CET 2011 - mantel@suse.de

- rename "Red Hat Network" to "SUSE Manager" for SUSE (bnc#672643)
- add mgrpush symlink (bnc#672643)

-------------------------------------------------------------------
Tue Feb  1 16:54:39 CET 2011 - mc@suse.de

- fix build for RH4 

-------------------------------------------------------------------
Tue Jan 11 16:39:04 CET 2011 - mc@suse.de

- tag strings which needs changes
- fix Requires line in spec 

-------------------------------------------------------------------
Wed Sep 15 12:16:32 CEST 2010 - mantel@suse.de

- fix post-build-checks

-------------------------------------------------------------------
Tue Sep 14 17:59:47 CEST 2010 - mantel@suse.de

- Initial release of rhnpush

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

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


# Old name and version+1 before renaming to mgr-push
%define oldname rhnpush
%define oldversion 5.5.114
%{!?pylint_check: %global pylint_check 0}

%if 0%{?fedora} || 0%{?suse_version} > 1320 || 0%{?rhel} >= 8
%global build_py3   1
%global default_py3 1
%endif

%global build_py2   1

%define pythonX %{?default_py3: python3}%{!?default_py3: python2}

Name:           mgr-push
Summary:        Package uploader for the Spacewalk
License:        GPL-2.0-only
Group:          Applications/System
Url:            https://github.com/uyuni-project/uyuni
Version:        4.1.1
Provides:       %{oldname} = %{oldversion}
Obsoletes:      %{oldname} < %{oldversion}
Release:        1%{?dist}
Source0:        https://github.com/spacewalkproject/spacewalk/archive/%{name}-%{version}.tar.gz
Source1:        %{name}-rpmlintrc
BuildRoot:      %{_tmppath}/%{name}-%{version}-build
%if 0%{?fedora} || 0%{?rhel} || 0%{?suse_version} >= 1210
BuildArch:      noarch
%endif
Requires:       %{pythonX}-%{name} = %{version}-%{release}
BuildRequires:  docbook-utils
BuildRequires:  gettext
%if 0%{?pylint_check}
%if 0%{?build_py2}
BuildRequires:  spacewalk-python2-pylint
%endif
%if 0%{?build_py3}
BuildRequires:  spacewalk-python3-pylint
%endif
%endif

%description
rhnpush uploads package headers to the Spacewalk
servers into specified channels and allows for several other channel
management operations relevant to controlling what packages are available
per channel.

%if 0%{?build_py2}
%package -n python2-%{name}
Summary:        Package uploader for the Spacewalk or Red Hat Satellite Server
Group:          Applications/System
Provides:       python2-%{oldname} = %{oldversion}
Obsoletes:      python2-%{oldname} < %{oldversion}
Requires:       %{name} = %{version}-%{release}
%if 0%{?fedora} >= 28
Requires:       python2-rpm
BuildRequires:  python2-devel
%else
Requires:       rpm-python
BuildRequires:  python-devel
%endif
Requires:       python2-rhn-client-tools
Requires:       rhnlib >= 2.8.3
Requires:       python2-uyuni-common-libs
BuildRequires:  python2-rhn-client-tools
BuildRequires:  python2-uyuni-common-libs

%description -n python2-%{name}
Python 2 specific files for rhnpush.
%endif

%if 0%{?build_py3}
%package -n python3-%{name}
Summary:        Package uploader for the Spacewalk or Red Hat Satellite Server
Group:          Applications/System
Provides:       python3-%{oldname} = %{oldversion}
Obsoletes:      python3-%{oldname} < %{oldversion}
Requires:       %{name} = %{version}-%{release}
%if 0%{?suse_version}
Requires:       python3-rpm
%else
Requires:       rpm-python3
%endif
Requires:       python3-rhn-client-tools
Requires:       python3-rhnlib >= 2.8.3
Requires:       python3-uyuni-common-libs
BuildRequires:  python3-devel
BuildRequires:  python3-rhn-client-tools
BuildRequires:  python3-rpm-macros
BuildRequires:  python3-uyuni-common-libs

%description -n python3-%{name}
Python 3 specific files for rhnpush.
%endif

%prep
%setup -q

%build
make -f Makefile.rhnpush all

%install
install -d $RPM_BUILD_ROOT/%{python_sitelib}
%if 0%{?build_py2}
make -f Makefile.rhnpush install PREFIX=$RPM_BUILD_ROOT ROOT=%{python_sitelib} \
    MANDIR=%{_mandir} PYTHON_VERSION=%{python_version}
%endif

%if 0%{?build_py3}
sed -i 's|#!/usr/bin/python|#!/usr/bin/python3|' rhnpush
install -d $RPM_BUILD_ROOT/%{python3_sitelib}
make -f Makefile.rhnpush install PREFIX=$RPM_BUILD_ROOT ROOT=%{python3_sitelib} \
    MANDIR=%{_mandir} PYTHON_VERSION=%{python3_version}
%endif

%define default_suffix %{?default_py3:-%{python3_version}}%{!?default_py3:-%{python_version}}
ln -s rhnpush%{default_suffix} $RPM_BUILD_ROOT%{_bindir}/rhnpush
%if 0%{?suse_version}
ln -s rhnpush $RPM_BUILD_ROOT/%{_bindir}/mgrpush
%endif

%check
%if 0%{?pylint_check}
# check coding style
%if 0%{?build_py2}
export PYTHONPATH=$RPM_BUILD_ROOT%{python_sitelib}
spacewalk-python2-pylint $RPM_BUILD_ROOT%{_bindir} $RPM_BUILD_ROOT%{python_sitelib}
%endif
%if 0%{?build_py3}
export PYTHONPATH=$RPM_BUILD_ROOT%{python3_sitelib}
spacewalk-python3-pylint $RPM_BUILD_ROOT%{_bindir} $RPM_BUILD_ROOT%{python3_sitelib}
%endif
%endif

%files
%defattr(-,root,root)
%{_bindir}/rhnpush
%{_bindir}/rpm2mpm
%dir %{_sysconfdir}/sysconfig/rhn
%if 0%{?suse_version}
%{_bindir}/mgrpush
%endif
%config(noreplace) %attr(644,root,root) %{_sysconfdir}/sysconfig/rhn/rhnpushrc
%{_mandir}/man8/rhnpush.8*
%doc COPYING

%if 0%{?build_py2}
%files -n python2-%{name}
%defattr(-,root,root)
%attr(755,root,root) %{_bindir}/rhnpush-%{python_version}
%{python_sitelib}/rhnpush/
%endif

%if 0%{?build_py3}
%files -n python3-%{name}
%defattr(-,root,root)
%attr(755,root,root) %{_bindir}/rhnpush-%{python3_version}
%{python3_sitelib}/rhnpush/
%endif

%changelog
0707010000000B000041FD0000000000000000000000015DDE99FC00000000000000000000000000000000000000000000001100000000mgr-push/patches0707010000000C000081B40000000000000000000000015DDE99FC000001FB000000000000000000000000000000000000002200000000mgr-push/patches/rhel4-static.dif--- solaris2mpm.py
+++ solaris2mpm.py
@@ -30,7 +30,6 @@ try:
 except ImportError:
     import md5
     class hashlib:
-        @staticmethod
         def new(checksum):
             # Add sha1 if needed.
             if checksum == 'md5':
@@ -39,6 +38,8 @@ except ImportError:
             if checksum not in ['md5', 'sha1']:
                 raise ValueError, "Incompatible checksum type"
 
+        new = staticmethod(new)
+
 from spacewalk.common import rhn_mpm
 
 from archive import get_archive_parser
0707010000000D000081B40000000000000000000000015DDE99FC00001396000000000000000000000000000000000000001200000000mgr-push/pylintrc# mgr-push package pylint configuration

[MASTER]

# Profiled execution.
profile=no

# Pickle collected data for later comparisons.
persistent=no


[MESSAGES CONTROL]

# Disable the message(s) with the given id(s).


disable=I0011,
	C0302,
	C0111,
	R0801,
	R0902,
	R0903,
	R0904,
	R0912,
	R0913,
	R0914,
	R0915,
	R0921,
	R0922,
	W0142,
	W0403,
	W0603,
	C1001,
	W0121,
	useless-else-on-loop,
	bad-whitespace,
	unpacking-non-sequence,
	superfluous-parens,
	cyclic-import,
	redefined-variable-type,
	no-else-return,

        # Uyuni disabled
	E0203,
	E0611,
	E1101,
	E1102

# list of disabled messages:
#I0011: 62: Locally disabling R0201
#C0302:  1: Too many lines in module (2425)
#C0111:  1: Missing docstring
#R0902: 19:RequestedChannels: Too many instance attributes (9/7)
#R0903:  Too few public methods
#R0904: 26:Transport: Too many public methods (22/20)
#R0912:171:set_slots_from_cert: Too many branches (59/20)
#R0913:101:GETServer.__init__: Too many arguments (11/10)
#R0914:171:set_slots_from_cert: Too many local variables (38/20)
#R0915:171:set_slots_from_cert: Too many statements (169/50)
#W0142:228:MPM_Package.write: Used * or ** magic
#W0403: 28: Relative import 'rhnLog', should be 'backend.common.rhnLog'
#W0603: 72:initLOG: Using the global statement
# for pylint-1.0 we also disable
#C1001: 46, 0: Old-style class defined. (old-style-class)
#W0121: 33,16: Use raise ErrorClass(args) instead of raise ErrorClass, args. (old-raise-syntax)
#W:243, 8: Else clause on loop without a break statement (useless-else-on-loop)
# pylint-1.1 checks
#C:334, 0: No space allowed after bracket (bad-whitespace)
#W:162, 8: Attempting to unpack a non-sequence defined at line 6 of (unpacking-non-sequence)
#C: 37, 0: Unnecessary parens after 'not' keyword (superfluous-parens)
#C:301, 0: Unnecessary parens after 'if' keyword (superfluous-parens)

[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=
0707010000000E000081FD0000000000000000000000015DDE99FC000002E6000000000000000000000000000000000000001100000000mgr-push/rhnpush#!/usr/bin/python
#
# Wrapper for rhnpush_main.py
#
import sys

try:
    from rhnpush import rhnpush_main
except ImportError:
    e = sys.exc_info()[1]
    sys.stderr.write("Unable to load module rhnpush_main\n")
    sys.stderr.write(str(e) + "\n")
    sys.exit(1)

if __name__ == '__main__':
    try:
        sys.exit(rhnpush_main.main() or 0)
    except KeyboardInterrupt:
        e = sys.exc_info()[1]
        sys.stderr.write("\nUser interrupted process.\n")
        sys.exit(0)
    except SystemExit:
        e = sys.exc_info()[1]
        sys.exit(e.code)

    # pylint: disable=W0703
    except Exception:
        e = sys.exc_info()[1]
        sys.stderr.write("\nERROR: unhandled exception occurred: (%s).\n" % e)
        sys.exit(-1)
0707010000000F000081B40000000000000000000000015DDE99FC0000329F000000000000000000000000000000000000001600000000mgr-push/rhnpush.sgml<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V3.1//EN" [
<!ENTITY RHNSAT "Spacewalk Server" >
<!ENTITY RHNPUSH "Spacewalk Package Pusher" >

]>
<refentry>

<RefMeta>
<RefEntryTitle>rhnpush</RefEntryTitle><manvolnum>8</manvolnum>
<RefMiscInfo>Version 2.0</RefMiscInfo>
</RefMeta>

<RefNameDiv>
<RefName><command>rhnpush</command></RefName>
<RefPurpose>
Utility to push binary and source RPMs into an RHN channel.
</RefPurpose>
</RefNameDiv>

<RefSynopsisDiv>
<Synopsis>
    <cmdsynopsis>
        <command>rhnpush</command>
        <arg>options <replaceable>...</replaceable></arg>
        <arg>-v</arg> <arg>--verbose</arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>-d<replaceable>DIRECTORY</replaceable></arg>
        <arg>--dir=<replaceable>DIRECTORY</replaceable></arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>-c<replaceable>CHANNEL_LABEL</replaceable></arg>
        <arg>--channel=<replaceable>CHANNEL_LABEL</replaceable></arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>-n<replaceable>N_HEADERS_PER_CALL</replaceable></arg>
        <arg>--count=<replaceable>N_HEADERS_PER_CALL</replaceable></arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>-l</arg> <arg>--list</arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>-r<replaceable>RELATIVE_DIRECTORY</replaceable></arg>
        <arg>--reldir=<replaceable>RELATIVE_DIRECTORY</replaceable></arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>-o<replaceable>ORGANIZATION_ID</replaceable></arg>
        <arg>--orgid=<replaceable>ORGANIZATION_ID</replaceable></arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>-u<replaceable>USERNAME</replaceable></arg>
        <arg>--username=<replaceable>USERNAME</replaceable></arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>-p<replaceable>PASSWORD</replaceable></arg>
        <arg>--password=<replaceable>PASSWORD</replaceable></arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>-s</arg> <arg>--stdin</arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>-X<replaceable>glob</replaceable></arg>
        <arg>--exclude=<replaceable>GLOB</replaceable></arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>--force</arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>--nosig</arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>--newest</arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>--nullorg</arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>--header</arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>--source</arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>--server=<replaceable>SERVER</replaceable></arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>--proxy=<replaceable>PROXY:PORT</replaceable></arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>-N</arg> <arg>--new-cache</arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>--no-session-caching</arg>
    </cmdsynopsis>
  <cmdsynopsis>
        <arg>--extended-test</arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>--test</arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>--tolerant</arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>--timeout=<replaceable>SECONDS</replaceable></arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>-h</arg> <arg>--help</arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>-?</arg> <arg>--usage</arg>
    </cmdsynopsis>
</Synopsis>
</RefSynopsisDiv>

<RefSect1><Title>Description</Title>

<para>
    The &RHNPUSH; (<emphasis>rhnpush</emphasis>) pushes RPMs into locally
    managed channels on a &RHNSAT;. Refer to the Red Hat Satellite documentation
    for further reference.
</para>

</RefSect1>

<RefSect1><Title>Options</Title>
<variablelist>
    <varlistentry>
        <term>-v --verbose</term>
        <listitem>
            <para>Increase verbosity (can use multiple times).</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>-d<replaceable>DIRECTORY</replaceable>
        --dir=<replaceable>DIRECTORY</replaceable></term>
        <listitem>
            <para>process packages from this directory.</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>-c<replaceable>CHANNEL_LABEL</replaceable>,
            --channel=<replaceable>CHANNEL_LABEL</replaceable></term>
        <listitem>
            <para>process data for this specific channel (specified by label)
            only.
            NOTE: the channel's *label* is NOT the same as the channel's
            *name*.</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>-n<replaceable>N_HEADERS_PER_CALL</replaceable>
        --count=<replaceable>N_HEADERS_PER_CALL</replaceable></term>
        <listitem>
            <para>process this number of headers per call. Current default is
            25. Must be an integer.</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>-l --list</term>
        <listitem>
            <para>only list the specified channels.</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>-r<replaceable>RELATIVE_DIRECTORY</replaceable>
        --reldir=<replaceable>RELATIVE_DIRECTORY</replaceable></term>
        <listitem>
            <para>relative directory to associate with each file.</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>-o<replaceable>ORGANIZATION_ID</replaceable>
        --orgid=<replaceable>ORGANIZATION_ID</replaceable></term>
        <listitem>
            <para>Your organization's ID number. Must be an integer.</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>-u<replaceable>USERNAME</replaceable>
        --username=<replaceable>USERNAME</replaceable></term>
        <listitem>
            <para>username of user that has administrative access to the
            specified channel.</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>-p<replaceable>PASSWORD</replaceable>
        --password=<replaceable>PASSWORD</replaceable></term>
        <listitem>
            <para>see --username.</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>-s --stdin</term>
        <listitem>
            <para>read the package names from standard-in (allows piping).</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>-X<replaceable>glob</replaceable>
        --exclude=<replaceable>GLOB</replaceable></term>
        <listitem>
            <para>exclude packages that matches this glob expression.</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>--force</term>
        <listitem>
            <para>force the package upload (overwrites if package is already
            uploaded).</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>--tolerant</term>
        <listitem>
            <para>If rhnpush errors while uploading a package, continue attempting
            to push the rest of the packages.</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>--nosig</term>
        <listitem>
            <para>don't fail if packages are unsigned.</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>--newest</term>
        <listitem>
            <para>push only the packages that are newer than those on the
            server.</para>
            <para>Please note that source RPMs are special in that their
            versions are never compared. Therefore, using this option with
            &RHNPUSH; will upload source RPMs for the specified channels only
            if they were not previously uploaded, and binaries built from
            them exist in those channels.</para>
            <para>In other words, you have to upload a binary rpm before being
            able to use <emphasis>--newest</emphasis> with its source rpm.
            </para>
        </listitem>

    </varlistentry>
    <varlistentry>
        <term>--timeout=<replaceable>SECONDS</replaceable></term>
        <listitem>
            <para>Change default connection timeout.</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>--nullorg</term>
        <listitem>
            <para>use the null org ID (most often the case).</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>--header</term>
        <listitem>
            <para>only upload the headers.</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>--source</term>
        <listitem>
            <para>the packages indicated are source packages (we treat them differently).</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>--server=<replaceable>SERVER</replaceable></term>
        <listitem>
            <para>push to this server. The format is http[s]://&lt;hostname&gt;/APP</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>--proxy=<replaceable>PROXY:PORT</replaceable></term>
        <listitem>
            <para>Use proxy server (<server>:<port>)</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>--test</term>
        <listitem>
            <para>only print the packages to be pushed, don't actually push
            them.</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>-N --new-cache</term>
        <listitem>
            <para>create a new username/password cache</para>
        </listitem>
    </varlistentry>
   <varlistentry>
        <term>--no-session-caching</term>
        <listitem>
            <para>This option disabled session token authentication. Useful if you want to push to two or more different servers.</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>--extended-test</term>
        <listitem>
            <para>perform a more verbose test.</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>-h --help</term>
        <listitem>
            <para>briefly describe the options.</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>-? --usage</term>
        <listitem>
            <para>usage summary.</para>
        </listitem>
    </varlistentry>
</variablelist>
</RefSect1>

<RefSect1><Title>Notes on using rhnpush</Title>
<simplelist>
    <member>
Rhnpush has three configuration files called /etc/sysconfig/rhn/rhnpushrc, ~/.rhnpushrc, and ./.rhnpushrc.
    </member>
    <member>
/etc/sysconfig/rhn/rhnpushrc is the system-wide default settings for rhnpush.
    </member>
    <member>
~/.rhnpushrc is the user-specific settings that override the system-wide settings.
    </member>
    <member>
./.rhnpushrc controls the directory specific settings that override the user-specific and system-specific settings.
    </member>
    <member>
/etc/sysconfig/rhn/rhnpushrc must be present for rhnpush to function correctly. If it is missing, rhnpush will attempt to use a series of default settings stored internally as a replacement. ~/.rhnpushrc and ./.rhnpushrc are not required to be present, but will be used if they are present. They are not created automatically by rhnpush.
    </member>
    <member>
Rhnpush uses a cache, stored at ~/.rhnpushcache, to temporarily hold the username and password for a user.
    </member>
    <member>
If the cache is missing, it will be created by rhnpush.
    </member>
    <member>
If the cache is present and not too old, the usename-password combo will be used as a convenience for the user.
    </member>
    <member>
The amount of time a cache lasts is configurable in any of the three configuration files.
    </member>
    <member>
If your username/password combination gets messed up you have two options. One, you can wait until the cache expires, which takes minutes by default. Two, you can use the --new_cache option to force rhnpush to let you reenter your username/password.
    </member>
    <member>
Using the --stdin and --dir options at the same time works as follows: rhnpush will let you type in rpm names, each rpm name on a separate line. When you have finished entering in rpm names, hit Ctrl-D. Rhnpush will then grab the files from directory you specified with --dir, put them in a list with the rpms you listed through standard input, and send them to the channel that was listed on the command-line or in the configuration files.
    </member>
</simplelist>
</RefSect1>

<RefSect1><Title>See Also</Title>
<simplelist>
    <member>db-control(1) - embedded database environment only!</member>
    <member>rhn-charsets(8)</member>
    <member>rhn-schema-version(8)</member>
    <member>rhn-ssl-dbstore(8)</member>
    <member>satellite-debug(8)</member>
    <member>satellite-sync(8)</member>
</simplelist>
</RefSect1>

<RefSect1><Title>Authors</Title>
<simplelist>
    <member>Todd Warner <email>taw@redhat.com</email></member>
    <member>Mihai Ibanescu <email>misa@redhat.com</email></member>
</simplelist>
</RefSect1>
</RefEntry>
07070100000010000081B40000000000000000000000015DDE99FC00000633000000000000000000000000000000000000001A00000000mgr-push/rhnpush_cache.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.
#
# rhnpush_cache.py
#
# Classes that control the caching of usernames and passwords,
# along with the retrieval of the username and password.
#
# UserInfo - Instantiations of this class are pickled.
#            Cache won't be valid after a certain amount of time.
#
# CacheManager - Controls access to the cache.

import os
from rhnpush import utils

# This is the class that contains the session.


class RHNPushSession:

    def __init__(self):
        self.location = os.path.join(utils.get_home_dir(), ".rhnpushcache")
        self.session = None

    def setSessionString(self, session):
        self.session = session

    def getSessionString(self):
        return self.session

    def readSession(self):
        sessionfile = open(self.location, "r")
        self.session = sessionfile.read()
        sessionfile.close()

    def writeSession(self):
        sessionfile = open(self.location, "w")
        sessionfile.write(self.session)
        sessionfile.close()
07070100000011000081B40000000000000000000000015DDE99FC0000149C000000000000000000000000000000000000001B00000000mgr-push/rhnpush_config.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.
#
#
# The configuration file parser for the rhnpush utility.
# The majority of this code is taken from rhncfg/config_common/local_config.py
#
# 11/11/2004 John Wregglesworth
#

import sys

# pylint: disable=F0401
if sys.version_info[0] == 3:
    import configparser as ConfigParser
else:
    import ConfigParser

# Class that contains the options read in from the config file.
# Uses a ConfigParser to create a dictionary of the configuration options.
# That dictionary is then used to add instance variables to the object dynamically.


class rhnpushConfigParser:
    # pylint: disable=W0201
    _instance = None

    def __init__(self, filename=None, ensure_consistency=False):

        # Defaults that are used if the ensure_consistency parameter of the constructor is true
        # and the config file that is being read is missing some values.
        self.options_defaults = {
            'newest':   '0',
            'usage':   '0',
            'header':   '0',
            'test':   '0',
            'nullorg':   '0',
            'source':   '0',
            'stdin':   '0',
            'verbose':   '0',
            'force':   '0',
            'nosig':   '0',
            'list':   '0',
            'exclude':   '',
            'files':   '',
            'orgid':   '',
            'reldir':   '',
            'count':   '',
            'dir':   '',
            'server':   'http://rhn.redhat.com/APP',
            'channel':   '',
            'cache_lifetime': '600',
            'new_cache':   '0',
            'extended_test':   '0',
            'no_session_caching':   '0',
            'proxy':   '',
            'tolerant':   '0',
            'ca_chain':   '/usr/share/rhn/RHN-ORG-TRUSTED-SSL-CERT',
            'timeout': None
        }

        # Used to parse the config file.
        self.settings = ConfigParser.ConfigParser()

        # use options from the rhnpush section.
        self.section = "rhnpush"

        self.username = None
        self.password = None

        if filename:
            self.filename = filename
            self._read_config_files()

        # Take all of the options read from the configuration file and add them as attributes
        #(instance variables, member variables, whatever) of this object.
        self._add_config_as_attr(ensure_consistency=ensure_consistency)

    # Use the ConfigParser to read in the configuration file.
    def _read_config_files(self):
        try:
            self.settings.read([self.filename])
        except IOError:
            e = sys.exc_info()[1]
            print(("Config File Error: line %s, file %s: %s" % (e.lineno, e.filename, e)))
            sys.exit(1)

    def write(self, fileobj):
        try:
            self.settings.write(fileobj)
        except IOError:
            e = sys.exc_info()[1]
            print(("Config File Error: line %s, file %s: %s" % (e.lineno, e.filename, e)))
            sys.exit(1)

    # Returns an option read in from the configuration files and specified by the string variable option.
    # This function can probably be safely removed, since all configuration options become attributes
    # of an instantiation of this class.
    def get_option(self, option):
        try:
            return self.settings.get(self.section, option)
        except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
            e = sys.exc_info()[1]
            print("Option/Section Error: line %s, file %s: %s" % (e.lineno, e.filename, e))
            sys.exit(1)

    # Returns the keys of the attributes of the object.
    def keys(self):
        return list(self.__dict__.keys())

    # Returns the keys of the options read in from the configuration files.
    def _keys(self):
        if self.settings.has_section(self.section):
            return self.settings.options(self.section)

        return ()

    # Returns an option read in from the configuration files.
    def __getitem__(self, item):
        return self.get_option(item)

    def __delitem__(self, item):
        pass

    def __len__(self):
        pass

    def __setitem__(self, key, value):
        pass

    # Takes all of the configuration options read in by the ConfigParser and makes them attributes of the object.
    def _add_config_as_attr(self, ensure_consistency=False):
        for k in self._keys():
            self.__dict__[k] = self.settings.get(self.section, k)

        # ensuring consistency only checks for missing configuration option.
        if ensure_consistency:
            for thiskey in self.options_defaults:
                if thiskey not in self.__dict__:
                    self.__dict__[thiskey] = self.options_defaults[thiskey]
07070100000012000081B40000000000000000000000015DDE99FC00001367000000000000000000000000000000000000002000000000mgr-push/rhnpush_confmanager.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 sys
import os
from rhnpush import rhnpush_config
from rhnpush import utils


class ConfManager:

    def __init__(self, optionparser, store_true_list):
        sysdir = '/etc/sysconfig/rhn'
        homedir = utils.get_home_dir()
        default = 'rhnpushrc'
        regular = '.rhnpushrc'
        deffile = os.path.join(sysdir, default)
        regfile = os.path.join(homedir, regular)
        cwdfile = os.path.join(os.getcwd(), regular)

        self.cfgFileList = [deffile, regfile, cwdfile]
        self.defaultconfig = rhnpush_config.rhnpushConfigParser(ensure_consistency=True)

        # Get a reference to the object containing command-line options
        self.cmdconfig = optionparser
        self.store_true_list = store_true_list

    # Change the files options of the self.userconfig
    # Change the exclude options of the self.userconfig
    def _files_to_list(self):
        # Change the files options to lists.
        if ('files' in self.defaultconfig.__dict__ and
                not isinstance(self.defaultconfig.files, type([]))):
            self.defaultconfig.files = [x.strip() for x in
                                        self.defaultconfig.files.split(',')]

        # Change the exclude options to list.
        if ('exclude' in self.defaultconfig.__dict__ and
                not isinstance(self.defaultconfig.__dict__['exclude'], type([]))):
            self.defaultconfig.exclude = [x.strip() for x in
                                          self.defaultconfig.exclude.split(',')]

    def get_config(self):
        for f in self.cfgFileList:
            if os.access(f, os.F_OK):
                if not os.access(f, os.R_OK):
                    print(("rhnpush does not have read permission on %s" % f))
                    sys.exit(1)
                config2 = rhnpush_config.rhnpushConfigParser(f)
                self.defaultconfig, config2 = utils.make_common_attr_equal(self.defaultconfig, config2)

        self._files_to_list()

        # Change the channel string into a list of strings.
        # pylint: disable=E1103
        if not self.defaultconfig.channel:
            # if no channel then make it null array instead of
            # an empty string array from of size 1 [''] .
            self.defaultconfig.channel = []
        else:
            self.defaultconfig.channel = [x.strip() for x in
                                          self.defaultconfig.channel.split(',')]

        # Get the command line arguments. These take precedence over the other settings
        argoptions, files = self.cmdconfig.parse_args()

        # Makes self.defaultconfig compatible with argoptions by changing all '0' value attributes to None.
        _zero_to_none(self.defaultconfig, self.store_true_list)

        # If verbose isn't set at the command-line, it automatically gets set to zero. If it's at zero, change it to
        # None so the settings in the config files take precedence.
        if argoptions.verbose == 0:
            argoptions.verbose = None

        # Orgid, count, cache_lifetime, and verbose all need to be integers, just like in argoptions.
        if self.defaultconfig.orgid:
            self.defaultconfig.orgid = int(self.defaultconfig.orgid)

        if self.defaultconfig.count:
            self.defaultconfig.count = int(self.defaultconfig.count)

        if self.defaultconfig.cache_lifetime:
            self.defaultconfig.cache_lifetime = int(self.defaultconfig.cache_lifetime)

        if self.defaultconfig.verbose:
            self.defaultconfig.verbose = int(self.defaultconfig.verbose)

        if self.defaultconfig.timeout:
            self.defaultconfig.timeout = int(self.defaultconfig.timeout)

        # Copy the settings in argoptions into self.defaultconfig.
        self.defaultconfig, argoptions = utils.make_common_attr_equal(self.defaultconfig, argoptions)

        # Make sure files is in the correct format.
        if self.defaultconfig.files != files:
            self.defaultconfig.files = files

        return self.defaultconfig


# Changes every option in config that is also in store_true_list that is set to '0' to None
def _zero_to_none(config, store_true_list):
    for opt in config.keys():
        for cmd in store_true_list:
            if str(opt) == cmd and config.__dict__[opt] == '0':
                config.__dict__[opt] = None
07070100000013000081FD0000000000000000000000015DDE99FC0000689D000000000000000000000000000000000000001900000000mgr-push/rhnpush_main.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.
#

#
"""
Management tool for the Spacewalk Proxy.

This script performs various management operations on the Spacewalk Proxy:
- Creates the local directory structure needed to store local packages
- Uploads packages from a given directory to the RHN servers
- Optionally, once the packages are uploaded, they can be linked to (one or
  more) channels, and copied in the local directories for these channels.
- Lists the RHN server's vision on a certain channel
- Checks if the local image of the channel (the local directory) is in sync
  with the server's image, and prints the missing packages (or the extra
  ones)
"""

import os
import random
import sys
import time
# pylint: disable=W0402
from optparse import Option, OptionParser

# pylint: disable=F0401,E0611
from rhn.connections import idn_ascii_to_puny

from rhn import rpclib
from rhn.i18n import sstr
from uyuni.common.rhn_pkg import InvalidPackageError, package_from_filename
from uyuni.common.usix import raise_with_tb

from rhnpush.utils import tupleify_urlparse
from rhnpush import rhnpush_confmanager, uploadLib, rhnpush_v2

if sys.version_info[0] == 3:
    import urllib.parse as urlparse
else:
    import urlparse

# Global settings
BUFFER_SIZE = 65536
HEADERS_PER_CALL = 10
DEBUG = 0
RPMTAG_NOSOURCE = 1051


def main():
    # Initialize a command-line processing object with a table of options
    optionsTable = [
        Option('-v', '--verbose', action='count', help='Increase verbosity',
               default=0),
        Option('-d', '--dir', action='store',
               help='Process packages from this directory'),
        Option('-c', '--channel', action='append',
               help='Manage this channel (specified by label)'),
        Option('-n', '--count', action='store',
               help='Process this number of headers per call', type='int'),
        Option('-l', '--list', action='store_true',
               help='Only list the specified channels'),
        Option('-r', '--reldir', action='store',
               help='Relative dir to associate with the file'),
        Option('-o', '--orgid', action='store',
               help='Org ID', type='int'),
        Option('-u', '--username', action='store',
               help='Use this username to connect to RHN/Satellite'),
        Option('-p', '--password', action='store',
               help='Use this password to connect to RHN/Satellite'),
        Option('-s', '--stdin', action='store_true',
               help='Read the package names from stdin'),
        Option('-X', '--exclude', action='append',
               help='Exclude packages that match this glob expression'),
        Option('--force', action='store_true',
               help='Force the package upload (overwrites if already uploaded)'),
        Option('--nosig', action='store_true', help='Push unsigned packages'),
        Option('--newest', action='store_true',
               help='Only push the packages that are newer than the server ones'),
        Option('--nullorg', action='store_true', help='Use the null org id'),
        Option('--header', action='store_true',
               help='Upload only the header(s)'),
        Option('--source', action='store_true',
               help='Upload source package information'),
        Option('--server', action='store',
               help='Push to this server (http[s]://<hostname>/APP)'),
        Option('--proxy', action='store',
               help='Use proxy server (<server>:<port>)'),
        Option('--test', action='store_true',
               help='Only print the packages to be pushed'),
        Option('-?', '--usage', action='store_true',
               help='Briefly describe the options'),
        Option('-N', '--new-cache', action='store_true',
               help='Create a new username/password cache'),
        Option('--extended-test', action='store_true',
               help='Perform a more verbose test'),
        Option('--no-session-caching', action='store_true',
               help='Disables session-token authentication.'),
        Option('--tolerant', action='store_true',
               help='If rhnpush errors while uploading a package, continue uploading the rest of the packages.'),
        Option('--ca-chain', action='store', help='alternative SSL CA Cert'),
        Option('--timeout', action='store', type='int', metavar='SECONDS',
               help='Change default connection timeout.')
    ]

    # Having to maintain a store_true list is ugly. I'm trying to get rid of this.
    true_list = ['usage', 'test', 'source', 'header', 'nullorg', 'newest',
                 'nosig', 'force', 'list', 'stdin', 'new_cache',
                 'extended_test', 'no_session_caching', 'tolerant']
    # pylint: disable=E1101,E1103
    optionParser = OptionParser(option_list=optionsTable, usage="%prog [OPTION] [<package>]")
    manager = rhnpush_confmanager.ConfManager(optionParser, true_list)
    options = manager.get_config()

    upload = UploadClass(options, files=options.files)

    if options.usage:
        optionParser.print_usage()
        sys.exit(0)

    if options.proxy:
        options.proxy = idn_ascii_to_puny(options.proxy)

    if options.list:
        if not options.channel:
            upload.die(1, "Must specify a channel for --list to work")
        upload.list()
        return

    if options.dir and not options.stdin:
        upload.directory()

    elif options.stdin and not options.dir:
        upload.readStdin()

    elif options.dir and options.stdin:
        upload.readStdin()
        upload.directory()

    if options.exclude:
        upload.filter_excludes()

    if options.newest:
        if not options.channel:
            upload.die(1, "Must specify a channel for --newest to work")

        upload.newest()

    if not upload.files:
        if upload.newest:
            print("No new files to upload; exiting")
        else:
            print("Nothing to do (try --help for more options)")
        sys.exit(0)

    if options.test:
        upload.test()
        return

    if options.extended_test:
        upload.extended_test()
        return

    if options.header:
        upload.uploadHeaders()
        return

    ret = upload.packages()
    if ret != 0:
        return 1


class UploadClass(uploadLib.UploadClass):
    # pylint: disable=E1101,W0201,W0632

    def __init__(self, options, files=None):
        uploadLib.UploadClass.__init__(self, options, files)
        self.url_v2 = None

    def setURL(self):
        server = sstr(idn_ascii_to_puny(self.options.server))
        if server is None:
            self.die(1, "Required parameter --server not supplied")
        scheme, netloc, path, params, query, fragment = tupleify_urlparse(
            urlparse.urlparse(server))
        if not netloc:
            # No schema - trying to patch it up ourselves?
            server = "http://%s" % server
            scheme, netloc, path, params, query, fragment = tupleify_urlparse(
                urlparse.urlparse(server))

        if not netloc:
            self.die(2, "Invalid URL %s" % server)
        if path == '':
            path = '/APP'
        if scheme.lower() not in ('http', 'https'):
            self.die(3, "Unknown URL scheme %s" % scheme)
        self.url = urlparse.urlunparse((scheme, netloc, path, params, query,
                                        fragment))
        self.url_v2 = urlparse.urlunparse((scheme, netloc, "/PACKAGE-PUSH",
                                           params, query, fragment))

    def setOrg(self):
        if self.options.nullorg:
            if self.options.force:
                self.die(1, "ERROR: You cannot force a package to a nullorg channel.")
            else:
                # They push things to the None org id
                self.orgId = ''
        else:
            self.orgId = self.options.orgid or -1

    def setForce(self):
        if self.options.force:
            self.force = 4
        else:
            self.force = None

    def setRelativeDir(self):
        self.relativeDir = self.options.reldir

    def setChannels(self):
        self.channels = self.options.channel or []

    # pylint: disable=W0702
    def _test_force(self):
        test_force_str = "Setting force flag:  %s"
        test_force = "Passed"
        try:
            self.setForce()
        except:
            test_force = "Failed"
        print(test_force_str % test_force)

    def _test_set_org(self):
        test_set_org_str = "Setting the org:    %s"
        test_set_org = "Passed"
        try:
            self.setOrg()
        except:
            test_set_org = "Failed"
        print(test_set_org_str % test_set_org)

    def _test_set_url(self):
        test_set_url_str = "Setting the URL:    %s"
        test_set_url = "Passed"
        try:
            self.setURL()
        except:
            test_set_url = "Failed"
        print(test_set_url_str % test_set_url)

    def _test_set_channels(self):
        test_set_channels_str = "Setting the channels:  %s"
        test_set_channels = "Passed"
        try:
            self.setChannels()
        except:
            test_set_channels = "Failed"
        print(test_set_channels_str % test_set_channels)

    def _test_username_password(self):
        test_user_pass_str = "Setting the username and password:    %s"
        test_user_pass = "Passed"
        try:
            self.setUsernamePassword()
        except:
            test_user_pass = "Failed"
        print(test_user_pass_str % test_user_pass)

    def _test_set_server(self):
        test_set_server_str = "Setting the server:  %s"
        test_set_server = "Passed"
        try:
            self.setServer()
        except:
            test_set_server = "Failed"
        print(test_set_server_str % test_set_server)

    def _test_connect(self):
        auth_ret = uploadLib.call(self.server.packages.test_login,
                                  self.username, self.password)
        if auth_ret == 1:
            test_auth = "Passed"
        else:
            test_auth = "Failed"
        print("Testing connection and authentication:   %s" % test_auth)

    def _test_access(self):
        access_ret = callable(self.server.packages.channelPackageSubscriptionBySession)

        if access_ret == 1:
            test_access = "Passed"
        else:
            test_access = "Failed"
        print("Testing access to upload functionality on server:    %s" % test_access)

    # 12/22/05 wregglej 173287  Added a this funtion to test the new session authentication stuff.
    # It still needs work.
    def _test_authenticate(self):
        self.authenticate()

    def extended_test(self):
        self._test_force()
        self._test_set_org()
        self._test_set_url()
        self._test_set_channels()
        self._test_username_password()
        self._test_set_server()
        self._test_connect()
        self._test_access()
        print("The files that would have been pushed:")
        self.test()

    def packages(self):
        self.setForce()
        # set the org
        self.setOrg()
        # set the URL
        self.setURL()
        # set the channels
        self.setChannels()
        # set the server
        self.setServer()
        # 12/22/05 wregglej 173287 authenticate the session.
        self.authenticate()

        # Do we have the new-style handler available?

        # ping the server for status
        self.warn(2, "url is", self.url_v2)
        ping = rhnpush_v2.PingPackageUpload(self.url_v2, self.options.proxy)
        ping_status, errmsg, headerinfo = ping.ping()
        self.warn(2, "Result codes:", ping_status, errmsg)

        # move patch clusters to the end because all the patches in the cluster
        # have to be pushed before the cluster itself
        files1 = []
        files2 = []
        for filename in self.files:
            if filename.startswith('patch-cluster-'):
                files2.append(filename)
            else:
                files1.append(filename)

        self.files = files1 + files2

        channel_packages = []

        # a little fault tolarence is in order
        random.seed()
        tries = 3

        # satellites < 4.1.0 are no more supported
        if sys.version_info[0] == 3:
            pack_exist_check = headerinfo.get('X-RHN-Check-Package-Exists')
        else:
            pack_exist_check = headerinfo.getheader('X-RHN-Check-Package-Exists')
        if not pack_exist_check:
            self.die(-1, "Pushing to Satellite < 4.1.0 is not supported.")

        (server_digest_hash, pkgs_info, digest_hash) = self.check_package_exists()

        for pkg in self.files:
            ret = None  # pkilambi:errors off as not initialized.this fixes it.

            # temporary fix for picking pkgs instead of full paths
            pkg_key = (pkg.strip()).split('/')[-1]

            if pkg_key not in server_digest_hash:
                continue

            checksum_type, checksum = digest = digest_hash[pkg_key]
            server_digest = tuple(server_digest_hash[pkg_key])

            # compare checksums for existance check
            if server_digest == digest and not self.options.force:
                channel_packages.append(pkgs_info[pkg_key])
                self.warn(1, "Package %s already exists on the SUSE Manager Server-- Skipping Upload...." % pkg)
                continue

            elif server_digest == ():
                self.warn(1,"Package %s Not Found on SUSE Manager Server -- Uploading" % pkg)

            elif server_digest == "on-disk" and not self.options.force:
                channel_packages.append(pkgs_info[pkg_key])
                self.warn(0, "Package on disk but not on db -- Skipping Upload " % pkg)
                continue

            elif server_digest != digest:
                if self.options.force:
                    self.warn(1, "Package checksum %s mismatch  -- Forcing Upload" % pkg)
                else:
                    msg = "Error: Package %s already exists on the server with" \
                          " a different checksum. Skipping upload to prevent" \
                          " overwriting existing package. (You may use rhnpush with" \
                          " the --force option to force this upload if the" \
                          " force_upload option is enabled on your server.)\n" % pkg
                    if not self.options.tolerant:
                        self.die(-1, msg)
                    self.warn(0, msg)
                    continue

            for _t in range(0, tries):
                try:
                    ret = self.package(pkg, checksum_type, checksum)
                    if ret is None:
                        raise uploadLib.UploadError()

                # TODO:  Revisit this.  We throw this error all over the place,
                #        but doing so will cause us to skip the --tolerant logic
                #        below.  I don't think we really want this behavior.
                #        There are some cases where we don't want to retry 3
                #        times, but not at the expense of disabling the tolerant
                #        flag, IMHO.  This loop needs some lovin'.  -- pav

                # FIX: it checks for tolerant flag and aborts only if the flag is
                #not specified
                except uploadLib.UploadError:
                    ue = sys.exc_info()[1]
                    if not self.options.tolerant:
                        self.die(1, ue)
                    self.warn(2, ue)
                except AuthenticationRequired:
                    # session expired so we re-authenticate for the process to complete
                    # this uses the username and password from memory if available
                    # else it prompts for one.
                    self.authenticate()
                except:
                    self.warn(2, sys.exc_info()[1])
                    wait = random.randint(1, 5)
                    self.warn(0, "Waiting %d seconds and trying again..." % wait)
                    time.sleep(wait)
                # The else clause gets executed in the stuff in the try-except block *succeeds*.
                else:
                    break

            # if the preceeding for-loop exits without a call to break, then this else clause gets called.
            # What's kind of weird is that if the preceeding for-loop doesn't call break then an error occurred
            # and all of retry attempts failed. If the for-loop *does* call break then everything is hunky-dory.
            # In short, this else clause only get's called if something is F.U.B.A.R and the retry attempts don't
            # fix anything.
            else:
                if not self.options.tolerant:
                    # pkilambi:bug#176358:this exits with a error code of 1
                    self.die(1, "Giving up after %d attempts" % tries)
                else:
                    print("Giving up after %d attempts and continuing on..." % (tries,))

            # 5/13/05 wregglej - 154248 ?? we still want to add the packages if they're source.
            if ret and self.channels:  # and ret['arch'] != 'src':
                # Don't bother to add the package if
                # no channel was specified or a source rpm was passed
                channel_packages.append(ret)

        # self.channels is never None, it always has at least one entry with an empty string.
        if len(self.channels) == 1 and self.channels[0] == '':
            return
        info = {
            'packages': channel_packages,
            'channels': self.channels
        }
        if self.orgId == '' or self.orgId > 0:
            info['orgId'] = self.orgId

        # 2/3/06 wregglej 173287 Added check to see if we can use session tokens.
        if channel_packages:
            self.authenticate()
            uploadLib.call(self.server.packages.channelPackageSubscriptionBySession,
                           self.session.getSessionString(), info)
        return 0

    # does an existance check of the packages to be uploaded and returns their checksum and other info
    def check_package_exists(self):
        self.warn(2, "Computing checksum and package info. This may take some time ...")
        pkg_hash = {}
        digest_hash = {}

        for pkg in self.files:
            pkg_info = {}
            pkg_key = (pkg.strip()).split('/')[-1]

            if not os.access(pkg, os.R_OK):
                if not self.options.tolerant:
                    self.die(-1, "Could not read file %s" % pkg)
                self.warn(-1, "Could not read file %s" % pkg)
                continue
            try:
                a_pkg = package_from_filename(pkg)
                a_pkg.read_header()
                a_pkg.payload_checksum()
            except InvalidPackageError:
                if not self.options.tolerant:
                    self.die(-1, "ERROR: %s: This file doesn't appear to be a package" % pkg)
                self.warn(2, "ERROR: %s: This file doesn't appear to be a package" % pkg)
                continue
            except IOError:
                if not self.options.tolerant:
                    self.die(-1, "ERROR: %s: No such file or directory available" % pkg)
                self.warn(2, "ERROR: %s: No such file or directory available" % pkg)
                continue

            digest_hash[pkg_key] = (a_pkg.checksum_type, a_pkg.checksum)
            a_pkg.input_stream.close()

            for tag in ('name', 'version', 'release', 'epoch', 'arch'):
                val = a_pkg.header[tag]
                if val is None:
                    val = ''
                pkg_info[tag] = val
            # b195903:the arch for srpms should be obtained by is_source check
            # instead of checking arch in header
            if a_pkg.header.is_source:
                if not self.options.source:
                    self.die(-1, "ERROR: Trying to Push src rpm, Please re-try with --source.")
                if RPMTAG_NOSOURCE in a_pkg.header.keys():
                    pkg_info['arch'] = 'nosrc'
                else:
                    pkg_info['arch'] = 'src'
            pkg_info['checksum_type'] = a_pkg.checksum_type
            pkg_info['checksum'] = a_pkg.checksum
            pkg_hash[pkg_key] = pkg_info

        if self.options.nullorg:
            # to satisfy xmlrpc from None values.
            orgid = 'null'
        else:
            orgid = ''

        info = {
            'packages': pkg_hash,
            'channels': self.channels,
            'org_id': orgid,
            'force': self.options.force or 0
        }
        # rpc call to get checksum info for all the packages to be uploaded
        if not self.options.source:
            # computing checksum and other info is expensive process and session
            # could have expired.Make sure its re-authenticated.
            self.authenticate()
            if uploadLib.exists_getPackageChecksumBySession(self.server):
                checksum_data = uploadLib.getPackageChecksumBySession(self.server,
                                                                      self.session.getSessionString(), info)
            else:
                # old server only md5 capable
                checksum_data = uploadLib.getPackageMD5sumBySession(self.server,
                                                                    self.session.getSessionString(), info)
        else:
            # computing checksum and other info is expensive process and session
            # could have expired.Make sure its re-authenticated.
            self.authenticate()
            if uploadLib.exists_getPackageChecksumBySession(self.server):
                checksum_data = uploadLib.getSourcePackageChecksumBySession(self.server,
                                                                            self.session.getSessionString(), info)
            else:
                # old server only md5 capable
                checksum_data = uploadLib.getSourcePackageMD5sumBySession(self.server,
                                                                          self.session.getSessionString(), info)

        return (checksum_data, pkg_hash, digest_hash)

    def package(self, package, fileChecksumType, fileChecksum):
        self.warn(1, "Uploading package %s" % package)
        if not os.access(package, os.R_OK):
            self.die(-1, "Could not read file %s" % package)

        try:
            h = uploadLib.get_header(package, source=self.options.source)
        except uploadLib.UploadError:
            e = sys.exc_info()[1]
            # GS: MALFORMED PACKAGE
            print("Unable to load package", package, ":", e)
            return None

        if hasattr(h, 'packaging'):
            packaging = h.packaging
        else:
            packaging = 'rpm'

        if packaging == 'rpm' and self.options.nosig is None and not h.is_signed():
            # pkilambi:bug#173886:force exit to check for sig if --nosig
            raise uploadLib.UploadError("ERROR: %s: unsigned rpm (use --nosig to force)" % package)

        try:
            ret = self._push_package_v2(package, fileChecksumType, fileChecksum)
        except uploadLib.UploadError:
            e = sys.exc_info()[1]
            ret, diff_level, pdict = e.args[:3]
            severities = {
                1: 'path changed',
                2: 'package resigned',
                3: 'differing build times or hosts',
                4: 'package recompiled',
            }
            if diff_level in severities:
                strmsg = \
                    "Error: Package with same name already exists on " + \
                    "server but contents differ ("                     + \
                    severities[diff_level]                             + \
                    ").  Use --force or remove old package before "    + \
                    "uploading the newer version."
            else:
                strmsg = "Error: severity %s" % diff_level
            self.warn(-1, "Uploading failed for %s\n%s\n\tDiff: %s" %
                      (package, strmsg, pdict['diff']['diff']))
            if diff_level != 1:
                # This will prevent us from annoyingly retrying when there is
                # no reason to.
                raise uploadLib.UploadError()
            return ret

        return ret

    def _push_package_v2(self, package, fileChecksumType, fileChecksum):
        self.warn(1, "Using POST request")
        pu = rhnpush_v2.PackageUpload(self.url_v2, self.options.proxy)

        pu.set_session(self.session.getSessionString())
        pu.set_force(self.options.force)
        pu.set_null_org(self.options.nullorg)
        pu.set_timeout(self.options.timeout)

        status, msgstr = pu.upload(package, fileChecksumType, fileChecksum)

        ret = {}
        for tag in ('name', 'version', 'release', 'epoch', 'arch'):
            val = getattr(pu, "package_%s" % tag)
            if val is None:
                val = ''
            ret[tag] = val

        ret['checksum_type'] = fileChecksumType
        ret['checksum'] = fileChecksum
        if status == 400:
            # Bad request - something bad happened
            try:
                data = rpclib.xmlrpclib.loads(msgstr)
            except:
                # Raise the exception instead of silently dying
                raise_with_tb(uploadLib.UploadError("Error pushing %s: %s (%s)" %
                                                    (package, msgstr, status)), sys.exc_info()[2])
            (diff_dict, ), methodname = data
            del methodname
            diff_level = diff_dict['level']
            pdict = diff_dict['diff']
            raise uploadLib.UploadError(ret, diff_level, pdict)

        if status == 403:
            # auth expired raise an exception to grab one
            raise AuthenticationRequired()

        if status != 200:
            self.die(1, "Error pushing %s: %s (%s)" % (package, msgstr, status))

        return ret


class AuthenticationRequired(Exception):
    pass

if __name__ == '__main__':
    # test code
    sys.exit(main() or 0)
07070100000014000081B40000000000000000000000015DDE99FC00000912000000000000000000000000000000000000001700000000mgr-push/rhnpush_v2.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.
#

#
# Package uploading tool
#

import base64

from rhnpush import connection


class PackageUpload(connection.PackageUpload):
    user_agent = "rhnpush"

    def set_auth(self, username, password):
        auth_vals = self.encode_values([username, password])
        self.headers["%s-%s" % (self.header_prefix, "Auth")] = auth_vals

    def set_session(self, session_string):
        self.headers["%s-%s" % (self.header_prefix, "Auth-Session")] = session_string

    def set_force(self, force):
        if force:
            force = 1
        else:
            force = 0
        self.headers["%s-%s" % (self.header_prefix, "Force")] = str(force)

    def set_null_org(self, null_org):
        if null_org:
            self.headers["%s-%s" % (self.header_prefix, "Null-Org")] = "1"

    def set_timeout(self, timeout):
        self.connection.set_timeout(timeout)

    # Encodes an array of variables into Base64 (column-separated)
    @staticmethod
    def encode_values(arr):
        val = ':'.join([x.strip() for x in map(base64.encodestring, arr)])
        # Get rid of the newlines
        val = val.replace('\n', '')
        # And split the result into lines of fixed size
        line_len = 80
        result = []
        start = 0
        while 1:
            if start >= len(val):
                break
            result.append(val[start:start + line_len])
            start = start + line_len
        return result


class PingPackageUpload(connection.PackageUpload):
    user_agent = "rhnpush-ping"

    def ping(self):
        self.send_http("GET")
        # return the header info as well to check for capabilities.
        return self._response.status, self._response.reason, self._response.msg
07070100000015000081B40000000000000000000000015DDE99FC0000060D000000000000000000000000000000000000001300000000mgr-push/rhnpushrc#This is the default config file for rhnpush. Place it in your home directory.
[rhnpush]

#Only push the packages that are newer than those on the server
newest			=	0

#Briefly describe the options
usage			=	0

#Upload only the header(s)
header			=	0

#Only print the packages that would otherwise have been pushed
test			=	0

#Use the null org id
nullorg			=	0

#Upload source package information
source			=	0

#Read the package names from stdin
stdin			=	0

#Increase verbosity
verbose			=	0

#Force the package upload (overwrites if already uploaded)
force			=	0

#Push unsigned packages
nosig			=	0

#Only list the specified channels
list			=	0

#Exclude the packages that match this glob expression
exclude			=


files			=

#Organization ID
orgid			=

#Relative directory to associate with the package
reldir			=

#Process this number of headers per call
count			=

#Process packages from this directory
dir				=

#Push to this server (http[s]://<hostname>/APP)
server 			=	https://localhost/APP

#Manage this channel(s)
channel			=

#Determines how long the cache will last, in seconds
cache_lifetime 	=	600

#Reset the cache
new_cache		=	0

#Run the extended version of the test
extended_test   =   0

#Disable the session-token support
no_session_caching  =   0

#Have rhnpush be tolerant of errors when pushing a large number of packages.
tolerant        =   0

#The CA cert used to verify the ssl server
ca_chain        =   /usr/share/rhn/RHN-ORG-TRUSTED-SSL-CERT

#Default connection timeout, (no value for default)
timeout         = 300
07070100000016000081B40000000000000000000000015DDE99FC000015AF000000000000000000000000000000000000001400000000mgr-push/rpm2mpm.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 sys
import time

# pylint: disable=F0401
from uyuni.common import rhn_rpm, rhn_mpm
from uyuni.common.usix import ListType, TupleType


def main():
    packages = sys.argv[1:]
    if not packages:
        return

    for pkgfile in packages:
        # Try to open the package as a patch first
        with open(pkgfile) as f:
            header = rhn_rpm.get_package_header(file_obj=f)
            p = rpm_to_mpm(header, f)
            dest_filename = _compute_filename(p.header)
            print("Writing out the package to %s" % dest_filename)
            with open(dest_filename, "w+") as dest_file:
                p.write(dest_file)


def _compute_filename(hdr):
    return '%s-%s.%s.mpm' % (hdr['name'], hdr['version'], hdr['arch'])


def rpm_to_mpm(header, file_stream):
    tag_map = {
        'package_group': 'group',
        'rpm_version': 'rpmversion',
        'payload_size': 'archivesize',
        'payload_format': 'payloadformat',
        'build_host': 'buildhost',
        'build_time': 'buildtime',
        'source_rpm': 'sourcerpm',
    }

    tags = [
        'name',
        'epoch',
        'version',
        'release',
        'arch',
        'description',
        'summary',
        'license',
        'package_group',
        'rpm_version',
        'payload_size',
        'payload_format',
        'build_host',
        'build_time',
        'cookie',
        'vendor',
        'source_rpm',
        'sigmd5',
        'sigpgp',
        'siggpg',
        'sigsize',
    ]

    result = {}
    for t in tags:
        tt = tag_map.get(t, t)
        result[t] = header[tt]

    # Add files
    result['files'] = _extract_files(header)

    # Dependency
    result['provides'] = _extract_rpm_requires(header)
    result['requires'] = _extract_rpm_provides(header)
    result['conflicts'] = _extract_rpm_conflicts(header)
    result['obsoletes'] = _extract_rpm_obsoletes(header)

    result['changelog'] = _extract_rpm_changelog(header)

    # md5sum, package_size
    file_stream.seek(0, 2)
    file_size = file_stream.tell()
    result['package_size'] = file_size

    is_source = 0
    if header.is_source:
        is_source = 1
    result['is_source'] = is_source

    result['package_type'] = 'rpm'

    h = rhn_mpm.MPM_Header(result)
    p = rhn_mpm.MPM_Package()
    p.header = h
    p.payload_stream = file_stream

    return p


def _extract_files(header):
    tag_maps = {
        'name': 'filenames',
        'device': 'filedevices',
        'inode': 'fileinodes',
        'file_mode': 'filemodes',
        'username': 'fileusername',
        'groupname': 'filegroupname',
        'rdev': 'filerdevs',
        'file_size': 'filesizes',
        'mtime': 'filemtimes',
        'md5': 'filemd5s',
        'linkto': 'filelinktos',
        'flags': 'fileflags',
        'verifyflags': 'fileverifyflags',
        'lang': 'filelangs',
    }
    files = _extract_array_fields(header, tag_maps)
    # Munge the mtime
    for f in files:
        f['mtime'] = gmtime(f['mtime'])
    return files


def _extract_rpm_provides(header):
    tag_maps = {
        'name': 'provides',
        'version': 'provideversion',
        'flags': 'provideflags',
    }
    return _extract_array_fields(header, tag_maps)


def _extract_rpm_requires(header):
    tag_maps = {
        'name': 'requirename',
        'version': 'requireversion',
        'flags': 'requireflags',
    }
    return _extract_array_fields(header, tag_maps)


def _extract_rpm_conflicts(header):
    tag_maps = {
        'name': 'conflictname',
        'version': 'conflictversion',
        'flags': 'conflictflags',
    }
    return _extract_array_fields(header, tag_maps)


def _extract_rpm_obsoletes(header):
    tag_maps = {
        'name': 'obsoletename',
        'version': 'obsoleteversion',
        'flags': 'obsoleteflags',
    }
    return _extract_array_fields(header, tag_maps)


def _extract_rpm_changelog(header):
    tag_maps = {
        'name': 'changelogname',
        'text': 'changelogtext',
        'time': 'changelogtime',
    }
    cl = _extract_array_fields(header, tag_maps)
    # Munge the changelog time
    for c in cl:
        c['time'] = gmtime(c['time'])
    return cl


def _extract_array_fields(header, tag_maps):
    # First determine the number of entries
    key = list(tag_maps.keys())[0]
    rpmtag = tag_maps.get(key)
    arr = header[rpmtag]
    if arr is None:
        # nothing to do
        return []
    count = len(arr)

    result = []
    for i in range(count):
        tag_dict = {}
        for key, rpmtag in tag_maps.items():
            arr = header[rpmtag]
            if not isinstance(arr, (ListType, TupleType)):
                arr = [arr]
            tag_dict[key] = arr[i]
        result.append(tag_dict)
    return result


def gmtime(timestamp):
    ttuple = time.gmtime(timestamp)
    return "%d-%02d-%02d %02d:%02d:%02d" % ttuple[:6]

if __name__ == '__main__':
    sys.exit(main() or 0)
07070100000017000041FD0000000000000000000000015DDE99FC00000000000000000000000000000000000000000000000E00000000mgr-push/test07070100000018000081B40000000000000000000000015DDE99FC000010E7000000000000000000000000000000000000002200000000mgr-push/test/testRhnpushCache.py#
# Copyright (c) 2008--2015 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 unittest
import rhnpush_cache
import time


class UserInfoTestCase(unittest.TestCase):

    def setUp(self):
        self.userinfo = rhnpush_cache.UserInfo(5, username='wregglej', password='password')

    def tearDown(self):
        self.userinfo = None

    def testCheckCacheTrue(self):
        assert self.userinfo.checkCache() == True

    def testCheckCacheFalse(self):
        time.sleep(7)
        assert self.userinfo.checkCache() == False

    def testSetUsernamePassword(self):
        self.userinfo = rhnpush_cache.UserInfo(5, username='wregglej', password='password')
        self.userinfo.setUsernamePassword('aaaa', 'bbbb')
        assert self.userinfo.username != 'wregglej' and self.userinfo.password != 'password'

    def testSetUsernamePassword3(self):
        self.userinfo = rhnpush_cache.UserInfo(5, username='wregglej', password='password')
        self.userinfo.setUsernamePassword('aaaa', 'bbbb')
        assert self.userinfo.username == 'aaaa' and self.userinfo.password == 'bbbb'

    def testGetUsernamePassword(self):
        self.userinfo = rhnpush_cache.UserInfo(5, username='wregglej', password='password')
        assert self.userinfo.username == 'wregglej' and self.userinfo.password == 'password'

    def testIsFresh(self):
        self.userinfo = rhnpush_cache.UserInfo(5, username='wregglej', password='password')
        assert self.userinfo.isFresh() == True

    def testIsntFresh(self):
        time.sleep(6)
        assert self.userinfo.isFresh() == False

    def testSetCacheLifetime(self):
        self.userinfo = rhnpush_cache.UserInfo(5, username='wregglej', password='password')
        self.userinfo.setCacheLifetime(6667)
        assert self.userinfo.cache_lifetime != 5 and self.userinfo.cache_lifetime == 6667

    def testGetTimeLeft(self):
        self.userinfo = rhnpush_cache.UserInfo(10, username='wregglej', password='password')
        time.sleep(2.0)
        assert self.userinfo.getTimeLeft() >= 7.98 and self.userinfo.getTimeLeft() <= 8.002


class CacheManagerTestCase(unittest.TestCase):

    def setUp(self):
        self.cache = rhnpush_cache.CacheManager(5)

    def tearDown(self):
        self.cache = None

    def testIsFresh(self):
        self.cache = rhnpush_cache.CacheManager(5)
        self.cache.setUsernamePassword('a', 'b')
        assert self.cache.isFresh() == True

    def testIsntFresh(self):
        self.cache = rhnpush_cache.CacheManager(5)
        time.sleep(7)
        assert self.cache.isFresh() == False

    def testSetUsernamePassword(self):
        self.cache = rhnpush_cache.CacheManager(5)
        self.cache.setUsernamePassword('wregglej', 'password')
        # print self.cache.cache.username
        assert self.cache.cache.username == 'wregglej' and self.cache.cache.password == 'password'

    def testSetUsernamePassword2(self):
        self.cache = rhnpush_cache.CacheManager(5)
        self.cache.setUsernamePassword('wregglej', 'password')
        self.cache.setUsernamePassword('aaaa', 'bbbb')
        assert self.cache.cache.username == 'aaaa' and self.cache.cache.password == 'bbbb'

    def testGetUsernamePassword(self):
        self.cache = rhnpush_cache.CacheManager(5)
        self.cache.setUsernamePassword('wregglej', 'password')
        u, p = self.cache.getUsernamePassword()
        assert u == 'wregglej' and p == 'password'

    def testSetCacheLifetime(self):
        self.cache = rhnpush_cache.CacheManager(5)
        self.cache.setCacheLifetime(10)
        assert self.cache.cache.cache_lifetime == 10 and self.cache.cache.cache_lifetime != 5

    def testWriteCache(self):
        pass

    def testGetTimeLeft(self):
        pass

if __name__ == "__main__":
    unittest.main()
07070100000019000081B40000000000000000000000015DDE99FC000007E0000000000000000000000000000000000000002300000000mgr-push/test/testRhnpushConfig.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 rhnpush_config
import unittest


# pylint: disable=W0212,E1101,R0904
class RhnConfigTestCase(unittest.TestCase):

    def setUp(self):
        self.userconfig = rhnpush_config.rhnpushConfigParser('.rhnpushrc')
        self.defaultconfig = rhnpush_config.rhnpushConfigParser('/etc/sysconfig/rhn/rhnpushrc')

    def tearDown(self):
        self.userconfig = None
        self.defaultconfig = None

    def testReadConfigFiles(self):
        self.userconfig._read_config_files()
        self.defaultconfig._read_config_files()
        assert self.userconfig.settings != None and self.defaultconfig.settings != None

    def testGetOption(self):
        a = self.userconfig.get_option('usage')
        b = self.defaultconfig.get_option('usage')
        assert a != None and b != None and a == '0' and b == '0'

    def testKeys(self):
        a = list(self.userconfig.keys())
        b = list(self.defaultconfig.keys())
        assert a != None and b != None

    def test_keys(self):
        a = self.userconfig._keys()
        b = self.defaultconfig._keys()
        assert a != None and b != None

    def testGetItem(self):
        pass

    def testAddConfigAsAttr(self):
        self.userconfig._add_config_as_attr()
        self.userconfig._add_config_as_attr()
        assert self.userconfig.usage != None and self.defaultconfig.usage != None

if __name__ == "__main__":
    unittest.main()
0707010000001A000081B40000000000000000000000015DDE99FC000007C9000000000000000000000000000000000000001B00000000mgr-push/test/testUtils.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 unittest
import utils


class TestObj1:

    def __init__(self):
        self.a = 1
        self.b = 2
        self.c = 3
        self._d = '1'
        self._e = '2'

    @staticmethod
    def _private_function():
        print("This is privatei to TestObj1 instances")

    @staticmethod
    def public_function():
        print("This is public and belongs to TestObj1")


class TestObj2:

    def __init__(self):
        self.a = 4
        self.b = 5
        self.c = 6
        self._d = '4'
        self._e = '5'
        self.f = 'aaa'

    @staticmethod
    def _private_function():
        print("This is private to TestObj2 instances")

    @staticmethod
    def public_function():
        print("This is public and belongs to TestObj2")


class UtilsTestCase(unittest.TestCase):

    def setUp(self):
        self.obj1 = TestObj1()
        self.obj2 = TestObj2()

    def tearDown(self):
        self.obj1 = None
        self.obj2 = None

    # pylint: disable=W0212
    def testMakeCommonAttrEqual(self):
        self.obj1, self.obj2 = utils.make_common_attr_equal(self.obj1, self.obj2)
        assert (self.obj1._d == '1'
                and self.obj2._d == '4'
                and self.obj1.a == 4
                and self.obj1.b == 5
                and self.obj1.c == 6
                and self.obj2.f == 'aaa')

if __name__ == "__main__":
    unittest.main()
0707010000001B000081B40000000000000000000000015DDE99FC00000A9F000000000000000000000000000000000000001E00000000mgr-push/test/test_archive.py#
# Copyright (c) 2008--2015 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 unittest
import zipfile

# test import
import archive

# globals ----------------------------------------------------------------

TEST_ARCHIVE = "/tmp/test_archive.zip"
TEST_DIR = "./test_archive/foo/bar"
TEST_FILE = "test_file"
TEST_FILE_PATH = os.path.join(TEST_DIR, TEST_FILE)

TEST_CONTENTS = """
ONE: foo
TWO: you
THREE: too
FOUR: foo

"""

# test case --------------------------------------------------------------


class ArchiveTest(unittest.TestCase):

    def setUp(self):
        if not os.path.isdir(TEST_DIR):
            os.makedirs(TEST_DIR)

        fd = open(TEST_FILE_PATH, 'w')
        fd.write(TEST_CONTENTS)
        fd.close()

        fzip = zipfile.ZipFile(TEST_ARCHIVE, 'w')
        fzip.write(TEST_FILE_PATH)
        fzip.close()

    def tearDown(self):
        if os.path.isfile(TEST_FILE_PATH):
            os.unlink(TEST_FILE_PATH)

        if os.path.isdir(TEST_DIR):
            os.removedirs(TEST_DIR)

        if os.path.isfile(TEST_ARCHIVE):
            os.unlink(TEST_ARCHIVE)

    # test methods -------------------------------------------------------

    @staticmethod
    def testInstantiation():
        "test the instantiation of an archive parser object"
        p = archive.get_archive_parser(TEST_ARCHIVE)
        assert isinstance(p, archive.ArchiveParser)

    @staticmethod
    def testFind():
        "test the ability of the parser to find a file in the archive"
        p = archive.get_archive_parser(TEST_ARCHIVE)
        assert p.contains(TEST_FILE)

    @staticmethod
    def testFindPath():
        "test the ability of the parser to find a subpath in the archive"
        p = archive.get_archive_parser(TEST_ARCHIVE)
        assert p.contains("foo/bar/" + TEST_FILE)

    @staticmethod
    def testRead():
        "test the ability of the parser to read a file in the archive"
        p = archive.get_archive_parser(TEST_ARCHIVE)
        contents = p.read(TEST_FILE)
        assert contents == TEST_CONTENTS

# run the tests ----------------------------------------------------------

if __name__ == "__main__":
    unittest.main()
0707010000001C000081B40000000000000000000000015DDE99FC0000687F000000000000000000000000000000000000001600000000mgr-push/uploadLib.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.
#

# system imports
import os
import sys
import fnmatch
import getpass

# imports
# pylint: disable=F0401,E0611

# exceptions
# pylint: disable=W0702,W0703

import inspect
from uyuni.common import rhn_mpm
from uyuni.common.rhn_pkg import package_from_filename, get_package_header
from uyuni.common.usix import raise_with_tb
from up2date_client import rhnserver
from rhn.i18n import sstr
from rhnpush import rhnpush_cache

if sys.version_info[0] == 3:
    import xmlrpc.client as xmlrpclib
else:
    import xmlrpclib

try:
    from rhn import rpclib # pylint: disable=C0412
    Binary = rpclib.xmlrpclib.Binary
    Output = rpclib.transports.Output
except ImportError:
    # old-style xmlrpclib library
    rpclib = xmlrpclib
    Binary = rpclib.Binary
    # pylint: disable=F0401
    import cgiwrap
    Output = cgiwrap.Output

# Buffer size we use for copying
BUFFER_SIZE = 65536
HEADERS_PER_CALL = 25

# Exception class


class UploadError(Exception):
    pass


class ServerFault(Exception):

    def __init__(self, faultCode=None, faultString="", faultExplanation=""):
        Exception.__init__(self)
        self.faultCode = faultCode
        self.faultString = faultString
        self.faultExplanation = faultExplanation


class UploadClass:

    """Functionality for an uploading tool
    """

    def __init__(self, options, files=None):
        self.options = options
        self.username = None
        self.password = None
        self.proxy = None
        self.proxyUsername = None
        self.proxyPassword = None
        self.ca_chain = None
        self.force = None
        self.files = files or []
        self.new_sat = None
        self.url = None
        self.channels = None
        self.count = None
        self.server = None
        self.session = None
        self.orgId = None
        self.relativeDir = None
        self.use_session = True
        self.use_checksum_paths = False

    def warn(self, verbose, *args):
        if self.options.verbose >= verbose:
            ReportError(*args)

    @staticmethod
    def die(errcode, *args):
        ReportError(*args)
        # pkilambi:bug#176358:this should exit with error code
        sys.exit(errcode)

    def setURL(self):
        # Redefine this in derived classes
        self.url = None

    def setUsernamePassword(self):
        # Use the stored values, if available
        username = self.username or self.options.username
        password = self.password or self.options.password
        self.username, self.password = getUsernamePassword(username, password)

    def setProxyUsernamePassword(self):
        self.proxyUsername = None
        self.proxyPassword = None

    def setCAchain(self):
        self.ca_chain = self.options.ca_chain

    def setProxy(self):
        if self.options.proxy is None or self.options.proxy == '':
            self.proxy = None
        else:
            self.proxy = "http://%s" % self.options.proxy

    def setForce(self):
        self.force = None

    def setServer(self):
        # set the proxy
        self.setProxy()

        if self.proxy is None:
            self.warn(1, "Connecting to %s" % self.url)
        else:
            self.warn(1, "Connecting to %s (via proxy '%s')" % (self.url, self.proxy))

        # set the CA chain
        self.setCAchain()
        # set the proxy username and password
        self.setProxyUsernamePassword()
        self.server = getServer(self.url, self.proxy, self.proxyUsername,
                                self.proxyPassword, self.ca_chain)
        # Compress the output, just to be fast
        self.server.set_transport_flags(
            transfer=Output.TRANSFER_BINARY,
            encoding=Output.ENCODE_GZIP)

    def setChannels(self):
        if not self.options.channel:
            self.die(-1, "No channel was specified")
        self.channels = self.options.channel
        self.warn(1, "Channels: %s" % ' '.join(self.channels))

    setNoChannels = setChannels

    def setOrg(self):
        self.orgId = -1

    def setCount(self):
        if not self.options.count:
            self.count = HEADERS_PER_CALL
        else:
            self.count = self.options.count

    def setRelativeDir(self):
        self.relativeDir = None

    def directory(self):
        self.warn(2, "Uploading files from directory", self.options.dir)

        for filename in listdir(self.options.dir):
            # only add packages
            if filename[-3:] in ("rpm", "mpm"):
                self.files.append(filename)

    def filter_excludes(self):
        if not self.options.exclude:
            return self
        for f in self.files[:]:
            bf = os.path.basename(f)
            for pattern in self.options.exclude:
                if fnmatch.fnmatch(bf, pattern):
                    self.warn(1, "Ignoring %s" % f)
                    self.files.remove(f)
        return self

    def readStdin(self):
        self.warn(1, "Reading package names from stdin")
        self.files = self.files + readStdin()

    def _listChannelSource(self):
        if self.use_session:
            return listChannelSourceBySession(self.server,
                                              self.session.getSessionString(),
                                              self.channels)

        return listChannelSource(self.server,
                                 self.username, self.password,
                                 self.channels)

    def _listChannel(self):
        if self.use_session:
            if self.use_checksum_paths:
                return listChannelChecksumBySession(self.server,
                                                    self.session.getSessionString(), self.channels)

            return listChannelBySession(self.server,
                                        self.session.getSessionString(),
                                        self.channels)

        if self.use_checksum_paths:
            return listChannelChecksum(self.server,
                                       self.username, self.password,
                                       self.channels)

        return listChannel(self.server,
                           self.username, self.password,
                           self.channels)

    def list(self):
        # set the URL
        self.setURL()
        # set the channels
        self.setChannels()
        # set the server
        self.setServer()

        self.authenticate()

        if self.options.source:
            channel_list = self._listChannelSource()
        else:
            # List the channel's contents
            channel_list = self._listChannel()

        for p in channel_list:
            print(p[:6])

    def newest(self):
        # set the URL
        self.setURL()
        # set the channels
        self.setChannels()
        # set the server
        self.setServer()

        self.authenticate()

        sources = self.options.source

        if sources:
            return self.get_missing_source_packages()

        return self.get_newest_binary_packages()

    def get_newest_binary_packages(self):
        # Loop through the args and only keep the newest ones
        localPackagesHash = {}
        for filename in self.files:
            nvrea = self._processFile(filename, nosig=1)['nvrea']
            name = nvrea[0]
            if name not in localPackagesHash:
                localPackagesHash[name] = {nvrea: filename}
                continue

            same_names_hash = localPackagesHash[name]
            # Already saw this name
            if nvrea in same_names_hash:
                # Already seen this nvrea
                continue
            skip_rpm = 0
            for local_nvrea in same_names_hash.keys():
                # XXX is_mpm should be set accordingly
                ret = packageCompare(local_nvrea, nvrea,
                                     is_mpm=0)
                if ret == 0 and local_nvrea[4] == nvrea[4]:
                    # Weird case, we've already compared the two
                    skip_rpm = 1
                    break

                if ret > 0:
                    # nvrea is older than local_nvrea
                    skip_rpm = 1
                    break

                if ret < 0:
                    # nvrea is newer than local_nvrea
                    del same_names_hash[local_nvrea]

                # Different arches - go on

            if skip_rpm:
                # Older
                continue

            same_names_hash[nvrea] = filename

        # Now get the list from the server
        pkglist = self._listChannel()

        for p in pkglist:
            name = p[0]
            if name not in localPackagesHash:
                # Not in the local list
                continue
            same_names_hash = localPackagesHash[name]
            remote_nvrea = tuple(p[:5])
            if remote_nvrea in same_names_hash:
                # The same package is already uploaded
                del same_names_hash[remote_nvrea]
                continue

            for local_nvrea in list(same_names_hash.keys()):
                # XXX is_mpm sould be set accordingly
                ret = packageCompare(local_nvrea, remote_nvrea,
                                     is_mpm=0)
                if ret < 0:
                    # The remote package is newer than the local one
                    del same_names_hash[local_nvrea]
                    continue
                if ret == 0 and local_nvrea[4] == remote_nvrea[4]:
                    # Same arch
                    del same_names_hash[local_nvrea]
                    continue
                # This means local is newer

        # Return the list of files to push
        l = []
        for fhash in localPackagesHash.values():
            for filename in fhash.values():
                l.append(filename)
        l.sort()
        self.files = l

    def _listMissingSourcePackages(self):
        if self.use_session:
            return listMissingSourcePackagesBySession(self.server,
                                                      self.session.getSessionString(), self.channels)

        return listMissingSourcePackages(self.server,
                                         self.username, self.password, self.channels)

    def get_missing_source_packages(self):
        localPackagesHash = {}
        for filename in self.files:
            localPackagesHash[os.path.basename(filename)] = filename

        # Now get the list from the server
        pkglist = self._listMissingSourcePackages()

        to_push = []
        for pkg in pkglist:
            pkg_name, _pkg_channel = pkg[:2]
            if pkg_name not in localPackagesHash:
                # We don't have it
                continue
            to_push.append(localPackagesHash[pkg_name])

        to_push.sort()
        self.files = to_push
        return self.files

    def test(self):
        # Test only
        for p in self.files:
            print(p)

    def _get_files(self):
        return self.files[:]

    def _uploadSourcePackageInfo(self, info):
        if self.use_session:
            return call(self.server.packages.uploadSourcePackageInfoBySession,
                        self.session.getSessionString(), info)

        return call(self.server.packages.uploadSourcePackageInfo,
                    self.username, self.password, info)

    def _uploadPackageInfo(self, info):
        if self.use_session:
            return call(self.server.packages.uploadPackageInfoBySession,
                        self.session.getSessionString(), info)

        return call(self.server.packages.uploadPackageInfo,
                    self.username, self.password, info)

    def uploadHeaders(self):
        # Set the forcing factor
        self.setForce()
        # Relative directory
        self.setRelativeDir()
        # Set the count
        self.setCount()
        # set the org
        self.setOrg()
        # set the URL
        self.setURL()
        # set the channels
        self.setNoChannels()

        # set the server
        self.setServer()

        self.authenticate()

        source = self.options.source
        file_list = self._get_files()

        while file_list:
            chunk = file_list[:self.count]
            del file_list[:self.count]
            uploadedPackages, headersList = self._processBatch(chunk,
                                                               relativeDir=self.relativeDir, source=self.options.source,
                                                               verbose=self.options.verbose, nosig=self.options.nosig)

            if not headersList:
                # Nothing to do here...
                continue

            # Send the big hash
            info = {'packages': headersList}
            if self.orgId > 0 or self.orgId == '':
                info['orgId'] = self.orgId

            if self.force:
                info['force'] = self.force

            if self.channels:
                info['channels'] = self.channels

            # Some feedback
            if self.options.verbose:
                ReportError("Uploading batch:")
                for p in list(uploadedPackages.values())[0]:
                    ReportError("\t\t%s" % p)

            if source:
                ret = self._uploadSourcePackageInfo(info)
            else:
                ret = self._uploadPackageInfo(info)

            if ret is None:
                self.die(-1, "Upload attempt failed")

            # Append the package information
            alreadyUploaded, newPackages = ret
            pkglists = (alreadyUploaded, newPackages)

            for idx, item in enumerate(pkglists):
                for p in item:
                    key = tuple(p[:5])
                    if key not in uploadedPackages:
                        # XXX Hmm
                        self.warn(1, "XXX XXX %s" % str(p))
                    filename, checksum = uploadedPackages[key]
                    # Some debugging
                    if self.options.verbose:
                        if idx == 0:
                            pattern = "Already uploaded: %s"
                        else:
                            pattern = "Uploaded: %s"
                        print(pattern % filename)
                    # Per-package post actions
                    # For backwards-compatibility with old spacewalk-proxy
                    try:
                        self.processPackage(p, filename, checksum)
                    except TypeError:
                        self.processPackage(p, filename)

    def processPackage(self, package, filename, checksum=None):
        pass

    def checkSession(self, session):
        return call(self.server.packages.check_session, session)

    def readSession(self):
        # pylint: disable=W0703
        try:
            self.session = rhnpush_cache.RHNPushSession()
            self.session.readSession()
        except Exception:
            self.session = None

    def writeSession(self, session):
        if self.session:
            self.session.setSessionString(session)
        else:
            self.session = rhnpush_cache.RHNPushSession()
            self.session.setSessionString(session)

        if not self.options.no_session_caching:
            self.session.writeSession()

    def authenticate(self):
        # Only use the session token stuff if we're talking to a sat that supports session-token authentication.
        self.readSession()
        if self.session and not self.options.new_cache and self.options.username == self.username:
            chksession = self.checkSession(self.session.getSessionString())
            if chksession:
                return
        self.setUsernamePassword()
        sessstr = call(self.server.packages.login, self.username, self.password)
        self.writeSession(sessstr)

        # set whether we should use checksum paths or not (if upstream supports
        # it we should).
        self.use_checksum_paths = hasChannelChecksumCapability(self.server)

    @staticmethod
    def _processFile(filename, relativeDir=None, source=None, nosig=None):
        """ Processes a file
            Returns a hash containing:
              header
              packageSize
              checksum
              relativePath
              nvrea
         """

        # Is this a file?
        if not os.access(filename, os.R_OK):
            raise UploadError("Could not stat the file %s" % filename)
        if not os.path.isfile(filename):
            raise UploadError("%s is not a file" % filename)

        # Size
        size = os.path.getsize(filename)

        try:
            a_pkg = package_from_filename(filename)
            a_pkg.read_header()
            a_pkg.payload_checksum()
            assert a_pkg.header
        except:
            raise_with_tb(UploadError("%s is not a valid package" % filename), sys.exc_info()[2])

        if nosig is None and not a_pkg.header.is_signed():
            raise UploadError("ERROR: %s: unsigned rpm (use --nosig to force)"
                              % filename)

        # Get the name, version, release, epoch, arch
        lh = []
        for k in ['name', 'version', 'release', 'epoch']:
            if k == 'epoch' and not a_pkg.header[k]:
            # Fix the epoch
                lh.append(sstr(""))
            else:
                lh.append(sstr(a_pkg.header[k]))

        if source:
            lh.append('src')
        else:
            lh.append(sstr(a_pkg.header['arch']))

        # Build the header hash to be sent
        info = {'header': Binary(a_pkg.header.unload()),
                'checksum_type': a_pkg.checksum_type,
                'checksum': a_pkg.checksum,
                'packageSize': size,
                'header_start': a_pkg.header_start,
                'header_end': a_pkg.header_end}
        if relativeDir:
            # Append the relative dir too
            info["relativePath"] = "%s/%s" % (relativeDir,
                                              os.path.basename(filename))
        info['nvrea'] = tuple(lh)
        return info

    def _processBatch(self, batch, relativeDir, source, verbose, nosig=None):
        sentPackages = {}
        headersList = []
        for filename in batch:
            if verbose:
                print("Uploading %s" % filename)
            info = self._processFile(filename, relativeDir=relativeDir, source=source,
                                     nosig=nosig)
            # Get nvrea
            nvrea = info['nvrea']
            del info['nvrea']

            sentPackages[nvrea] = (filename, info['checksum'])

            # Append the header to the list of headers to be sent out
            headersList.append(info)
        return sentPackages, headersList


def readStdin():
    # Reads the standard input lines and returns a list
    l = []
    while 1:
        line = sys.stdin.readline()
        if not line:
            break
        l.append(line.strip())
    return l


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 = open("/dev/tty", "w")
    tty.write("SUSE Manager username: ")
    tty.close()
    tty = open("/dev/tty", "r")

    while not 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 listdir(directory):
    directory = os.path.abspath(os.path.normpath(directory))
    if not os.access(directory, os.R_OK | os.X_OK):
        raise UploadError("Cannot read from directory %s" % directory)
    if not os.path.isdir(directory):
        raise UploadError("%s not a directory" % directory)
    # Build the package list
    packagesList = []
    for f in os.listdir(directory):
        packagesList.append("%s/%s" % (directory, f))
    return packagesList


def call(function, *params, **kwargs):
    # Wrapper function
    try:
        ret = function(*params)
    except xmlrpclib.Fault:
        e = sys.exc_info()[1]
        x = parseXMLRPCfault(e)
        if x.faultString:
            print(x.faultString)
        if x.faultExplanation:
            print(x.faultExplanation)
        sys.exit(-1)
    except xmlrpclib.ProtocolError:
        e = sys.exc_info()[1]
        if kwargs.get('raise_protocol_error'):
            raise
        print(e.errmsg)
        sys.exit(-1)

    return ret


def parseXMLRPCfault(fault):
    if not isinstance(fault, xmlrpclib.Fault):
        return None
    faultCode = fault.faultCode
    if faultCode and isinstance(faultCode, type(1)):
        faultCode = -faultCode
    return ServerFault(faultCode, "", fault.faultString)

# pylint: disable=C0103


def listChannel(server, username, password, channels):
    return call(server.packages.listChannel, channels, username, password)


def listChannelChecksum(server, username, password, channels):
    return call(server.packages.listChannelChecksum, channels, username,
                password)


def listChannelBySession(server, session_string, channels):
    return call(server.packages.listChannelBySession, channels, session_string)


def listChannelChecksumBySession(server, session_string, channels):
    return call(server.packages.listChannelChecksumBySession, channels,
                session_string)


def listChannelSource(server, username, password, channels):
    return call(server.packages.listChannelSource, channels, username, password)


def listChannelSourceBySession(server, session_string, channels):
    return call(server.packages.listChannelSourceBySession, channels, session_string)


def listMissingSourcePackages(server, username, password, channels):
    return call(server.packages.listMissingSourcePackages, channels, username, password)


def listMissingSourcePackagesBySession(server, session_string, channels):
    return call(server.packages.listMissingSourcePackagesBySession, channels, session_string)


def getPackageChecksumBySession(server, session_string, info):
    return call(server.packages.getPackageChecksumBySession, session_string, info)


def getSourcePackageChecksumBySession(server, session_string, info):
    return call(server.packages.getSourcePackageChecksumBySession, session_string, info)


def getSourcePackageChecksum(server, username, password, info):
    return call(server.packages.getSourcePackageChecksum, username, password, info)

# for backward compatibility with satellite <5.4.0


def getPackageMD5sumBySession(server, session_string, info):
    return call(server.packages.getPackageMD5sumBySession, session_string, info)


def getSourcePackageMD5sumBySession(server, session_string, info):
    return call(server.packages.getSourcePackageMD5sumBySession, session_string, info)


def getServer(uri, proxy=None, username=None, password=None, ca_chain=None):
    s = rpclib.Server(uri, proxy=proxy, username=username, password=password)
    if ca_chain:
        s.add_trusted_cert(ca_chain)
    return s

# pylint: disable=E1123
def hasChannelChecksumCapability(rpc_server):
    """ check whether server supports getPackageChecksumBySession function"""
    # pylint: disable=W1505
    if 'rpcServerOverride' in inspect.getargspec(rhnserver.RhnServer.__init__)[0]:
        server = rhnserver.RhnServer(rpcServerOverride=rpc_server)
    else:
        server = rhnserver.RhnServer()
        # pylint: disable=W0212
        server._server = rpc_server
    return server.capabilities.hasCapability('xmlrpc.packages.checksums')


def exists_getPackageChecksumBySession(rpc_server):
    """ check whether server supports getPackageChecksumBySession function"""
    # unfortunatelly we do not have capability for getPackageChecksumBySession function,
    # but extended_profile in version 2 has been created just 2 months before
    # getPackageChecksumBySession lets use it instead
    # pylint: disable=W1505
    if 'rpcServerOverride' in inspect.getargspec(rhnserver.RhnServer.__init__)[0]:
        server = rhnserver.RhnServer(rpcServerOverride=rpc_server)
    else:
        server = rhnserver.RhnServer()
        # pylint: disable=W0212
        server._server = rpc_server
    return server.capabilities.hasCapability('xmlrpc.packages.extended_profile', 2)

# compare two package [n,v,r,e] tuples


def packageCompare(pkg1, pkg2, is_mpm=None):
    if pkg1[0] != pkg2[0]:
        raise ValueError("You should only compare packages with the same name")
    packages = []
    for pkg in (pkg1, pkg2):
        e = pkg[3]
        if e == "":
            e = None
        elif e is not None:
            e = str(e)
        evr = (e, str(pkg[1]), str(pkg[2]))
        packages.append(evr)
    if is_mpm:
        func = rhn_mpm.labelCompare
    else:
        from uyuni.common import rhn_rpm
        func = rhn_rpm.labelCompare
    return func(packages[0], packages[1])


# returns a header from a package file on disk.
def get_header(filename, fildes=None, source=None):
    try:
        h = get_package_header(filename=filename, fd=fildes)
    except:
        raise_with_tb(UploadError("Package is invalid"), sys.exc_info()[2])

    # Verify that this is indeed a binary/source. xor magic
    # xor doesn't work with None values, so compare the negated values - the
    # results are identical
    if (not source) ^ (not h.is_source):
        raise UploadError("Unexpected RPM package type")
    return h


def ReportError(*args):
    sys.stderr.write(' '.join(map(str, args)) + "\n")
0707010000001D000081B40000000000000000000000015DDE99FC00000B4C000000000000000000000000000000000000001200000000mgr-push/utils.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 pwd
import sys

def get_home_dir():
    userid = os.getuid()
    info = pwd.getpwuid(userid)
    return info[5]


# If Object1 and Object2 have any common attributes, set the attribute in Object1
# to the value of the attribute in Object2. Does not make functions or variables starting with '_' equivalent.
def make_common_attr_equal(object1, object2):

    # Go through every attribute in object1
    for attr in object1.__dict__.keys():

        # Make sure that the attribute name doesn't begin with "_"
        if len(attr) < 1 or attr[0] == "_":
            continue

        # Make sure that object2 has the attribute as well. and that it's not equal to ''.
        if attr not in object2.__dict__ or object2.__dict__[attr] == '':
            continue

        # Make sure the attributes are the same type OR that the attribute in object1 is None.
        if isinstance(object1.__dict__[attr], (type(object2.__dict__[attr]), type(None))):
            if object1.__dict__[attr] != object2.__dict__[attr]:
                object1.__dict__[attr] = object2.__dict__[attr]
            else:
                continue
        else:
            continue

    return (object1, object2)


# Pylint is too stupid to understand subclasses of tuples apparently.
# This is just to make it shut up.
def tupleify_urlparse(urlparse_object):
    ret = []
    if hasattr(urlparse_object, 'scheme'):
        ret.append(urlparse_object.scheme)
        ret.append(urlparse_object.netloc)
        ret.append(urlparse_object.path)
        ret.append(urlparse_object.params)
        ret.append(urlparse_object.query)
        ret.append(urlparse_object.fragment)
    else:
        ret = [urlparse_object[i] for i in range(0, 6)]

    if sys.version_info[0] == 3:
        for i in range(0, 6):
            if not isinstance(ret[i], str):
                ret[i] = ret[i].decode('ascii')
    return tuple(ret)

if __name__ == "__main__":
    # This is just for testing purposes.
    # pylint: disable=R0903
    class class1:

        def __init__(self):
            self.a = "aaaa"

    class class2:

        def __init__(self):
            self.a = 1

    obj1 = class1()
    obj2 = class2()

    obj1, obj2 = make_common_attr_equal(obj1, obj2)

    print(obj1.a)
    print(obj2.a)
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!