#  pylint: disable=missing-module-docstring,invalid-name
#
# Copyright (c) 2008--2016 Red Hat, Inc.
#
# Authors: Pradeep Kilambi
#
# 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
import shutil

# pylint: disable-next=deprecated-module
from optparse import Option, OptionParser
from spacewalk.common.rhnLog import initLOG, rhnLog
from spacewalk.common.rhnConfig import CFG, initCFG
from uyuni.common import rhn_rpm

# pylint: disable-next=ungrouped-imports
from spacewalk.server.rhnLib import parseRPMFilename, get_package_path
from spacewalk.server import rhnSQL, rhnPackageUpload
from spacewalk.server.rhnServer import server_packages
from spacewalk.satellite_tools.progress_bar import ProgressBar

# pylint: disable-next=ungrouped-imports
from uyuni.common.checksum import getFileChecksum

# pylint: disable-next=ungrouped-imports
from spacewalk.server.importlib import mpmSource

initCFG("server.satellite")
initLOG(CFG.LOG_FILE, CFG.DEBUG)

OPTIONS = None
debug = 0
verbose = 0

options_table = [
    Option(
        "--update-package-files",
        action="store_true",
        help="Update package files (bugs #659348, #652852)",
    ),
    Option(
        "--update-sha256", action="store_true", help="Update SHA-256 capable packages"
    ),
    Option("--update-filer", action="store_true", help="Convert filer structure"),
    Option(
        "--update-kstrees", action="store_true", help="Fix kickstart trees permissions"
    ),
    Option(
        "--update-changelog",
        action="store_true",
        help="Fix incorrectly encoded package changelog data",
    ),
    Option("-v", "--verbose", action="count", help="Increase verbosity"),
    Option(
        "--debug", action="store_true", help="Log the debug information to a log file"
    ),
]


def main():
    global debug, verbose
    parser = OptionParser(option_list=options_table)

    (options, args) = parser.parse_args()

    if args:
        for arg in args:
            # pylint: disable-next=consider-using-f-string
            sys.stderr.write("Not a valid option ('%s'), try --help\n" % arg)
        sys.exit(-1)

    if options.verbose:
        initLOG("stdout", options.verbose or 0)
        verbose = 1

    if options.debug:
        initLOG(CFG.LOG_FILE, options.debug or 0)
        debug = 1

    rhnSQL.initDB()

    if options.update_filer:
        process_package_data()

    if options.update_sha256:
        process_sha256_packages()

    if options.update_kstrees:
        process_kickstart_trees()

    if options.update_package_files:
        process_package_files()

    if options.update_changelog:
        process_changelog()


_get_path_query = """
        select id, checksum_type, checksum, path, epoch, new_path
        from (
                select rhnPackage.id,
                       rhnChecksumView.checksum_type,
                       rhnChecksumView.checksum,
                       rhnPackage.path,
                       rhnPackageEvr.epoch,
                        case when rhnPackage.org_id is null then 'NULL'
                             else rhnPackage.org_id || '' end
                        || '/' || substr(rhnChecksumView.checksum, 1, 3)
                        || '/' || rhnPackageName.name
                        || '/' || case when rhnPackageEvr.epoch is null then ''
                                       else rhnPackageEvr.epoch || ':' end
                        || rhnPackageEvr.version || '-' || rhnPackageEvr.release
                        || '/' || rhnPackageArch.label
                        || '/' || rhnChecksumView.checksum
                        || substr(rhnPackage.path, instr(rhnPackage.path, '/', -1))
                        as new_path
                from rhnPackage, rhnPackagename, rhnPackageEvr, rhnPackageArch, rhnChecksumView
                where rhnPackage.name_id = rhnPackageName.id
                        and rhnPackage.evr_id = rhnPackageEvr.id
                        and rhnPackage.package_arch_id = rhnPackageArch.id
                        and rhnPackage.checksum_id = rhnChecksumView.id
                ) X
        where '/' || new_path <> nvl(substr(path, -length(new_path) - 1), 'x')
"""

_update_pkg_path_query = """
    update rhnPackage
       set path = :new_path
    where id = :the_id
"""


def process_package_data():
    if debug:
        # pylint: disable-next=no-value-for-parameter
        log = rhnLog("/var/log/rhn/update-packages.log", 5)

    _get_path_sql = rhnSQL.prepare(_get_path_query)
    _update_package_path = rhnSQL.prepare(_update_pkg_path_query)

    _get_path_sql.execute()
    paths = _get_path_sql.fetchall_dict()

    if not paths:
        # Nothing to change
        return
    if verbose:
        # pylint: disable-next=consider-using-f-string
        print(("Processing %s packages" % len(paths)))
    pb = ProgressBar(
        prompt="standby: ",
        endTag=" - Complete!",
        finalSize=len(paths),
        finalBarLength=40,
        stream=sys.stdout,
    )
    pb.printAll(1)
    skip_list = []
    new_ok_list = []
    i = 0
    for path in paths:
        pb.addTo(1)
        pb.printIncrement()
        old_path_nvrea = path["path"].split("/")
        org_id = old_path_nvrea[1]
        # pylint: disable=W0703
        try:
            nevra = parseRPMFilename(old_path_nvrea[-1])
            if nevra[1] in [None, ""]:
                nevra[1] = path["epoch"]
        except Exception:
            # probably not an rpm skip
            if debug:
                # pylint: disable-next=consider-using-f-string
                log.writeMessage("Skipping: %s Not a valid rpm" % old_path_nvrea[-1])
            continue
        old_abs_path = os.path.join(CFG.MOUNT_POINT, path["path"])

        checksum_type = path["checksum_type"]
        checksum = path["checksum"]
        new_path = get_package_path(
            nevra, org_id, prepend=old_path_nvrea[0], checksum=checksum
        )
        new_abs_path = os.path.join(CFG.MOUNT_POINT, new_path)

        bad_abs_path = os.path.join(
            CFG.MOUNT_POINT,
            get_package_path(
                nevra,
                org_id,
                prepend=old_path_nvrea[0],
                omit_epoch=True,
                checksum=checksum,
            ),
        )

        if not os.path.exists(old_abs_path):
            if os.path.exists(new_abs_path):
                new_ok_list.append(new_abs_path)
                if debug:
                    log.writeMessage(
                        # pylint: disable-next=consider-using-f-string
                        "File %s already on final path %s"
                        % (path["path"], new_abs_path)
                    )
                old_abs_path = new_abs_path
            elif os.path.exists(bad_abs_path):
                # pylint: disable-next=consider-using-f-string
                log.writeMessage("File %s found on %s" % (path["path"], bad_abs_path))
                old_abs_path = bad_abs_path
            else:
                skip_list.append(old_abs_path)
                if debug:
                    log.writeMessage(
                        # pylint: disable-next=consider-using-f-string
                        "Missing path %s for package %d"
                        % (old_abs_path, path["id"])
                    )
                continue

        # pylint: disable=W0703
        try:
            hdr = rhn_rpm.get_package_header(filename=old_abs_path)
        except Exception:
            e = sys.exc_info()[1]
            # pylint: disable-next=consider-using-f-string
            msg = "Exception occurred when reading package header %s: %s" % (
                old_abs_path,
                str(e),
            )
            print(msg)
            if debug:
                log.writeMessage(msg)
            rhnSQL.commit()
            sys.exit(1)

        if old_abs_path != new_abs_path:
            new_abs_dir = os.path.dirname(new_abs_path)
            # relocate the package on the filer
            if debug:
                log.writeMessage(
                    # pylint: disable-next=consider-using-f-string
                    "Relocating %s to %s on filer"
                    % (old_abs_path, new_abs_path)
                )
            if not os.path.isdir(new_abs_dir):
                os.makedirs(new_abs_dir)
            shutil.move(old_abs_path, new_abs_path)
            # Clean up left overs
            os.removedirs(os.path.dirname(old_abs_path))
            # make the path readable
            os.chmod(new_abs_path, int("0644", 8))

        # Update the db paths
        _update_package_path.execute(the_id=path["id"], new_path=new_path)
        if debug:
            log.writeMessage(
                # pylint: disable-next=consider-using-f-string
                "query Executed: update rhnPackage %d to %s"
                % (path["id"], new_path)
            )
        # Process gpg key ids
        server_packages.processPackageKeyAssociations(hdr, checksum_type, checksum)
        if debug:
            # pylint: disable-next=consider-using-f-string
            log.writeMessage("gpg key info updated from %s" % new_abs_path)
        i = i + 1
        # we need to break the transaction to smaller pieces
        if i % 1000 == 0:
            rhnSQL.commit()
    pb.printComplete()
    # All done, final commit
    rhnSQL.commit()
    sys.stderr.write("Transaction Committed! \n")
    if verbose:
        # pylint: disable-next=consider-using-f-string
        print((" Skipping %s packages, paths not found" % len(skip_list)))
    if new_ok_list and verbose:
        print(
            # pylint: disable-next=consider-using-f-string
            (" There were %s packages found in the correct location" % len(new_ok_list))
        )
    return


def process_kickstart_trees():
    # pylint: disable-next=unused-variable
    for root, _dirs, files in os.walk(CFG.MOUNT_POINT + "/rhn/"):
        for name in files:
            os.chmod(root + "/" + name, int("0644", 8))


_get_sha256_packages_query = """
select p.id, p.path
from rhnPackage p,
     rhnPackageRequires pr,
     rhnPackageCapability pc,
     rhnChecksumView cv
where pr.package_id = p.id and
      pr.capability_id = pc.id and
      pc.name = 'rpmlib(FileDigests)' and
      pc.version = '4.6.0-1' and
      cv.id = p.checksum_id and
      cv.checksum_type = 'md5'
"""

_update_sha256_package = """
update rhnPackage
set checksum_id = lookup_checksum(:ctype, :csum),
    path = :path
where id = :id
"""

_select_checksum_type_id = """
select id from rhnChecksumType where label = :ctype
"""

_update_package_files = """
declare
    checksum_id number;
begin
    begin
        insert into rhnChecksum values (
            sequence_nextval('rhnChecksum_seq'),
            :ctype_id,
            :csum ) returning id into checksum_id;
    exception when dup_val_on_index then
        select c.id
        into checksum_id
        from rhnChecksum c
        where c.checksum = :csum and
              c.checksum_type_id = :ctype_id;
    end;

    update rhnPackageFile p
    set p.checksum_id = checksum_id
    where p.capability_id = (
        select c.id
        from rhnPackageCapability c
        where p.package_id = :pid and
              c.name = :filename
    ) and p.package_id = :pid;
end;
"""


def process_sha256_packages():
    if debug:
        # pylint: disable-next=no-value-for-parameter
        log = rhnLog("/var/log/rhn/update-packages.log", 5)

    _get_sha256_packages_sql = rhnSQL.prepare(_get_sha256_packages_query)
    _get_sha256_packages_sql.execute()
    packages = _get_sha256_packages_sql.fetchall_dict()

    if not packages:
        print("No SHA256 capable packages to process.")
        if debug:
            log.writeMessage("No SHA256 capable packages to process.")

        return

    if verbose:
        # pylint: disable-next=consider-using-f-string
        print(("Processing %s SHA256 capable packages" % len(packages)))

    pb = ProgressBar(
        prompt="standby: ",
        endTag=" - Complete!",
        finalSize=len(packages),
        finalBarLength=40,
        stream=sys.stdout,
    )
    pb.printAll(1)

    _update_sha256_package_sql = rhnSQL.prepare(_update_sha256_package)
    _update_package_files_sql = rhnSQL.prepare(_update_package_files)

    for package in packages:
        pb.addTo(1)
        pb.printIncrement()

        old_abs_path = os.path.join(CFG.MOUNT_POINT, package["path"])
        if debug and verbose:
            # pylint: disable-next=consider-using-f-string
            log.writeMessage("Processing package: %s" % old_abs_path)
        temp_file = open(old_abs_path, "rb")
        (
            header,
            # pylint: disable-next=unused-variable
            _payload_stream,
            # pylint: disable-next=unused-variable
            _header_start,
            # pylint: disable-next=unused-variable
            _header_end,
        ) = rhnPackageUpload.load_package(temp_file)
        checksum_type = header.checksum_type()
        checksum = getFileChecksum(checksum_type, file_obj=temp_file)

        old_path = package["path"].split("/")
        nevra = parseRPMFilename(old_path[-1])
        org_id = old_path[1]
        new_path = get_package_path(
            nevra, org_id, prepend=old_path[0], checksum=checksum
        )
        new_abs_path = os.path.join(CFG.MOUNT_POINT, new_path)

        # Filer content relocation
        try:
            if old_abs_path != new_abs_path:
                if debug:
                    log.writeMessage(
                        # pylint: disable-next=consider-using-f-string
                        "Relocating %s to %s on filer"
                        % (old_abs_path, new_abs_path)
                    )

                new_abs_dir = os.path.dirname(new_abs_path)
                if not os.path.isdir(new_abs_dir):
                    os.makedirs(new_abs_dir)

                # link() the old path to the new path
                if not os.path.exists(new_abs_path):
                    os.link(old_abs_path, new_abs_path)
                elif debug:
                    # pylint: disable-next=consider-using-f-string
                    log.writeMessage("File %s already exists" % new_abs_path)

                # Make the new path readable
                os.chmod(new_abs_path, int("0644", 8))
        except OSError:
            e = sys.exc_info()[1]
            # pylint: disable-next=consider-using-f-string
            message = "Error when relocating %s to %s on filer: %s" % (
                old_abs_path,
                new_abs_path,
                str(e),
            )
            print(message)
            if debug:
                log.writeMessage(message)
            sys.exit(1)

        # Update package checksum in the database
        _update_sha256_package_sql.execute(
            ctype=checksum_type, csum=checksum, path=new_path, id=package["id"]
        )

        _select_checksum_type_id_sql = rhnSQL.prepare(_select_checksum_type_id)
        _select_checksum_type_id_sql.execute(ctype=checksum_type)
        checksum_type_id = _select_checksum_type_id_sql.fetchone()[0]

        # Update checksum of every single file in a package
        for i, f in enumerate(header["filenames"]):
            csum = header["filemd5s"][i]

            # Do not update checksums for directories & links
            if not csum:
                continue

            _update_package_files_sql.execute(
                ctype_id=checksum_type_id, csum=csum, pid=package["id"], filename=f
            )

        rhnSQL.commit()

        try:
            if os.path.exists(old_abs_path):
                os.unlink(old_abs_path)
            if os.path.exists(os.path.dirname(old_abs_path)):
                os.removedirs(os.path.dirname(old_abs_path))
        except OSError:
            e = sys.exc_info()[1]
            # pylint: disable-next=consider-using-f-string
            message = "Error when removing %s: %s" % (old_abs_path, str(e))
            print(message)
            if debug:
                log.writeMessage(message)

            sys.exit(1)

    pb.printComplete()


package_query = """
    select p.id as id,
           p.path as path,
           count(pf.capability_id) as filecount,
           count(pf.checksum_id) as nonnullcsums
      from rhnPackage p left outer join rhnPackageFile pf
        on p.id = pf.package_id
     where path is not null
  group by id, path
"""

package_capabilities = """
    select PC.name,
           PF.package_id,
           PF.capability_id,
           C.checksum,
           C.checksum_type
      from rhnPackageFile PF left outer join rhnChecksumView C
        on PF.checksum_id = C.id,
           rhnPackageCapability PC
     where PC.id = PF.capability_id and
           PF.package_id = :pid
"""

update_packagefile_checksum = """
    update rhnPackageFile
       set checksum_id = lookup_checksum(:ctype, :csum)
     where package_id = :pid and
           capability_id = :cid
"""

insert_packagefile = """
    insert into rhnPackageFile (
            package_id, capability_id, device, inode, file_mode, username,
            groupname, rdev, file_size, mtime, linkto, flags, verifyflags,
            lang, checksum_id
           )
    values (
            :pid, lookup_package_capability(:name, null),
            :device, :inode, :file_mode, :username, :groupname,
            :rdev, :file_size, to_timestamp(:mtime, 'YYYY-MM-DD HH24:MI:SS'), :linkto,
            :flags, :verifyflags, :lang, lookup_checksum(:ctype, :csum)
           )
"""

package_name_query = """
    select pn.name as name,
           evr_t_as_vre_simple(pevr.evr) as vre,
           pa.label as arch
      from rhnPackage p,
           rhnPackageName pn,
           rhnPackageEVR pevr,
           rhnPackageArch pa
     where p.id = :pid and
           p.name_id = pn.id and
           p.evr_id = pevr.id and
           p.package_arch_id = pa.id
"""

package_repodata_delete = """
    delete
          from rhnPackageRepoData
         where package_id = :pid
"""


def process_package_files():
    def parse_header(header):
        checksum_type = rhn_rpm.RPM_Header(header).checksum_type()
        return mpmSource.create_package(
            header,
            size=0,
            checksum_type=checksum_type,
            checksum=None,
            relpath=None,
            org_id=None,
            header_start=None,
            header_end=None,
            channels=[],
        )

    package_name_h = rhnSQL.prepare(package_name_query)

    def package_name(pid):
        package_name_h.execute(pid=pid)
        r = package_name_h.fetchall_dict()[0]
        # pylint: disable-next=consider-using-f-string
        return "%s-%s.%s" % (r["name"], r["vre"], r["arch"])

    package_repodata_h = rhnSQL.prepare(package_repodata_delete)

    def delete_package_repodata(pid):
        package_repodata_h.execute(pid=pid)

    # pylint: disable-next=no-value-for-parameter
    log = rhnLog("/var/log/rhn/update-packages.log", 5)

    package_query_h = rhnSQL.prepare(package_query)
    package_query_h.execute()

    package_capabilities_h = rhnSQL.prepare(package_capabilities)
    update_packagefile_checksum_h = rhnSQL.prepare(update_packagefile_checksum)
    insert_packagefile_h = rhnSQL.prepare(insert_packagefile)

    while True:
        row = package_query_h.fetchone_dict()
        if not row:  # No more packages in DB to process
            break

        package_path = os.path.join(CFG.MOUNT_POINT, row["path"])

        if not os.path.exists(package_path):
            if debug:
                # pylint: disable-next=consider-using-f-string
                log.writeMessage("Package path '%s' does not exist." % package_path)
            continue

        # pylint: disable=W0703
        try:
            hdr = rhn_rpm.get_package_header(filename=package_path)
        except Exception:
            e = sys.exc_info()[1]
            # pylint: disable-next=consider-using-f-string
            message = "Error when reading package %s header: %s" % (package_path, e)
            if debug:
                log.writeMessage(message)
            continue

        pkg_updates = 0
        if row["filecount"] != len(hdr["filenames"]):
            # Number of package files on disk and in the DB do not match
            # (possibly a bug #652852). We have to correct them one by one.
            package_capabilities_h.execute(pid=row["id"])
            pkg_caps = {}  # file-name : capabilities dictionary
            for cap in package_capabilities_h.fetchall_dict() or []:
                pkg_caps[cap["name"]] = cap

            for f in parse_header(hdr)["files"]:
                if f["name"] in pkg_caps:
                    continue  # The package files exists in the DB

                # Insert the missing package file into DB
                insert_packagefile_h.execute(
                    pid=row["id"],
                    name=f["name"],
                    ctype=f["checksum_type"],
                    csum=f["checksum"],
                    device=f["device"],
                    inode=f["inode"],
                    file_mode=f["file_mode"],
                    username=f["username"],
                    groupname=f["groupname"],
                    rdev=f["rdev"],
                    file_size=f["file_size"],
                    mtime=f["mtime"],
                    linkto=f["linkto"],
                    flags=f["flags"],
                    verifyflags=f["verifyflags"],
                    lang=f["lang"],
                )
                pkg_updates += 1

            if debug and pkg_updates:
                log.writeMessage(
                    # pylint: disable-next=consider-using-f-string
                    "Package id: %s, name: %s, %s files inserted"
                    % (row["id"], package_name(row["id"]), pkg_updates)
                )
        elif row["nonnullcsums"] == 0:
            # All package files in the DB have null checksum (possibly a bug #659348)
            package_capabilities_h.execute(pid=row["id"])
            pkg_caps = {}  # file-name : capabilities dictionary
            for cap in package_capabilities_h.fetchall_dict() or []:
                pkg_caps[cap["name"]] = cap

            for f in parse_header(hdr)["files"]:
                if (
                    f["checksum"] == ""
                ):  # Convert empty string (symlinks) to None to match w/ Oracle returns
                    f["checksum"] = None

                caps = pkg_caps[f["name"]]

                if not caps["checksum"] == f["checksum"]:
                    # Package file exists, but its checksum in the DB is incorrect
                    update_packagefile_checksum_h.execute(
                        ctype=f["checksum_type"],
                        csum=f["checksum"],
                        pid=caps["package_id"],
                        cid=caps["capability_id"],
                    )
                    pkg_updates += 1

            if debug and pkg_updates:
                log.writeMessage(
                    # pylint: disable-next=consider-using-f-string
                    "Package id: %s, name: %s, %s checksums updated"
                    % (row["id"], package_name(row["id"]), pkg_updates)
                )

        if pkg_updates:
            # pylint: disable-next=consider-using-f-string
            log.writeMessage("Package id: %s, purging rhnPackageRepoData" % row["id"])
            delete_package_repodata(row["id"])

        rhnSQL.commit()  # End of a package


def process_changelog():
    def convert(u):
        last = ""
        while u != last:
            last = u
            try:
                u = last.encode("iso8859-1").decode("utf8")
            except (UnicodeDecodeError, UnicodeEncodeError):
                e = sys.exc_info()[1]
                if e.reason == "unexpected end of data":
                    u = u[:-1]
                    continue
                else:
                    break
        return u

    if CFG.db_backend == "postgresql":
        lengthb = "octet_length(%s)"
    else:
        lengthb = "lengthb(%s)"
    # pylint: disable-next=consider-using-f-string
    _non_ascii_changelog_data_count = """select count(*) as cnt from rhnpackagechangelogdata
                                          where length(name) <> %s
                                             or length(text) <> %s
        """ % (
        lengthb % "name",
        lengthb % "text",
    )
    # pylint: disable-next=consider-using-f-string
    _non_ascii_changelog_data = """select * from rhnpackagechangelogdata
                                    where length(name) <> %s
                                       or length(text) <> %s
        """ % (
        lengthb % "name",
        lengthb % "text",
    )
    _update_changelog_data_name = """update rhnpackagechangelogdata set name = :name
                                           where id = :id"""
    _update_changelog_data_text = """update rhnpackagechangelogdata set text = :text
                                           where id = :id"""
    if debug:
        # pylint: disable-next=no-value-for-parameter
        log = rhnLog("/var/log/rhn/update-packages.log", 5)

    query_count = rhnSQL.prepare(_non_ascii_changelog_data_count)
    query_count.execute()
    nrows = query_count.fetchall_dict()[0]["cnt"]

    query = rhnSQL.prepare(_non_ascii_changelog_data)
    query.execute()

    if nrows == 0:
        msg = "No non-ASCII changelog entries to process."
        print(msg)
        if debug:
            log.writeMessage(msg)
        return

    if verbose:
        # pylint: disable-next=consider-using-f-string
        print(("Processing %s non-ASCII changelog entries" % nrows))

    pb = ProgressBar(
        prompt="standby: ",
        endTag=" - Complete!",
        finalSize=nrows,
        finalBarLength=40,
        stream=sys.stdout,
    )
    pb.printAll(1)

    update_name = rhnSQL.prepare(_update_changelog_data_name)
    update_text = rhnSQL.prepare(_update_changelog_data_text)

    while True:
        row = query.fetchone_dict()
        if not row:  # No more packages in DB to process
            break

        pb.addTo(1)
        pb.printIncrement()

        name_u = row["name"].decode("utf8", "ignore")
        name_fixed = name_u
        if len(row["name"]) != len(name_u):
            name_fixed = convert(name_u)
        if name_fixed != name_u:
            if debug and verbose:
                log.writeMessage(
                    # pylint: disable-next=consider-using-f-string
                    "Fixing record %s: name: '%s'"
                    % (row["id"], row["name"])
                )
            update_name.execute(id=row["id"], name=name_fixed)

        text_u = row["text"].decode("utf8", "ignore")
        text_fixed = text_u
        if len(row["text"]) != len(text_u):
            text_fixed = convert(text_u)
        if text_fixed != text_u:
            if debug and verbose:
                log.writeMessage(
                    # pylint: disable-next=consider-using-f-string
                    "Fixing record %s: text: '%s'"
                    % (row["id"], row["text"])
                )
            update_text.execute(id=row["id"], text=text_fixed)

        rhnSQL.commit()

    pb.printComplete()


if __name__ == "__main__":
    main()
