1#!/usr/local/bin/python3.8
2# -*- coding: utf-8 -*-
3#
4# (c) Copyright 2003-2015 HP Development Company, L.P.
5#
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 2 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19#
20# Author: Don Welch
21#
22
23# Std Lib
24import sys
25import os
26import os.path
27import re
28import time
29import grp
30import pwd
31import tarfile
32import stat
33import glob
34
35try:
36    import hashlib  # new in 2.5
37
38    def get_checksum(s):
39        return hashlib.sha1(s).hexdigest()
40
41except ImportError:
42    import sha  # deprecated in 2.6/3.0
43
44    def get_checksum(s):
45        return sha.new(s).hexdigest()
46
47
48# Local
49from base.g import *
50from base.codes import *
51from base import utils, tui, password, services, os_utils
52from .dcheck import *
53
54
55DISTRO_UNKNOWN = 0
56DISTRO_VER_UNKNOWN = '0.0'
57
58MODE_INSTALLER = 0  # hplip-install/hp-setup
59MODE_CHECK = 1  # hp-check
60MODE_CREATE_DOCS = 2  # create_docs
61
62TYPE_STRING = 1
63TYPE_LIST = 2
64TYPE_BOOL = 3
65TYPE_INT = 4
66
67DEPENDENCY_RUN_TIME = 1
68DEPENDENCY_COMPILE_TIME = 2
69DEPENDENCY_RUN_AND_COMPILE_TIME = 3
70
71DEPENDENCY_REQUIRED_INDEX = 0
72DEPENDENCY_DISPLAY_INDEX = 2
73
74# Mapping from patterns to probability contribution of pattern
75# Example code from David Mertz' Text Processing in Python.
76# Released in the Public Domain.
77err_pats = {r'(?is)<TITLE>.*?(404|403).*?ERROR.*?</TITLE>': 0.95,
78            r'(?is)<TITLE>.*?ERROR.*?(404|403).*?</TITLE>': 0.95,
79            r'(?is)<TITLE>ERROR</TITLE>': 0.30,
80            r'(?is)<TITLE>.*?ERROR.*?</TITLE>': 0.10,
81            r'(?is)<META .*?(404|403).*?ERROR.*?>': 0.80,
82            r'(?is)<META .*?ERROR.*?(404|403).*?>': 0.80,
83            r'(?is)<TITLE>.*?File Not Found.*?</TITLE>': 0.80,
84            r'(?is)<TITLE>.*?Not Found.*?</TITLE>': 0.40,
85            r'(?is)<BODY.*(404|403).*</BODY>': 0.10,
86            r'(?is)<H1>.*?(404|403).*?</H1>': 0.15,
87            r'(?is)<BODY.*not found.*</BODY>': 0.10,
88            r'(?is)<H1>.*?not found.*?</H1>': 0.15,
89            r'(?is)<BODY.*the requested URL.*</BODY>': 0.10,
90            r'(?is)<BODY.*the page you requested.*</BODY>': 0.10,
91            r'(?is)<BODY.*page.{1,50}unavailable.*</BODY>': 0.10,
92            r'(?is)<BODY.*request.{1,50}unavailable.*</BODY>': 0.10,
93            r'(?i)does not exist': 0.10,
94            }
95# Note:- If new utility is added, add same utility here to uninstall properly.
96
97BINS_LIST = ['hpijs', 'hp-align', 'hp-colorcal', 'hp-faxsetup', 'hp-linefeedcal', 'hp-pkservice', 'hp-printsettings', 'hp-sendfax', 'hp-timedate', 'hp-check', 'hp-devicesettings', 'hp-firmware', 'hp-makecopies', 'hp-plugin', 'hp-probe', 'hp-setup', 'hp-toolbox', 'hp-check-plugin', 'hp-diagnose_plugin',
98             'hp-info', 'hp-makeuri', 'hp-pqdiag', 'hp-query', 'hp-systray', 'hp-unload', 'hp-clean', 'hp-fab', 'hp-levels', 'hp-print', 'hp-scan', 'hp-testpage', 'hp-wificonfig', 'hp-upgrade', 'hplip-info', 'hp-check-upgrade', 'hp-config_usb_printer', 'hp-diagnose_queues', 'hp-devicesetup', 'hp-doctor', 'hp-logcapture']
99
100LIBS_LIST = ['libhpmud.*', 'libhpip.*', 'sane/libsane-hpaio.*', 'cups/backend/hp', 'cups/backend/hpfax', 'cups/filter/hpcac','sane/libsane-hp2000S1*', 'libjpeg*',
101             'cups/filter/hpps', 'cups/filter/pstotiff', 'cups/filter/hpcups', 'cups/filter/hpcupsfax', 'cups/filter/hplipjs']
102
103HPLIP_EXT_LIST = ['cupsext.so', 'cupsext.la', 'scanext.so', 'scanext.la',
104                  'hpmudext.so', 'hpmudext.la', 'pcardext.so', 'pcardext.la']
105
106FILES_LIST = ['/usr/local/share/cups/drv/hp/', '/usr/local/share/ppd/HP/', '/usr/local/share/cups/drv/hp/', '/usr/local/share/applications/hplip.desktop', '/usr/local/share/applications/hp-uiscan.desktop',
107              '/etc/xdg/autostart/hplip-systray.desktop', '/usr/local/etc/hp/hplip.conf', '/usr/local/share/doc/hplip-*', '/usr/lib/systemd/system/hplip-printer*.service']
108
109RULES_LIST = ['56-hpmud.rules', '56-hpmud_sysfs.rules', '40-hplip.rules', '56-hpmud_support.rules', '56-hpmud_support_sysfs.rules', '55-hpmud.rules','S99-2000S1.rules',
110              '55-hpmud_sysfs.rules', '56-hpmud_add_printer.rules', '56-hpmud_add_printer_sysfs.rules', '86-hpmud-hp_*.rules', '86-hpmud_plugin.rules', '86-hpmud_plugin_sysfs.rules']
111
112HPLIP_LIST = ['*.py', '*.pyc', 'base', 'copier', 'data', 'installer', 'pcard', 'ui4', 'ui', 'fax/*.py', 'fax/*.pyc',
113              'fax/pstotiff.convs', 'fax/pstotiff.types', 'fax/pstotiff', 'prnt/*.py', 'prnt/*.pyc', 'scan/*.py', 'scan/*.pyc']
114
115PLUGIN_LIST = ['fax/plugins/', 'prnt/plugins/', 'scan/plugins/']
116
117PLUGIN_STATE = ['/var/lib/hp/hplip.state']
118
119
120# end
121
122
123OK_PROCESS_LIST = ['adept-notifier',
124                   'adept_notifier',
125                   'yum-updatesd',
126                   ]
127
128CONFIGURE_ERRORS = {1: "General/unknown error",
129                    2: "libusb not found",
130                    3: "cups-devel not found",
131                    4: "libnetsnmp not found",
132                    5: "netsnmp-devel not found",
133                    6: "python-devel not found",
134                    7: "pthread-devel not found",
135                    8: "ppdev-devel not found",
136                    9: "libcups not found",
137                    10: "libm not found",
138                    11: "libusb-devel not found",
139                    12: "sane-backends-devel not found",
140                    13: "libdbus not found",
141                    14: "dbus-devel not found",
142                    15: "fax requires dbus support",
143                    102: "libjpeg not found",
144                    103: "jpeg-devel not found",
145                    104: "libdi not found",
146                    }
147
148
149EXTERNALDEP = 1
150GENERALDEP = 2
151COMPILEDEP = 3
152PYEXT = 4
153SCANCONF = 5
154
155JPEG_STR = "libjpeg - JPEG library"
156LIBTOOL_STR = "libtool - Library building support services"
157CUPS_STR = "CUPS - Common Unix Printing System"
158CUPS_DEV_STR = "CUPS devel- Common Unix Printing System development files"
159CUPS_IMG_STR = "CUPS image - CUPS image development files"
160GCC_STR = "gcc - GNU Project C and C++ Compiler"
161MAKE_STR = "make - GNU make utility to maintain groups of programs"
162THREAD_STR = "libpthread - POSIX threads library"
163GS_STR = "GhostScript - PostScript and PDF language interpreter and previewer"
164USB_STR = "libusb - USB library"
165CUPS_DDK_STR = "CUPS DDK - CUPS driver development kit"
166SANE_STR = "SANE - Scanning library"
167SANE_DEV_STR = "SANE - Scanning library development files"
168XSANE_STR = "xsane - Graphical scanner frontend for SANE"
169SCANIMAGE_STR = "scanimage - Shell scanning program"
170DBUS_STR = "DBus - Message bus system"
171POLKIT_STR = "PolicyKit - Administrative policy framework"
172SNMP_DEV_STR = "libnetsnmp-devel - SNMP networking library development files"
173CRYPTO_STR = "libcrypto - OpenSSL cryptographic library"
174NETWORK_STR = "network -wget"
175AVAHI_STR = "avahi-utils"
176PYTHON_STR = "Python 2.2 or greater - Python programming language"
177PYNTF_STR = "Python libnotify - Python bindings for the libnotify Desktop notifications"
178QT4DBUS_STR = "PyQt 4 DBus - DBus Support for PyQt4"
179QT4_STR = "PyQt 4- Qt interface for Python (for Qt version 4.x)"
180QT5DBUS_STR = "PyQt 5 DBus - DBus Support for PyQt5"
181QT5_STR = "PyQt 5- Qt interface for Python (for Qt version 4.x)"
182PYDBUS_STR = "Python DBus - Python bindings for DBus"
183PYXML_STR = "Python XML libraries"
184PY_DEV_STR = "Python devel - Python development files"
185PIL_STR = "PIL - Python Imaging Library (required for commandline scanning with hp-scan)"
186PIP_STR = "PIP - preferred installer program"
187TESS_STR = "Tesseract - Tesseract library for python"
188TESSEROCR_STR = "Tesserocr - Optical-character-recognition tesseract library for python"
189IMUTILS_STR = "Imutils - A series of basic image processing functions"
190OPENCV_STR = "Opencv - opencv library for python"
191ZBAR_STR = "ZBAR -zbar library for python"
192LEPTO_STR = "leptonica - leptonica library for python"
193#SETUPTOOLS_STR = "setuptools - library designed to facilitate packaging Python projects"
194PYPDF2_STR = "pypdf2 - pdf library for python"
195REPORTLAB_STR = "Reportlab - PDF library for Python"
196CUPSEXT_STR = 'CUPS-Extension'
197HPMUDEXT_STR = 'IO-Extension'
198HPAIO_STR = 'HPLIP-SANE-Backend'
199SCANEXT_STR = 'Scan-SANE-Extension'
200QT_STR = "Python-Qt"
201EPM_STR = "Build Debian Package"
202AUTOMAKE_STR = "Build Driver"
203
204APPARMOR_DIR = "/etc/apparmor.d"
205SELINUX_DIR = "/etc/selinux/targeted/policy/policy*"
206SEC_DICT = {"AppArmor": (APPARMOR_DIR, ["/etc/apparmor.d/usr.share.hplip", "/etc/apparmor.d/abstractions/hplip"]),
207            "SELinux": (SELINUX_DIR, ["/etc/selinux/targeted/modules/active/modules/hplip.pp"])
208            }
209
210
211try:
212    from functools import update_wrapper
213except ImportError:  # using Python version < 2.5
214    def trace(f):
215        def newf(*args, **kw):
216            log.debug("TRACE: func=%s(), args=%s, kwargs=%s" %
217                      (f.__name__, args, kw))
218            return f(*args, **kw)
219        newf.__name__ = f.__name__
220        newf.__dict__.update(f.__dict__)
221        newf.__doc__ = f.__doc__
222        newf.__module__ = f.__module__
223        return newf
224else:  # using Python 2.5+
225    def trace(f):
226        def newf(*args, **kw):
227            log.debug("TRACE: func=%s(), args=%s, kwargs=%s" %
228                      (f.__name__, args, kw))
229            return f(*args, **kw)
230        return update_wrapper(newf, f)
231
232
233class CoreInstall(object):
234
235    def __init__(self, mode=MODE_INSTALLER, ui_mode=INTERACTIVE_MODE,
236                 ui_toolkit='qt4'):
237        os.umask(0o022)
238        self.mode = mode
239        self.ui_mode = ui_mode
240        self.passwordObj = password.Password(ui_mode)
241        self.version_description, self.version_public, self.version_internal = '', '', ''
242        self.bitness = 32
243        self.endian = utils.LITTLE_ENDIAN
244        self.distro, self.distro_name, self.distro_version = DISTRO_UNKNOWN, '', DISTRO_VER_UNKNOWN
245        self.distro_version_supported = False
246        self.install_location = '/usr'
247        self.hplip_present = False
248        self.have_dependencies = {}
249        self.native_cups = True
250        self.ppd_dir = None
251        self.drv_dir = None
252        self.distros = {}
253        self.ui_toolkit = ui_toolkit
254        self.enable = None
255        self.disable = None
256        self.reload_dbus = False
257        self.security_package = ""
258
259        self.FIELD_TYPES = {
260            'distros': TYPE_LIST,
261            'index': TYPE_INT,
262            'versions': TYPE_LIST,
263            'display_name': TYPE_STRING,
264            'alt_names': TYPE_LIST,
265            'display': TYPE_BOOL,
266            'notes': TYPE_STRING,
267            'package_mgrs': TYPE_LIST,
268            'package_mgr_cmd': TYPE_STRING,
269            'pre_install_cmd': TYPE_LIST,
270            'pre_depend_cmd': TYPE_LIST,
271            'post_depend_cmd': TYPE_LIST,
272            'scanjet_depend_cmd': TYPE_LIST,
273            'scanjet_py3_depend_cmd': TYPE_LIST,
274            'hpoj_remove_cmd': TYPE_STRING,
275            'hplip_remove_cmd': TYPE_STRING,
276            'su_sudo': TYPE_STRING,
277            'ppd_install': TYPE_STRING,
278            'udev_mode_fix': TYPE_BOOL,
279            'ppd_dir': TYPE_STRING,
280            'drv_dir': TYPE_STRING,
281            'fix_ppd_symlink': TYPE_BOOL,
282            'code_name': TYPE_STRING,
283            'supported': TYPE_BOOL,  # Supported by installer
284            'release_date': TYPE_STRING,
285            'packages': TYPE_LIST,
286            'commands': TYPE_LIST,
287            'same_as_version': TYPE_STRING,
288            'scan_supported': TYPE_BOOL,
289            'fax_supported': TYPE_BOOL,
290            'pcard_supported': TYPE_BOOL,
291            'network_supported': TYPE_BOOL,
292            'parallel_supported': TYPE_BOOL,
293            'usb_supported': TYPE_BOOL,
294            'packaged_version': TYPE_STRING,  # Version of HPLIP pre-packaged in distro
295            'cups_path_with_bitness': TYPE_BOOL,
296            'ui_toolkit': TYPE_STRING,  # qt3 or qt4 [or gtk] or none
297            'policykit': TYPE_BOOL,
298            'libusb01': TYPE_BOOL,
299            'udev_sysfs_rule': TYPE_BOOL,
300            'native_cups': TYPE_BOOL,
301            'package_available': TYPE_BOOL,
302            'package_arch': TYPE_LIST,
303            'open_mdns_port': TYPE_LIST,  # command to use to open mdns multicast port 5353
304            'libdir_path': TYPE_STRING,
305        }
306
307        # components
308        # 'name': ('description', [<option list>])
309        self.components = {
310            'hplip': ("HP Linux Imaging and Printing System", ['prnt', 'base', 'network', 'gui_qt4',
311                                                               'gui_qt5', 'fax', 'scan', 'docs']),
312        }
313
314        self.selected_component = 'hplip'
315
316        # options
317        # name: (<required>, "<display_name>", [<dependency list>]), ...
318        self.options = {
319            # HPLIP
320            'prnt':     (True, 'Print Component ', []),
321            'base':     (True,  'Required HPLIP base components (including hpcups)', []),
322            'network': (False, 'Network/JetDirect I/O', []),
323            'gui_qt4': (False, 'Graphical User Interfaces (Qt4)', []),
324            'gui_qt5': (False, 'Graphical User Interfaces (Qt5)', []),
325            'gui_qt': (False, 'Graphical User Interfaces (Qt)', []),
326            'fax':     (False, 'PC Send Fax support', []),
327            'scan':     (False, 'Scanning support', []),
328            'docs':     (False, 'HPLIP documentation (HTML)', []),
329            'policykit': (False, 'Administrative policy framework', []),
330            'libusb01': (False, 'libusb-1.0', []),
331            'udev_sysfs_rule': (False, 'udev_sysfs_rule', []),
332        }
333
334        # holds whether the user has selected (turned on each option)
335        # initial values are defaults (for GUI only)
336        self.selected_options = {
337            'prnt':        False,
338            'base':        True,
339            'network':     True,
340            'gui_qt4':     False,
341            'gui_qt5':     False,
342            'gui_qt':      False,
343            'fax':         True,
344            'scan':        True,
345            'docs':        True,
346            'policykit':   False,
347            'libusb01':   False,
348            'udev_sysfs_rule': False,
349            'native_cups': False,
350            'class-driver': False,
351        }
352
353        # dependencies
354        # 'name': (<required or option>, [<option list>], <display_name>, <check_func>, <runtime/compiletime>), ...
355        # Note: any change to the list of dependencies must be reflected in
356        # base/distros.py
357        self.dependencies = {
358            # Required base packages
359            'epm':              (False, ['prnt'], EPM_STR, self.check_epm, DEPENDENCY_RUN_AND_COMPILE_TIME, '-', None, GENERALDEP),
360            'automake':         (True, ['prnt'], AUTOMAKE_STR, self.check_automake, DEPENDENCY_RUN_AND_COMPILE_TIME, '-', None, GENERALDEP),
361            'libjpeg':          (True,  ['base', 'prnt'], JPEG_STR, self.check_libjpeg, DEPENDENCY_RUN_AND_COMPILE_TIME, '-', None, GENERALDEP),
362            'libtool':          (True,  ['base', 'prnt'], LIBTOOL_STR, self.check_libtool, DEPENDENCY_COMPILE_TIME, '-', 'libtool --version', COMPILEDEP),
363            'cups':            (True,  ['base', 'prnt'], CUPS_STR, self.check_cups, DEPENDENCY_RUN_TIME, '1.1', 'cups-config --version', EXTERNALDEP),
364            'cups-devel':       (True,  ['base', 'prnt'], CUPS_DEV_STR, self.check_cups_devel, DEPENDENCY_COMPILE_TIME, '-', 'cups-config --version', GENERALDEP),
365            'cups-image':       (True,  ['base', 'prnt'], CUPS_IMG_STR, self.check_cups_image, DEPENDENCY_COMPILE_TIME, '-', 'cups-config --version', GENERALDEP),
366            'gcc':             (True,  ['base', 'prnt'], GCC_STR, self.check_gcc, DEPENDENCY_COMPILE_TIME, '-', 'gcc --version', COMPILEDEP),
367            'make':            (True,  ['base', 'prnt'], MAKE_STR, self.check_make, DEPENDENCY_COMPILE_TIME, '3.0', 'make --version', COMPILEDEP),
368            'libpthread':      (True,  ['base', 'prnt'], THREAD_STR, self.check_libpthread, DEPENDENCY_RUN_AND_COMPILE_TIME, '-', 'FUNC#get_libpthread_version', GENERALDEP),
369            'gs':               (True,  ['base', 'prnt'], GS_STR, self.check_gs, DEPENDENCY_RUN_TIME, '7.05', 'gs --version', EXTERNALDEP),
370            'libusb':           (True,  ['base'], USB_STR, self.check_libusb, DEPENDENCY_RUN_AND_COMPILE_TIME, '-', 'FUNC#get_libusb_version', GENERALDEP),
371
372            # Optional base packages
373            # req. for .drv PPD installs
374            'cups-ddk':          (False, ['base', 'prnt'], CUPS_DDK_STR, self.check_cupsddk, DEPENDENCY_RUN_TIME, '-', None, GENERALDEP),
375
376
377            # Required scan packages
378            'sane':            (True,  ['scan'], SANE_STR, self.check_sane, DEPENDENCY_RUN_TIME, '-', 'sane-config --version', GENERALDEP),
379            'sane-devel':      (True,  ['scan'], SANE_DEV_STR, self.check_sane_devel, DEPENDENCY_COMPILE_TIME, '-', 'sane-config --version', GENERALDEP),
380            #'tesseract':       (True, ['scan'], TESS_STR, self.check_tesseract, DEPENDENCY_RUN_TIME, '-', 'tesseract --version', GENERALDEP),
381
382            #'zbar':            (True, ['scan'], ZBAR_STR, self.check_zbar, DEPENDENCY_RUN_TIME, '-', None, GENERALDEP),
383            #'libleptonica':    (True, ['scan'], LEPTO_STR, self.check_libleptonica, DEPENDENCY_RUN_TIME, '-', None, GENERALDEP),
384
385            # Optional scan packages
386            'xsane':            (False, ['scan'], XSANE_STR, self.check_xsane, DEPENDENCY_RUN_TIME, '0.9', 'FUNC#get_xsane_version', EXTERNALDEP),
387            'scanimage':        (False, ['scan'], SCANIMAGE_STR, self.check_scanimage, DEPENDENCY_RUN_TIME, '1.0', 'scanimage --version', EXTERNALDEP),
388
389            # Required fax packages
390            'dbus':             (True,  ['fax'], DBUS_STR, self.check_dbus, DEPENDENCY_RUN_AND_COMPILE_TIME, '-', 'dbus-daemon --version', EXTERNALDEP),
391
392            # Required and optional qt4 GUI packages
393            # optional for non-sudo behavior of plugins (only optional for Qt4
394            # option)
395            'policykit':        (False, ['gui_qt4', 'gui_qt5'], POLKIT_STR, self.check_policykit, DEPENDENCY_RUN_TIME, '-', 'pkexec --version', EXTERNALDEP),
396
397            # Required network I/O packages
398            'libnetsnmp-devel': (True,  ['network'], SNMP_DEV_STR, self.check_libnetsnmp, DEPENDENCY_RUN_AND_COMPILE_TIME, '5.0.9', 'net-snmp-config --version', GENERALDEP),
399            'libcrypto':        (True,  ['network'], CRYPTO_STR, self.check_libcrypto, DEPENDENCY_RUN_AND_COMPILE_TIME, '-', 'openssl version', GENERALDEP),
400            'network':        (False, ['network'], NETWORK_STR, self.check_wget, DEPENDENCY_RUN_TIME, '-', 'wget --version', EXTERNALDEP),
401            'avahi-utils':        (False, ['network'], AVAHI_STR, self.check_avahi_utils, DEPENDENCY_RUN_TIME, '-', 'avahi-browse --version', EXTERNALDEP),
402        }
403
404        python2_dep = {
405            'python2X':         (True,  ['base'], PYTHON_STR, self.check_python, DEPENDENCY_RUN_AND_COMPILE_TIME, '2.2', 'python --version', GENERALDEP),
406            #'setuptools':      (False, ['scan'], SETUPTOOLS_STR, self.check_setuptools, DEPENDENCY_RUN_TIME, '-', None, GENERALDEP),
407            #'pip':          (True, ['scan'], PIP_STR, self.check_pip, DEPENDENCY_RUN_TIME, '-', None, GENERALDEP),
408            #'pypdf2':          (True, ['scan'], PYPDF2_STR, self.check_pypdf2, DEPENDENCY_RUN_TIME, '-', None, GENERALDEP),
409            #'opencv':          (True, ['scan'], OPENCV_STR, self.check_opencv, DEPENDENCY_RUN_TIME, '-', None, GENERALDEP),
410            #'zbar':            (True, ['scan'], ZBAR_STR, self.check_zbar, DEPENDENCY_RUN_TIME, '-', None, GENERALDEP),
411            # Optional for libnotify style popups from hp-systray
412            'python-notify':   (False, ['gui_qt5', 'gui_qt4'], PYNTF_STR, self.check_pynotify, DEPENDENCY_RUN_TIME, '-', 'python-notify --version', GENERALDEP),
413            'pyqt4-dbus':      (True,  ['gui_qt4'], QT4DBUS_STR, self.check_pyqt4_dbus, DEPENDENCY_RUN_TIME, '4.0', 'FUNC#get_pyQt4_version', GENERALDEP),
414            # PyQt 4.x )
415            'pyqt4':            (True,  ['gui_qt4'], QT4_STR, self.check_pyqt4, DEPENDENCY_RUN_TIME, '4.0', 'FUNC#get_pyQt4_version', GENERALDEP),
416            'pyqt5-dbus':      (False,  ['gui_qt5'], QT5DBUS_STR, self.check_pyqt5_dbus, DEPENDENCY_RUN_TIME, '5.0', 'FUNC#get_pyQt5_version', GENERALDEP),
417            # PyQt 5.x )
418            'pyqt5':            (True,  ['gui_qt5'], QT5_STR, self.check_pyqt5, DEPENDENCY_RUN_TIME, '5.0', 'FUNC#get_pyQt5_version', GENERALDEP),
419            'python-dbus':      (True,  ['fax'], PYDBUS_STR, self.check_python_dbus, DEPENDENCY_RUN_TIME, '0.80.0', 'FUNC#get_python_dbus_ver', GENERALDEP),
420            'python-xml':     (True,  ['base'], PYXML_STR, self.check_python_xml, DEPENDENCY_RUN_TIME, '-', 'FUNC#get_python_xml_version', GENERALDEP),
421            'python-devel':    (True,  ['base'], PY_DEV_STR, self.check_python_devel, DEPENDENCY_COMPILE_TIME, '2.2', 'python --version', GENERALDEP),
422            'pil':              (False, ['scan'], PIL_STR, self.check_pil, DEPENDENCY_RUN_TIME, '-', 'FUNC#get_pil_version', GENERALDEP),
423            #'imutils':          (True, ['scan'], IMUTILS_STR, self.check_imutils, DEPENDENCY_RUN_TIME, '-', None, GENERALDEP),
424            # Optional fax packages
425            'reportlab':        (False, ['fax'], REPORTLAB_STR, self.check_reportlab, DEPENDENCY_RUN_TIME, '2.0', 'FUNC#get_reportlab_version', GENERALDEP),
426            #'tesserocr':          (True, ['scan'], TESSEROCR_STR, self.check_tesserocr, DEPENDENCY_RUN_TIME, '-', None, GENERALDEP),
427
428        }
429
430        python3_dep = {
431            'python3X':           (True,  ['base'], PYTHON_STR, self.check_python, DEPENDENCY_RUN_AND_COMPILE_TIME, '2.2', 'python3 --version', GENERALDEP),
432            # Optional for libnotify style popups from hp-systray
433            #'py3_setuptools':      (False, ['scan'], SETUPTOOLS_STR, self.check_setuptools, DEPENDENCY_RUN_TIME, '-', None, GENERALDEP),
434            #'python3-pip':          (True, ['scan'], PIP_STR, self.check_pip3, DEPENDENCY_RUN_TIME, '-', None, GENERALDEP),
435            #'pypdf2':          (True, ['scan'], PYPDF2_STR, self.check_pypdf2, DEPENDENCY_RUN_TIME, '-', None, GENERALDEP),
436            #'opencv':          (True, ['scan'], OPENCV_STR, self.check_opencv, DEPENDENCY_RUN_TIME, '-', None, GENERALDEP),
437            'python3-notify2':   (False, ['gui_qt5', 'gui_qt4'], PYNTF_STR, self.check_pynotify, DEPENDENCY_RUN_TIME, '-', 'python-notify --version', GENERALDEP),
438            'python3-pyqt4-dbus': (False, ['gui_qt4'], QT4DBUS_STR, self.check_pyqt4_dbus, DEPENDENCY_RUN_TIME, '4.0', 'FUNC#get_pyQt4_version', GENERALDEP),
439            # PyQt 4.x )
440            'python3-pyqt4':      (True,  ['gui_qt4'], QT4_STR, self.check_pyqt4, DEPENDENCY_RUN_TIME, '4.0', 'FUNC#get_pyQt4_version', GENERALDEP),
441            'python3-pyqt5-dbus': (False, ['gui_qt5'], QT5DBUS_STR, self.check_pyqt5_dbus, DEPENDENCY_RUN_TIME, '5.0', 'FUNC#get_pyQt5_version', GENERALDEP),
442            # PyQt 5.x )
443            'python3-pyqt5':      (True,  ['gui_qt5'], QT5_STR, self.check_pyqt5, DEPENDENCY_RUN_TIME, '5.0', 'FUNC#get_pyQt5_version', GENERALDEP),
444            'python3-dbus':       (True,  ['fax'], PYDBUS_STR, self.check_python_dbus, DEPENDENCY_RUN_TIME, '0.80.0', 'FUNC#get_python_dbus_ver', GENERALDEP),
445            'python3-xml':      (True,  ['base'], PYXML_STR, self.check_python_xml, DEPENDENCY_RUN_TIME, '-', 'FUNC#get_python_xml_version', GENERALDEP),
446            'python3-devel':     (True,  ['base'], PY_DEV_STR, self.check_python_devel, DEPENDENCY_COMPILE_TIME, '2.2', 'python3 --version', GENERALDEP),
447            'python3-pil':        (False, ['scan'], PIL_STR, self.check_pil, DEPENDENCY_RUN_TIME, '-', 'FUNC#get_pil_version', GENERALDEP),
448            #'imutils':          (True, ['scan'], IMUTILS_STR, self.check_imutils, DEPENDENCY_RUN_TIME, '-', None, GENERALDEP),
449            # Optional fax packages
450            'python3-reportlab':  (False, ['fax'], REPORTLAB_STR, self.check_reportlab, DEPENDENCY_RUN_TIME, '2.0', 'FUNC#get_reportlab_version', GENERALDEP),
451            #'tesserocr':          (True, ['scan'], TESSEROCR_STR, self.check_tesserocr, DEPENDENCY_RUN_TIME, '-', None, GENERALDEP),
452        }
453
454        from base.sixext import PY3
455
456        if PY3:
457            self.dependencies.update(python3_dep)
458        else:
459            self.dependencies.update(python2_dep)
460
461        self.hplip_dependencies = {
462            'cupsext':         (True,  ['base'], CUPSEXT_STR, self.check_cupsext, DEPENDENCY_RUN_AND_COMPILE_TIME, '-', 'FUNC#get_HPLIP_version', PYEXT),
463            'hpmudext':        (True,  ['base'], HPMUDEXT_STR, self.check_hpmudext, DEPENDENCY_RUN_AND_COMPILE_TIME, '-', 'FUNC#get_HPLIP_version', PYEXT),
464            'hpaio':           (True,  ['scan'], HPAIO_STR, self.check_hpaio, DEPENDENCY_RUN_AND_COMPILE_TIME, '-', 'FUNC#get_HPLIP_version', SCANCONF),
465            'scanext':           (True,  ['scan'], SCANEXT_STR, self.check_scanext, DEPENDENCY_RUN_AND_COMPILE_TIME, '-', 'FUNC#get_HPLIP_version', SCANCONF),
466            'pyqt':            (True,  ['gui_qt'], QT_STR, self.check_pyqt, DEPENDENCY_RUN_AND_COMPILE_TIME, '2.3', 'FUNC#get_pyQt_version', GENERALDEP),
467        }
468
469        self.version_func = {
470            'FUNC#get_python_dbus_ver': get_python_dbus_ver,
471            'FUNC#get_pyQt5_version': get_pyQt5_version,
472            'FUNC#get_pyQt4_version': get_pyQt4_version,
473            'FUNC#get_pyQt_version': get_pyQt_version,
474            'FUNC#get_reportlab_version': get_reportlab_version,
475            'FUNC#get_xsane_version': get_xsane_version,
476            'FUNC#get_pil_version': get_pil_version,
477            'FUNC#get_libpthread_version': get_libpthread_version,
478            'FUNC#get_python_xml_version': get_python_xml_version,
479            'FUNC#get_HPLIP_version': get_HPLIP_version,
480            'FUNC#get_libusb_version': get_libusb_version,
481        }
482
483        for opt in self.options:
484            update_spinner()
485            for d in self.dependencies:
486                if opt in self.dependencies[d][1]:
487                    self.options[opt][2].append(d)
488
489        self.load_distros()
490
491        self.distros_index = {}
492        for d in self.distros:
493            self.distros_index[self.distros[d]['index']] = d
494
495    def init(self, callback=None):
496        if callback is not None:
497            callback("Init...\n")
498
499        update_spinner()
500
501        # Package manager names
502        self.package_mgrs = []
503        for d in self.distros:
504            update_spinner()
505
506            for a in self.distros[d].get('package_mgrs', []):
507                if a and a not in self.package_mgrs:
508                    self.package_mgrs.append(a)
509
510        self.version_description, self.version_public, self.version_internal = self.get_hplip_version()
511        log.debug("HPLIP Description=%s Public version=%s Internal version = %s" %
512                  (self.version_description, self.version_public, self.version_internal))
513
514        # have_dependencies
515        # is each dependency satisfied?
516        # start with each one 'No'
517        for d in self.dependencies:
518            update_spinner()
519            self.have_dependencies[d] = False
520
521        self.get_distro()
522        self.distro_name = self.distros_index[self.distro]
523        self.distro_changed()
524
525        if callback is not None:
526            callback("Distro: %s\n" % self.distro)
527
528        self.check_dependencies(callback)
529
530        for d in self.dependencies:
531            update_spinner()
532
533            log.debug("have %s = %s" % (d, self.have_dependencies[d]))
534
535            if callback is not None:
536                callback("Result: %s = %s\n" % (d, self.have_dependencies[d]))
537
538        pid, cmdline = utils.check_pkg_mgr(self.package_mgrs)
539        if pid:
540            log.debug("Running package manager: %s (%s)" % (cmdline, pid))
541
542        self.bitness = utils.getBitness()
543        log.debug("Bitness = %d" % self.bitness)
544
545        update_spinner()
546
547        self.endian = utils.getEndian()
548        log.debug("Endian = %d" % self.endian)
549
550        update_spinner()
551
552        self.distro_version_supported = self.get_distro_ver_data(
553            'supported', False)
554
555        log.debug("Distro = %s Distro Name = %s Display Name= %s Version = %s Supported = %s" %
556                  (self.distro, self.distro_name, self.distros[self.distro_name]['display_name'],
557                   self.distro_version, self.distro_version_supported))
558
559        update_spinner()
560
561        self.hplip_present = self.check_hplip()
562        log.debug("HPLIP (prev install) = %s" % self.hplip_present)
563
564        status, output = utils.run('cups-config --version', self.passwordObj)
565        self.cups_ver = output.strip()
566        log.debug("CUPS version = %s" % self.cups_ver)
567
568        if self.distro_name == "ubuntu":
569            self.reload_dbus = True
570
571        log.debug("DBUS configuration reload possible? %s" % self.reload_dbus)
572
573        status, self.sys_uname_info = utils.run('uname -a', self.passwordObj)
574        self.sys_uname_info = self.sys_uname_info.replace('\n', '')
575        log.debug(self.sys_uname_info)
576
577        # Record the installation time/date and version.
578        # Also has the effect of making the .hplip.conf file user r/w
579        # on the 1st run so that running hp-setup as root doesn't lock
580        # the user out of owning the file
581        user_conf.set('installation', 'date_time',
582                      time.strftime("%x %H:%M:%S", time.localtime()))
583        user_conf.set('installation', 'version', self.version_public)
584
585        if callback is not None:
586            callback("Done")
587
588    def init_for_docs(self, distro_name, version, bitness=32):
589        self.distro_name = distro_name
590        self.distro_version = version
591
592        try:
593            self.distro = self.distros[distro_name]['index']
594        except KeyError:
595            log.error("Invalid distro name: %s" % distro_name)
596            sys.exit(1)
597
598        self.bitness = bitness
599
600        for d in self.dependencies:
601            self.have_dependencies[d] = True
602
603        self.enable_ppds = self.get_distro_ver_data(
604            'ppd_install', 'ppd') == 'ppd'
605        self.ppd_dir = self.get_distro_ver_data('ppd_dir')
606        self.drv_dir = self.get_distro_ver_data('drv_dir')
607
608        self.distro_version_supported = True  # for manual installs
609
610    def check_dependencies(self, callback=None):
611        update_ld_output()
612
613        for d in self.dependencies:
614            update_spinner()
615
616            log.debug("Checking for dependency '%s'...\n" % d)
617
618            if callback is not None:
619                callback("Checking: %s\n" % d)
620
621            self.have_dependencies[d] = self.dependencies[d][3]()
622            log.debug("have %s = %s" % (d, self.have_dependencies[d]))
623
624        cleanup_spinner()
625
626    def password_func(self):
627        if self.password:
628            return self.password
629        elif self.ui_mode == INTERACTIVE_MODE:
630            import getpass
631            return getpass.getpass("Enter password: ")
632        else:
633            return ''
634
635    def get_distro(self):
636        log.debug("Determining distro...")
637        name, ver = '', '0.0'
638        found = False
639
640        # Getting distro information using platform module
641        try:
642            import platform
643            try:
644                name = platform.dist()[0].lower()
645                ver = platform.dist()[1]
646            except AttributeError:
647                import distro
648                name = distro.linux_distribution()[0].lower()
649                ver = distro.linux_distribution()[1]
650
651            if not name:
652                found = False
653                log.debug("Not able to detect distro")
654            else:
655                found = True
656                log.debug("Able to detect distro")
657        except ImportError:
658            found = False
659            log.debug("Not able to detect distro in exception")
660
661        # Getting distro information using lsb_release command
662        # platform retrurn 'redhat' even for 'RHEL' or 'arch' for ManjaroLinux so re-reading using
663        # lsb_release.
664        if not found or name == 'redhat' or name == 'arch':
665            lsb_rel = utils.which("lsb_release", True)
666            if lsb_rel:
667                log.debug("Using 'lsb_release -is/-rs'")
668                status, name = utils.run(lsb_rel + ' -is', self.passwordObj)
669                if not status and name:
670                    status, ver = utils.run(lsb_rel + ' -rs', self.passwordObj)
671                    if not status and ver:
672                        ver = ver.lower().strip()
673                        found = True
674
675        # Getting distro information using /etc/issue file
676        if not found:
677            try:
678                name = open('/etc/issue', 'r').read().lower().strip()
679            except IOError:
680                found = False
681            else:
682                found = True
683                for n in name.split():
684                    m = n
685                    if '.' in n:
686                        m = '.'.join(n.split('.')[:2])
687
688                    try:
689                        ver = float(m)
690                    except ValueError:
691                        try:
692                            ver = int(m)
693                        except ValueError:
694                            ver = '0.0'
695
696        # Updating the distro name and version.
697        if found:
698            name = name.lower().strip()
699            log.debug("Distro name=%s" % name)
700            if name.find("redhatenterprise") > -1 or name.find("redhat") > -1:
701                name = "rhel"
702
703            log.debug("Distro version=%s" % ver)
704            if name == "rhel" and ver[0] == "5" and ver[1] == ".":
705                ver = "5.0"
706            elif name == "rhel" and ver[0] == "6" and ver[1] == ".":
707                ver = "6.0"
708
709            found_in_list = False
710            for d in self.distros:
711                if name.find(d) > -1:
712                    self.distro = self.distros[d]['index']
713                    found_in_list = True
714                else:
715                    for x in self.distros[d].get('alt_names', ''):
716                        if x and name.find(x) > -1:
717                            self.distro = self.distros[d]['index']
718                            found_in_list = True
719                            break
720                if found_in_list:
721                    break
722
723            self.distro_version = ver
724            self.distro_name = name
725        else:
726            log.warn("Failed to get the distro information.")
727            self.distro, self.distro_version = DISTRO_UNKNOWN, '0.0'
728
729        log.debug("distro=%d, distro_version=%s" %
730                  (self.distro, self.distro_version))
731
732    def distro_changed(self):
733        ppd_install = self.get_distro_ver_data('ppd_install', 'ppd')
734
735        if ppd_install not in ('ppd', 'drv'):
736            log.warning("Invalid ppd_install value: %s" % ppd_install)
737
738        self.enable_ppds = (ppd_install == 'ppd')
739
740        log.debug("Enable PPD install: %s (False=drv)" % self.enable_ppds)
741
742        self.ppd_dir = self.get_distro_ver_data('ppd_dir')
743
744        self.drv_dir = self.get_distro_ver_data('drv_dir')
745        if not self.enable_ppds and not self.drv_dir:
746            log.warning("Invalid drv_dir value: %s" % self.drv_dir)
747
748        self.distro_version_supported = self.get_distro_ver_data(
749            'supported', False)
750        self.selected_options['fax'] = self.get_distro_ver_data(
751            'fax_supported', True)
752        self.selected_options['network'] = self.get_distro_ver_data(
753            'network_supported', True)
754        self.selected_options['scan'] = self.get_distro_ver_data(
755            'scan_supported', True)
756        self.selected_options['policykit'] = self.get_distro_ver_data(
757            'policykit', False)
758        self.selected_options[
759            'libusb01'] = self.get_distro_ver_data('libusb01', False)
760        self.selected_options['udev_sysfs_rule'] = self.get_distro_ver_data(
761            'udev_sysfs_rule', False)
762        self.native_cups = self.get_distro_ver_data('native_cups', False)
763
764        # Adjust required flag based on the distro ver ui_toolkit value
765        ui_toolkit = self.get_distro_ver_data('ui_toolkit', 'qt4').lower()
766
767        if ui_toolkit == 'qt4':
768            log.debug("Default UI toolkit: Qt4")
769            self.ui_toolkit = 'qt4'
770            self.selected_options['gui_qt4'] = True
771        elif ui_toolkit == 'qt5':
772            log.debug("Default UI toolkit: Qt5")
773            self.ui_toolkit = 'qt5'
774            self.selected_options['gui_qt5'] = True
775
776        # todo: gtk
777        # Override with --qt4 command args
778        if self.enable is not None:
779            if 'qt4' in self.enable:
780                log.debug("User selected UI toolkit: Qt4")
781                self.ui_toolkit = 'qt4'
782                self.selected_options['gui_qt4'] = True
783            elif 'qt5' in self.enable:
784                log.debug("User selected UI toolkit: Qt5")
785                self.ui_toolkit = 'qt5'
786                self.selected_options['gui_qt5'] = True
787
788        if self.disable is not None:
789            if 'qt4' in self.disable:
790                log.debug("User deselected UI toolkit: Qt4")
791                self.selected_options['gui_qt4'] = False
792            elif 'qt5' in self.disable:
793                log.debug("User deselected UI toolkit: Qt5")
794                self.selected_options['gui_qt5'] = False
795
796    def __fixup_data(self, key, data):
797        field_type = self.FIELD_TYPES.get(key, TYPE_STRING)
798        #log.debug("%s (%s) %d" % (key, data, field_type))
799
800        if field_type == TYPE_BOOL:
801            return utils.to_bool(data)
802
803        elif field_type == TYPE_STRING:
804            if type('') == type(data):
805                return data.strip()
806            else:
807                return data
808
809        elif field_type == TYPE_INT:
810            try:
811                return int(data)
812            except ValueError:
813                return 0
814
815        elif field_type == TYPE_LIST:
816            return [x for x in data.split(',') if x]
817
818    def load_distros(self):
819        if self.mode == MODE_INSTALLER:
820            distros_dat_file = os.path.join('installer', 'distros.dat')
821
822        elif self.mode == MODE_CREATE_DOCS:
823            distros_dat_file = os.path.join(
824                '..', '..', 'installer', 'distros.dat')
825
826        else:  # MODE_CHECK
827            distros_dat_file = os.path.join(
828                prop.home_dir, 'installer', 'distros.dat')
829
830            if not os.path.exists(distros_dat_file):
831                log.debug(
832                    "DAT file not found at %s. Using local relative path..." % distros_dat_file)
833                distros_dat_file = os.path.join('installer', 'distros.dat')
834
835        distros_dat = ConfigBase(distros_dat_file)
836        distros_list = self.__fixup_data(
837            'distros', distros_dat.get('distros', 'distros'))
838        log.debug(distros_list)
839
840        for distro in distros_list:
841            update_spinner()
842            d = {}
843
844            if not distros_dat.has_section(distro):
845                log.debug(
846                    "Missing distro section in distros.dat: [%s]" % distro)
847                continue
848
849            for key in distros_dat.keys(distro):
850                d[key] = self.__fixup_data(key, distros_dat.get(distro, key))
851
852            self.distros[distro] = d
853            versions = self.__fixup_data(
854                "versions", distros_dat.get(distro, 'versions'))
855            self.distros[distro]['versions'] = {}
856            self.distros[distro]['versions_list'] = versions
857
858            for ver in versions:
859                same_as_version, supported = False, True
860                v = {}
861                ver_section = "%s:%s" % (distro, ver)
862
863                if not distros_dat.has_section(ver_section):
864                    log.error(
865                        "Missing version section in distros.dat: [%s:%s]" % (distro, ver))
866                    continue
867
868                if 'same_as_version' in distros_dat.keys(ver_section):
869                    same_as_version = True
870
871                supported = self.__fixup_data(
872                    'supported', distros_dat.get(ver_section, 'supported'))
873
874                for key in distros_dat.keys(ver_section):
875                    v[key] = self.__fixup_data(
876                        key, distros_dat.get(ver_section, key))
877
878                self.distros[distro]['versions'][ver] = v
879                self.distros[distro]['versions'][ver]['dependency_cmds'] = {}
880
881                if same_as_version:  # or not supported:
882                    continue
883
884                for dep in self.dependencies:
885                    dd = {}
886                    dep_section = "%s:%s:%s" % (distro, ver, dep)
887
888                    if not distros_dat.has_section(dep_section) and not same_as_version:
889                        continue
890
891                    # if same_as_version:
892                    #    continue
893
894                    for key in distros_dat.keys(dep_section):
895                        dd[key] = self.__fixup_data(
896                            key, distros_dat.get(dep_section, key))
897
898                    self.distros[distro]['versions'][
899                        ver]['dependency_cmds'][dep] = dd
900
901            versions = self.distros[distro]['versions']
902            for ver in versions:
903                ver_section = "%s:%s" % (distro, ver)
904
905                if 'same_as_version' in distros_dat.keys(ver_section):
906                    v = self.__fixup_data("same_as_version", distros_dat.get(
907                        ver_section, 'same_as_version'))
908
909                    try:
910                        import copy
911                        vv = copy.deepcopy(self.distros[distro]['versions'][v])
912                        #vv = self.distros[distro]['versions'][v].copy()
913                        vv['same_as_version'] = v
914                        self.distros[distro]['versions'][ver] = vv
915                        for key in distros_dat.keys(ver_section):
916                            vv[key] = self.__fixup_data(
917                                key, distros_dat.get(ver_section, key))
918                        dd = {}
919                        for dep in self.dependencies:
920                            dep_section = "%s:%s:%s" % (distro, ver, dep)
921                            if not distros_dat.has_section(dep_section):
922                                continue
923
924                            for key in distros_dat.keys(dep_section):
925                                dd[key] = self.__fixup_data(
926                                    key, distros_dat.get(dep_section, key))
927
928                            self.distros[distro]['versions'][
929                                ver]['dependency_cmds'][dep] = dd
930
931                    except KeyError:
932                        log.debug(
933                            "Missing 'same_as_version=' version in distros.dat for section [%s:%s]." % (distro, v))
934                        continue
935
936        #import pprint
937        # pprint.pprint(self.distros)
938
939    def pre_install(self):
940        pass
941
942    def pre_depend(self):
943        pass
944
945    def check_python(self):
946        py_ver = sys.version_info
947        py_major_ver, py_minor_ver = py_ver[:2]
948        log.debug("Python ver=%d.%d" % (py_major_ver, py_minor_ver))
949        return py_major_ver >= 2
950
951    def check_gcc(self):
952        return check_tool('gcc --version', 0) and check_tool('g++ --version', 0)
953
954    def check_make(self):
955        return check_tool('make --version', 3.0)
956
957    def check_libusb(self):
958        Is_libusb01_enabled = self.get_distro_ver_data('libusb01', False)
959        if Is_libusb01_enabled == True:
960            if not check_lib('libusb'):
961                return False
962            if self.distro_name != "rhel":
963                return len(locate_file_contains("usb.h", '/usr/local/include', 'usb_init'))
964            else:
965                return True
966        else:
967            if not check_lib('libusb-1.0'):
968                return False
969            if self.distro_name != "rhel":
970                return len(locate_file_contains("libusb.h", '/usr/local/include/libusb-1.0', 'libusb_init'))
971            else:
972                return True
973
974    def check_epm(self):
975        return check_tool('epm --version')
976
977    def check_automake(self):
978        return check_tool('automake --version')
979
980    def check_libjpeg(self):
981        return check_lib("libjpeg") and check_file("jpeglib.h")
982
983    def check_libcrypto(self):
984        return check_lib("libcrypto") and check_file("crypto.h")
985
986    def check_libpthread(self):
987        return check_lib("libpthread") and check_file("pthread.h")
988
989    def check_libnetsnmp(self):
990        return check_lib("libnetsnmp") and check_file("net-snmp-config.h")
991
992    def check_reportlab(self):
993        try:
994            log.debug("Trying to import 'reportlab'...")
995            import reportlab
996
997            ver = str(reportlab.Version)
998            log.debug("Version: %.1s" % ver)
999            if ver >= "2.0":
1000                log.debug("Success.")
1001                return True
1002            else:
1003                return False
1004
1005        except ImportError:
1006            log.debug("Failed.")
1007            return False
1008
1009    def check_python23(self):
1010        py_ver = sys.version_info
1011        py_major_ver, py_minor_ver = py_ver[:2]
1012        log.debug("Python ver=%d.%d" % (py_major_ver, py_minor_ver))
1013        return py_major_ver >= 2 and py_minor_ver >= 3
1014
1015    def check_python_xml(self):
1016        try:
1017            import xml.parsers.expat
1018        except ImportError:
1019            return False
1020        else:
1021            return True
1022
1023    def check_sane(self):
1024        return check_lib('libsane')
1025
1026    def check_sane_devel(self):
1027        return len(locate_file_contains("sane.h", '/usr/local/include', 'extern SANE_Status sane_init'))
1028
1029    def check_xsane(self):
1030        if os.getenv('DISPLAY'):
1031            # will fail if X not running...
1032            return check_version(get_xsane_version(), '0.9')
1033# return check_tool('xsane --version', 0.9) # will fail if X not
1034# running...
1035        else:
1036            # ...so just see if it installed somewhere
1037            return bool(utils.which("xsane"))
1038
1039    def check_scanimage(self):
1040        return check_tool('scanimage --version', 1.0)
1041
1042    def check_gs(self):
1043        return check_tool('gs -v', 7.05)
1044
1045    def check_pyqt4(self):
1046        if self.ui_toolkit == 'qt4':
1047            try:
1048                import PyQt4
1049            except ImportError:
1050                return False
1051            else:
1052                return True
1053        else:
1054            return False
1055
1056    def check_pyqt5(self):
1057        if self.ui_toolkit == 'qt5':
1058            try:
1059                import PyQt5
1060            except ImportError:
1061                return False
1062            else:
1063                return True
1064        else:
1065            return False
1066
1067    def check_pyqt4_dbus(self):
1068        if self.ui_toolkit == 'qt4':
1069            try:
1070                from dbus.mainloop.qt import DBusQtMainLoop
1071            except ImportError:
1072                return False
1073            else:
1074                return True
1075        else:
1076            return False
1077
1078    def check_pyqt5_dbus(self):
1079        if self.ui_toolkit == 'qt5':
1080            try:
1081                from dbus.mainloop.pyqt5 import DBusQtMainLoop
1082            except ImportError:
1083                return False
1084            else:
1085                return True
1086        else:
1087            return False
1088
1089    def check_pyqt(self):
1090        if self.ui_toolkit == 'qt3':
1091            try:
1092                import qt
1093            except ImportError:
1094                return False
1095            else:
1096                return True
1097
1098        else:
1099            return False
1100
1101    def check_python_devel(self):
1102        dir_list = glob.glob('/usr/local/include/python%d*' % sys.version_info[0])
1103        Found = False
1104        for p in dir_list:
1105            if check_file('Python.h', dir=p):
1106                Found = True
1107                break
1108
1109        return Found
1110
1111    def check_pynotify(self):
1112        try:
1113            import notify2
1114        except (ImportError, RuntimeError):
1115            try:
1116                import pynotify
1117            except (ImportError, RuntimeError):
1118                return False
1119        return True
1120
1121    def check_python_dbus(self):
1122        log.debug("Checking for python-dbus (>= 0.80)...")
1123        try:
1124            import dbus
1125            try:
1126                ver = dbus.version
1127                log.debug("Version: %s" % '.'.join(
1128                    [str(x) for x in dbus.version]))
1129                return ver >= (0, 80, 0)
1130
1131            except AttributeError:
1132                try:
1133                    ver = dbus.__version__
1134                    log.debug("Version: %s" % dbus.__version__)
1135                    log.debug("HPLIP requires dbus version > 0.80.")
1136                    return False
1137
1138                except AttributeError:
1139                    log.debug(
1140                        "Unknown version. HPLIP requires dbus version > 0.80.")
1141                    return False
1142
1143        except ImportError:
1144            return False
1145
1146    def check_python_ctypes(self):
1147        try:
1148            import ctypes
1149            return True
1150        except ImportError:
1151            return False
1152
1153    def check_dbus(self):
1154        log.debug("Checking for dbus running and header files present (dbus-devel)...")
1155        if (self.distro_name.lower()=='fedora') and (self.distro_version>='30'):
1156            return True
1157        else:
1158            return check_ps(['dbus-daemon'])  and \
1159                len(locate_file_contains("dbus-message.h",
1160                                         '/usr/local/include', 'dbus_message_new_signal'))
1161
1162    def check_cups_devel(self):
1163        return check_file('cups.h') and bool(utils.which('lpr'))
1164
1165    def check_cups(self):
1166        status, output = utils.run('lpstat -r', self.passwordObj)
1167        if status > 0 or 'not running' in output:
1168            log.debug("CUPS is not running. %s" % output)
1169            return False
1170        else:
1171            log.debug("CUPS is running. %s " % output)
1172            return True
1173
1174    def check_cups_image(self):
1175        return check_file("raster.h", "/usr/local/include/cups")
1176
1177    def check_hplip(self):
1178        log.debug("Checking for HPLIP...")
1179        return locate_files('hplip.conf', '/usr/local/etc/hp')
1180
1181    def check_libtool(self):
1182        log.debug("Checking for libtool...")
1183        return check_tool('libtool --version')
1184
1185    '''def check_pip(self):
1186        return check_tool('pip2 --version')
1187
1188    def check_pip3(self):
1189        return check_tool('pip3 --version')
1190
1191    def check_tesseract(self):
1192        return check_tool('tesseract --version')
1193
1194    def check_opencv(self):
1195        try:
1196            import cv2
1197            return True
1198        except ImportError:
1199            return False
1200
1201    def check_tesserocr(self):
1202        try:
1203            import tesserocr
1204            return True
1205        except ImportError:
1206            return False
1207
1208    def check_imutils(self):
1209        try:
1210            import imutils
1211            return True
1212        except ImportError:
1213            return False
1214
1215    def check_zbar(self):
1216        return check_file("zbar.h", "/usr/local/include/")
1217
1218    def check_libleptonica(self):
1219        return check_file("allheaders.h", "/usr/local/include/leptonica")
1220
1221    def check_setuptools(self):
1222        try:
1223            import setuptools
1224            return True
1225        except ImportError:
1226            return False
1227
1228    def check_pypdf2(self):
1229        try:
1230            import PyPDF2
1231            return True
1232        except ImportError:
1233            return False
1234'''
1235    def check_pil(self):
1236        log.debug("Checking for PIL...")
1237        try:
1238            from PIL import Image
1239            return True
1240        except ImportError:
1241            return False
1242
1243    def check_cupsddk(self):
1244        log.debug("Checking for cups-ddk...")
1245        # TODO: Compute these paths some way or another...
1246        return check_file('media.defs', "/usr/local/share/cups/ppdc/")
1247
1248    def check_policykit(self):
1249        log.debug("Checking for PolicyKit...")
1250        if check_file('PolicyKit.conf', "/etc/PolicyKit") and check_file('org.gnome.PolicyKit.AuthorizationManager.service', "/usr/local/share/dbus-1/services"):
1251            return True
1252        elif check_file('50-localauthority.conf', "/etc/polkit-1/localauthority.conf.d") and check_file('org.freedesktop.PolicyKit1.service', "/usr/local/share/dbus-1/system-services"):
1253            return True
1254        elif check_file('org.freedesktop.PolicyKit1.conf', '/etc/dbus-1/system.d'):
1255            return True
1256        elif check_file('polkitd', "/usr/lib/polkit-1") and check_file('polkit-agent-helper-1', "/usr/lib/polkit-1") and check_file('polkit.conf', "/usr/lib/sysusers.d"):
1257            return True
1258        else:
1259            return False
1260
1261    def check_cupsext(self):
1262        log.debug("Checking 'cupsext' CUPS extension...")
1263        try:
1264            import cupsext
1265        except ImportError:
1266            log.error(
1267                "NOT FOUND OR FAILED TO LOAD! Please reinstall HPLIP and check for the proper installation of cupsext.")
1268            return False
1269        else:
1270            return True
1271
1272    def check_hpmudext(self):
1273        log.debug("Checking 'hpmudext' I/O extension...")
1274        try:
1275            import hpmudext
1276        except ImportError:
1277            log.error(
1278                "NOT FOUND OR FAILED TO LOAD! Please reinstall HPLIP and check for the proper installation of hpmudext.")
1279            return False
1280        else:
1281            return True
1282
1283    def check_pcardext(self):
1284        log.debug("Checking 'pcardext' Photocard extension...")
1285        try:
1286            import pcardext
1287        except ImportError:
1288            log.error(
1289                "NOT FOUND OR FAILED TO LOAD! Please reinstall HPLIP and check for the proper installation of pcardext.")
1290            return False
1291        else:
1292            return True
1293
1294    def check_hpaio(self):
1295        found = False
1296        for path in ['/usr/local/etc/sane.d/dll.conf', '/usr/local/etc/sane.d/dll.d/hpaio', '/usr/local/etc/sane.d/dll.d/hplip']:
1297            log.debug("'Checking for hpaio' in '%s'..." % path)
1298            try:
1299                f = open(path, 'r')
1300            except IOError:
1301                log.info("'%s' not found." % path)
1302            else:
1303                for line in f:
1304                    lineNoSpace = re.sub(r'\s', '', line)
1305                    hpaiomatched = re.match('hpaio', lineNoSpace)
1306                    if hpaiomatched:
1307                        found = True
1308                        break
1309            if found:
1310                break
1311
1312        if not found:
1313            log.error("'hpaio' not found in SANE conf files. Is SANE installed?")
1314
1315        return found
1316
1317    def update_hpaio(self):
1318        found = False
1319        home_dir = sys_conf.get('dirs', 'home')
1320        pat = re.compile(r"""(\S.*)share\/hplip""")
1321        usrbin_dir = None
1322        if pat.match(home_dir) is not None:
1323            usrlib_dir = pat.match(home_dir).group(1) + "lib/"
1324            if os.path.exists(usrlib_dir + 'sane/libsane-hpaio.so.1'):
1325                log.debug("'Updating hpaio' in '/usr/local/etc/sane.d/dll.conf'...")
1326                try:
1327                    f = open('/usr/local/etc/sane.d/dll.conf', 'r')
1328                except IOError:
1329                    log.error(
1330                        "'/usr/local/etc/sane.d/dll.conf' not found. Creating dll.conf file")
1331                    cmd = self.passwordObj.getAuthCmd() % 'touch /usr/local/etc/sane.d/dll.conf'
1332                    log.debug("cmd=%s" % cmd)
1333                    utils.run(cmd, self.passwordObj)
1334                else:
1335                    for line in f:
1336                        lineNoSpace = re.sub(r'\s', '', line)
1337                        hpaiomatched = re.match('hpaio', lineNoSpace)
1338                        if hpaiomatched:
1339                            found = True
1340                            break
1341                    f.close()
1342
1343                if not found:
1344                    st = os.stat('/usr/local/etc/sane.d/dll.conf')
1345                    cmd = self.passwordObj.getAuthCmd() % 'chmod 777 /usr/local/etc/sane.d/dll.conf'
1346                    log.debug("cmd=%s" % cmd)
1347                    utils.run(cmd, self.passwordObj)
1348                    try:
1349                        f = open('/usr/local/etc/sane.d/dll.conf', 'a+')
1350                    except IOError:
1351                        log.error(
1352                            "'/usr/local/etc/sane.d/dll.conf' not found. Creating dll.conf file")
1353                    else:
1354                        f.write('hpaio')
1355                        f.close()
1356                    actv_permissions = st.st_mode & 0o777
1357                    cmd = 'chmod %o /usr/local/etc/sane.d/dll.conf' % actv_permissions
1358                    cmd = self.passwordObj.getAuthCmd() % cmd
1359                    log.debug("cmd=%s" % cmd)
1360                    utils.run(cmd, self.passwordObj)
1361        return found
1362
1363    def check_scanext(self):
1364        log.debug("Checking 'scanext' SANE scanning extension...")
1365        found = False
1366        try:
1367            import scanext
1368        except ImportError:
1369            log.error(
1370                "NOT FOUND OR FAILED TO LOAD! Please reinstall HPLIP and check for the proper installation of scanext.")
1371        else:
1372            found = True
1373        return found
1374
1375    def security_package_status(self):
1376        found = ["", False]
1377
1378        for key in SEC_DICT.keys():
1379            if glob.glob(SEC_DICT[key][0]):
1380                found[0] = key
1381                found[1] = all(map(glob.glob, SEC_DICT[key][1]))
1382
1383                return found
1384
1385        return found
1386
1387    def selinux_install(self):
1388        src_dir = os.getcwd()
1389        profile_location = src_dir + "/selinux/hplip.pp"
1390        profile_cmd = "semodule -n -i " + profile_location
1391        cmd = self.passwordObj.getAuthCmd() % profile_cmd
1392        log.info("Installing SELinux profile...")
1393        status, output = utils.run(cmd, self.passwordObj)
1394
1395    def get_hplip_version(self):
1396        self.version_description, self.version_public, self.version_internal = '', '', ''
1397
1398        if self.mode == MODE_INSTALLER:
1399            ac_init_pat = re.compile(
1400                r"""AC_INIT\(\[(.*?)\], *\[(.*?)\], *\[(.*?)\], *\[(.*?)\] *\)""", re.IGNORECASE)
1401
1402            try:
1403                config_in = open('./configure.in', 'r')
1404            except IOError:
1405                self.version_description, self.version_public, self.version_internal = \
1406                    '', sys_conf.get('configure', 'internal-tag',
1407                                     '0.0.0'), prop.installed_version
1408            else:
1409                for c in config_in:
1410                    if c.startswith("AC_INIT"):
1411                        match_obj = ac_init_pat.search(c)
1412                        self.version_description = match_obj.group(1)
1413                        self.version_public = match_obj.group(2)
1414                        self.version_internal = match_obj.group(3)
1415                        name = match_obj.group(4)
1416                        break
1417
1418                config_in.close()
1419
1420                if name != 'hplip':
1421                    log.error("Invalid archive!")
1422
1423        else:  # MODE_CHECK
1424            try:
1425                self.version_description, self.version_public, self.version_internal = \
1426                    '', sys_conf.get('configure', 'internal-tag',
1427                                     '0.0.0'), prop.installed_version
1428            except KeyError:
1429                self.version_description, self.version_public, self.version_internal = '', '', ''
1430
1431        return self.version_description, self.version_public, self.version_internal
1432
1433    def configure(self, bClassDriver):
1434        configure_cmd = './configure'
1435        configuration = {}
1436        if PY3:
1437            dbus_avail = self.have_dependencies[
1438                'dbus'] and self.have_dependencies['python3-dbus']
1439        else:
1440            dbus_avail = self.have_dependencies[
1441                'dbus'] and self.have_dependencies['python-dbus']
1442        configuration['network-build'] = self.selected_options['network']
1443
1444        if bClassDriver:
1445            configuration['scan-build'] = False
1446            configuration['fax-build'] = False
1447            configuration['dbus-build'] = False
1448            configuration['qt4'] = False
1449            configuration['qt5'] = False
1450            configuration['class-driver'] = True
1451        else:
1452            configuration['scan-build'] = self.selected_options['scan']
1453            configuration[
1454                'fax-build'] = self.selected_options['fax'] and dbus_avail
1455            configuration['dbus-build'] = dbus_avail
1456            configuration['qt4'] = self.selected_options['gui_qt4']
1457            configuration['qt5'] = self.selected_options['gui_qt5']
1458            configuration['class-driver'] = self.selected_options[
1459                'class-driver']
1460
1461        configuration['doc-build'] = self.selected_options['docs']
1462        configuration['policykit'] = self.selected_options['policykit']
1463        configuration['libusb01_build'] = self.selected_options['libusb01']
1464        configuration['udev_sysfs_rules'] = self.selected_options[
1465            'udev_sysfs_rule']
1466
1467        # Setup printer driver configure flags based on distro data...
1468        if self.native_cups:  # hpcups
1469            configuration['hpcups-install'] = True
1470            configuration['hpijs-install'] = False
1471            configuration['foomatic-ppd-install'] = False
1472            configuration['foomatic-drv-install'] = False
1473
1474            if self.enable_ppds:
1475                configuration['cups-ppd-install'] = True
1476                configuration['cups-drv-install'] = False
1477            else:
1478                configuration['cups-ppd-install'] = False
1479                configuration['cups-drv-install'] = True
1480
1481        else:  # HPIJS/foomatic
1482            configuration['hpcups-install'] = False
1483            configuration['hpijs-install'] = True
1484            configuration['cups-ppd-install'] = False
1485            configuration['cups-drv-install'] = False
1486
1487            if self.enable_ppds:
1488                configuration['foomatic-ppd-install'] = True
1489                configuration['foomatic-drv-install'] = False
1490            else:
1491                configuration['foomatic-ppd-install'] = False
1492                configuration['foomatic-drv-install'] = True
1493
1494        # ... and then override and adjust for consistency with passed in parameters
1495        if self.enable is not None:
1496            for c in self.enable:
1497                if c == 'hpcups-install':
1498                    configuration['hpijs-install'] = False
1499                    configuration['foomatic-ppd-install'] = False
1500                    configuration['foomatic-drv-install'] = False
1501                elif c == 'hpijs-install':
1502                    configuration['hpcups-install'] = False
1503                    configuration['cups-ppd-install'] = False
1504                    configuration['cups-drv-install'] = False
1505                elif c == 'foomatic-ppd-install':
1506                    configuration['foomatic-drv-install'] = False
1507                elif c == 'foomatic-drv-install':
1508                    configuration['foomatic-ppd-install'] = False
1509                elif c == 'cups-ppd-install':
1510                    configuration['cups-drv-install'] = False
1511                elif c == 'cups-drv-install':
1512                    configuration['cups-ppd-install'] = False
1513
1514        if self.disable is not None:
1515            for c in self.disable:
1516                if c == 'hpcups-install':
1517                    configuration['hpijs-install'] = True
1518                    configuration['cups-ppd-install'] = False
1519                    configuration['cups-drv-install'] = False
1520                elif c == 'hpijs-install':
1521                    configuration['hpcups-install'] = True
1522                    configuration['foomatic-ppd-install'] = False
1523                    configuration['foomatic-drv-install'] = False
1524                elif c == 'foomatic-ppd-install':
1525                    configuration['foomatic-drv-install'] = True
1526                elif c == 'foomatic-drv-install':
1527                    configuration['foomatic-ppd-install'] = True
1528                elif c == 'cups-ppd-install':
1529                    configuration['cups-drv-install'] = True
1530                elif c == 'cups-drv-install':
1531                    configuration['cups-ppd-install'] = True
1532
1533        if self.ppd_dir is not None:
1534            configure_cmd += ' --with-hpppddir=%s' % self.ppd_dir
1535
1536        libdir_path = self.get_distro_ver_data('libdir_path', False)
1537        if libdir_path and self.bitness == 64:
1538            configure_cmd += ' --libdir=%s' % (libdir_path)
1539        elif self.bitness == 64:
1540            configure_cmd += ' --libdir=/usr/lib64'
1541
1542        configure_cmd += ' --prefix=%s' % self.install_location
1543
1544        if self.get_distro_ver_data('cups_path_with_bitness', False) and self.bitness == 64:
1545            configure_cmd += ' --with-cupsbackenddir=/usr/lib64/cups/backend --with-cupsfilterdir=/usr/lib64/cups/filter'
1546
1547        if self.enable is not None:
1548            for c in self.enable:
1549                configuration[c] = True
1550
1551        if self.disable is not None:
1552            for c in self.disable:
1553                configuration[c] = False
1554
1555        for c in configuration:
1556            if configuration[c]:
1557                configure_cmd += ' --enable-%s' % c
1558            else:
1559                configure_cmd += ' --disable-%s' % c
1560
1561        # For AppArmor Profiles
1562        if self.security_package == "AppArmor":
1563            configure_cmd += ' --enable-apparmor_build'
1564        if self.security_package == "SELinux":
1565            configure_cmd += ' --enable-selinux_build'
1566
1567        # For Unit/Functional testing changes.
1568        if ".internal" in prop.version and os.path.exists('testcommon/'):
1569            configure_cmd += ' --enable-hplip_testing_flag'
1570
1571        return configure_cmd
1572
1573    def configure_html(self):
1574        configure_cmd = './configure'
1575        configure_cmd += ' --prefix=/usr'
1576        configure_cmd += ' --with-hpppddir=%s' % self.ppd_dir
1577
1578        libdir_path = self.get_distro_ver_data('libdir_path', False)
1579        if libdir_path and self.bitness == 64:
1580            configure_cmd += ' --libdir=%s' % (libdir_path)
1581        elif self.bitness == 64:
1582            configure_cmd += ' --libdir=/usr/lib64'
1583
1584        self.ui_toolkit = self.get_distro_ver_data('ui_toolkit')
1585        if self.ui_toolkit is not None and self.ui_toolkit == 'qt3':
1586            configure_cmd += ' --enable-qt3 --disable-qt4'
1587        else:
1588            configure_cmd += ' --enable-qt4'
1589
1590        self.native_cups = self.get_distro_ver_data('native_cups')
1591        if self.native_cups is not None and self.native_cups == 1:
1592            if self.enable_ppds:
1593                configure_cmd += ' --enable-hpcups-install --disable-cups-drv-install --enable-cups-ppd-install --disable-hpijs-install --disable-foomatic-drv-install --disable-foomatic-ppd-install --disable-foomatic-rip-hplip-install'
1594            else:
1595                configure_cmd += ' --enable-hpcups-install --enable-cups-drv-install --disable-cups-ppd-install --disable-hpijs-install --disable-foomatic-drv-install --disable-foomatic-ppd-install --disable-foomatic-rip-hplip-install'
1596        else:
1597            configure_cmd += ' --disable-hpcups-install --disable-cups-drv-install --disable-cups-ppd-install --enable-hpijs-install --enable-foomatic-drv-install --enable-foomatic-ppd-install --enable-foomatic-rip-hplip-install'
1598
1599        self.fax_supported = self.get_distro_ver_data('fax_supported')
1600        if self.fax_supported is None:
1601            configure_cmd += ' --disable-fax-build --disable-dbus-build'
1602        else:
1603            configure_cmd += ' --enable-fax-build --enable-dbus-build'
1604
1605        self.network_supported = self.get_distro_ver_data('network_supported')
1606        if self.network_supported is None:
1607            configure_cmd += ' --disable-network-build'
1608        else:
1609            configure_cmd += ' --enable-network-build'
1610
1611        self.scan_supported = self.get_distro_ver_data('scan_supported')
1612        if self.scan_supported is None:
1613            configure_cmd += ' --disable-scan-build'
1614        else:
1615            configure_cmd += ' --enable-scan-build'
1616
1617        self.policykit = self.get_distro_ver_data('policykit')
1618        if self.policykit is not None and self.policykit == 1:
1619            configure_cmd += ' --enable-policykit'
1620        else:
1621            configure_cmd += ' --disable-policykit'
1622
1623        self.libusb01 = self.get_distro_ver_data('libusb01')
1624        if self.libusb01 is not None and self.libusb01 == 1:
1625            configure_cmd += ' --enable-libusb01_build'
1626        else:
1627            configure_cmd += ' --disable-libusb01_build'
1628
1629        self.udev_sysfs_rule = self.get_distro_ver_data('udev_sysfs_rule')
1630        if self.udev_sysfs_rule is not None and self.udev_sysfs_rule == 1:
1631            configure_cmd += ' --enable-udev_sysfs_rules'
1632        else:
1633            configure_cmd += ' --disable-udev_sysfs_rules'
1634
1635        configure_cmd += ' --enable-doc-build'
1636
1637        return configure_cmd
1638
1639    def build_cmds(self, bClassDriver, option):
1640        if option == 'i':
1641            cmnd = "make install"
1642            log.info("cmnd:%s" % cmnd)
1643            return [self.configure(bClassDriver),
1644                    'make clean',
1645                    'make', self.passwordObj.getAuthCmd() % cmnd]
1646
1647        elif option == 't':
1648            cmnd = "make dist"
1649            log.info("cmnd:%s" % cmnd)
1650            return [self.configure(bClassDriver),
1651                    'make clean',
1652                    'make', cmnd]
1653
1654        elif option == 'r':
1655            cmnd = "make rpm"
1656            log.info("cmnd:%s" % cmnd)
1657            return [self.configure(bClassDriver),
1658                    'make clean',
1659                    'make', cmnd]
1660
1661        elif option == 'b':
1662            cmnd = "make deb"
1663            log.info("cmnd:%s" % cmnd)
1664            return [self.configure(bClassDriver),
1665                    'make clean',
1666                    'make', self.passwordObj.getAuthCmd() % cmnd]
1667
1668        elif option == 'd':
1669            return [self.configure(bClassDriver),
1670                    'make clean',
1671                    'make',
1672                    self.passwordObj.getAuthCmd() % 'make install']
1673        else:
1674            return [self.configure(bClassDriver),
1675                    'make clean',
1676                    'make',
1677                    self.passwordObj.getAuthCmd() % 'make install']
1678
1679    def get_distro_ver_data(self, key, default=None, distro_ver=None):
1680        try:
1681            if distro_ver:
1682                return self.distros[self.distro_name]['versions'][distro_ver].get(key, None) or \
1683                    self.distros[self.distro_name].get(key, None) or default
1684            else:
1685                return self.distros[self.distro_name]['versions'][self.distro_version].get(key, None) or \
1686                    self.distros[self.distro_name].get(key, None) or default
1687        except KeyError:
1688            return default
1689
1690        return value
1691
1692    def get_distro_data(self, key, default=None):
1693        try:
1694            return self.distros[self.distro_name].get(key, None) or default
1695        except KeyError:
1696            return default
1697
1698    def get_ver_data(self, key, default=None, distro_ver=None):
1699        try:
1700            if distro_ver:
1701                return self.distros[self.distro_name]['versions'][distro_ver].get(key, None) or default
1702            else:
1703                return self.distros[self.distro_name]['versions'][self.distro_version].get(key, None) or default
1704
1705        except KeyError:
1706            return default
1707
1708        return value
1709
1710    def get_dependency_data(self, dependency, supported_distro_vrs=None):
1711        dependency_cmds = self.get_ver_data(
1712            "dependency_cmds", {}, supported_distro_vrs)
1713        dependency_data = dependency_cmds.get(dependency, {})
1714        packages = dependency_data.get('packages', [])
1715        commands = dependency_data.get('commands', [])
1716        return packages, commands
1717
1718    def get_dependency_commands(self):
1719        dd = list(self.dependencies.keys())
1720        dd.sort()
1721        commands_to_run = []
1722        packages_to_install = []
1723        overall_commands_to_run = []
1724        for d in dd:
1725            include = False
1726            for opt in self.dependencies[d][1]:
1727                if self.selected_options[opt]:
1728                    include = True
1729            if include:
1730                pkgs, cmds = self.get_dependency_data(d)
1731
1732                if pkgs:
1733                    for p in pkgs:
1734                        if not p in packages_to_install:
1735                            packages_to_install.append(p)
1736
1737                if cmds:
1738                    commands_to_run.extend(cmds)
1739
1740        package_mgr_cmd = self.get_distro_ver_data('package_mgr_cmd')
1741
1742        overall_commands_to_run.extend(commands_to_run)
1743
1744        if package_mgr_cmd:
1745            packages_to_install = ' '.join(packages_to_install)
1746            overall_commands_to_run.append(utils.cat(package_mgr_cmd))
1747
1748        if not overall_commands_to_run:
1749            log.error("No cmds/pkgs")
1750
1751        return overall_commands_to_run
1752
1753    def distro_known(self):
1754        return self.distro != DISTRO_UNKNOWN and self.distro_version != DISTRO_VER_UNKNOWN
1755
1756    def distro_supported(self):
1757        if self.mode == MODE_INSTALLER:
1758            return self.distro != DISTRO_UNKNOWN and self.distro_version != DISTRO_VER_UNKNOWN and self.get_ver_data('supported', False)
1759        else:
1760            return True  # For docs (manual install)
1761
1762    def count_num_required_missing_dependencies(self):
1763        num_req_missing = 0
1764        for d, desc, opt in self.missing_required_dependencies():
1765            num_req_missing += 1
1766        return num_req_missing
1767
1768    def count_num_optional_missing_dependencies(self):
1769        num_opt_missing = 0
1770        for d, desc, req, opt in self.missing_optional_dependencies():
1771            num_opt_missing += 1
1772        return num_opt_missing
1773
1774    # missing req. deps for selected components
1775    def missing_required_dependencies(self):
1776        for comp in self.components[self.selected_component][1]:
1777            if self.selected_options[comp]:     # if the component was selected
1778                # dependencies for this component
1779                for dep in self.options[comp][2]:
1780                    # Is it required dependency?
1781                    if self.dependencies[dep][DEPENDENCY_REQUIRED_INDEX]:
1782                        # Is it not already installed?
1783                        if not self.have_dependencies[dep]:
1784                            log.debug("Missing required dependency: %s" % dep)
1785                            yield dep, self.dependencies[dep][DEPENDENCY_DISPLAY_INDEX], comp
1786
1787    def missing_optional_dependencies(self):
1788        for comp in self.components[self.selected_component][1]:
1789            if self.selected_options[comp]:  # if the component was selected
1790                # dependencies for this component
1791                for dep in self.options[comp][2]:
1792                    # Is it optional dependency?
1793                    if not self.dependencies[dep][DEPENDENCY_REQUIRED_INDEX]:
1794                        # Is it not already installed?
1795                        if not self.have_dependencies[dep]:
1796                            log.debug("Missing optional dependency: %s" % dep)
1797                            yield dep, self.dependencies[dep][DEPENDENCY_DISPLAY_INDEX], self.dependencies[dep][0], comp
1798
1799    def select_options(self, answer_callback):
1800        num_opt_missing = 0
1801        # not-required options
1802        for opt in self.components[self.selected_component][1]:
1803            if opt.find("gui_") != -1 and opt.find(self.ui_toolkit) == -1:
1804                continue
1805            if not self.options[opt][0]:  # not required
1806                default = 'y'
1807
1808                if not self.selected_options[opt]:
1809                    default = 'n'
1810
1811                self.selected_options[opt] = answer_callback(
1812                    opt, self.options[opt][1], default)
1813
1814                if self.selected_options[opt]:  # only for options that are ON
1815                    for d in self.options[opt][2]:  # dependencies
1816                        if not self.have_dependencies[d]:  # missing dependency
1817                            log.debug("Missing optional dependency: %s" % d)
1818                            num_opt_missing += 1
1819
1820        return num_opt_missing
1821
1822    def check_wget(self):
1823        if utils.which("wget"):
1824            return True
1825        else:
1826            log.debug("wget is not installed")
1827            return False
1828
1829    def check_avahi_utils(self):
1830        if utils.which("avahi-browse"):
1831            return True
1832        else:
1833            log.debug("avahi-browse is not installed")
1834            return False
1835
1836    '''
1837    def check_passwd_util(self):
1838        if utils.which("gksu"):
1839            return True
1840        elif utils.which("kdesu"):
1841            return True
1842        elif utils.which("kdesudo"):
1843            return True
1844        else:
1845            log.debug("GUI password gksu/kdesu/kdesudo utility is not installed")
1846            return False'''
1847
1848    def run_pre_install(self, callback=None, distro_ver=None):
1849        pre_cmd = self.get_distro_ver_data('pre_install_cmd', None, distro_ver)
1850        log.debug(pre_cmd)
1851        if pre_cmd:
1852            x = 1
1853            for cmd in pre_cmd:
1854                status, output = utils.run(cmd, self.passwordObj)
1855
1856                if status != 0:
1857                    log.warn("An error occurred running '%s'" % cmd)
1858
1859                if callback is not None:
1860                    callback(cmd, "Pre-install step %d" % x)
1861
1862                x += 1
1863
1864            return True
1865
1866        else:
1867            return False
1868
1869    def run_pre_depend(self, callback=None, distro_ver=None):
1870        pre_cmd = self.get_distro_ver_data('pre_depend_cmd', None, distro_ver)
1871        log.debug(pre_cmd)
1872        if pre_cmd:
1873            x = 1
1874            for cmd in pre_cmd:
1875                status, output = utils.run(cmd, self.passwordObj)
1876                if any(['yum' in cmd, 'zypper' in cmd, 'dnf' in cmd, 'pacman' in cmd]):
1877                    if status == 1:
1878                        log.warn("An error occurred running '%s'" % cmd)
1879                else:
1880                    if status != 0:
1881                        log.warn("An error occurred running '%s'" % cmd)
1882
1883                if callback is not None:
1884                    callback(cmd, "Pre-depend step %d" % x)
1885
1886                x += 1
1887
1888    def run_post_depend(self, callback=None, distro_ver=None):
1889        post_cmd = self.get_distro_ver_data(
1890            'post_depend_cmd', None, distro_ver)
1891        log.debug(post_cmd)
1892        if post_cmd:
1893            x = 1
1894            for cmd in post_cmd:
1895                status, output = utils.run(cmd, self.passwordObj)
1896
1897                if status != 0:
1898                    log.warn("An error occurred running '%s'" % cmd)
1899
1900                if callback is not None:
1901                    callback(cmd, "Post-depend step %d" % x)
1902
1903                x += 1
1904
1905    def run_scanjet_depend(self, callback=None, distro_ver=None):
1906        from base.sixext import PY3
1907        if PY3:
1908            scanjet_cmd = self.get_distro_ver_data('scanjet_py3_depend_cmd', None, distro_ver)
1909        else:
1910            scanjet_cmd = self.get_distro_ver_data('scanjet_depend_cmd', None, distro_ver)
1911        log.debug(scanjet_cmd)
1912        if scanjet_cmd:
1913            x = 1
1914            #log.info("x is %d"%x)
1915            for cmd in scanjet_cmd:
1916                #log.info("cmd is %s"%cmd)
1917                if callback is not None:
1918                    callback(cmd, "Scanjet-depend step %d" % x)
1919                status, output = utils.run(cmd, self.passwordObj)
1920                #log.info("status is %d"%status)
1921                if status !=0:
1922                    log.warn("Failed to install this Scanjet dependency package. Some Scanjet features will not work.")
1923                #if callback is not None:
1924                    #callback(cmd, "Scanjet-depend step %d" % x)
1925
1926                x += 1
1927            '''if status !=0:
1928                sys.exit(0)'''
1929
1930    def pre_build(self, distro_ver=None):
1931        cmds = []
1932        if self.get_distro_ver_data('fix_ppd_symlink', False, distro_ver):
1933            cmds.append(self.passwordObj.getAuthCmd() %
1934                        'python ./installer/fix_symlink.py')
1935
1936        return cmds
1937
1938    def run_pre_build(self, callback=None, distro_ver=None):
1939        x = 1
1940        for cmd in self.pre_build(distro_ver):
1941            status, output = utils.run(cmd, self.passwordObj)
1942            if callback is not None:
1943                callback(cmd, "Pre-build step %d" % x)
1944
1945            x += 1
1946
1947    def run_post_build(self, callback=None, distro_ver=None):
1948        x = 1
1949        for cmd in self.post_build(distro_ver):
1950            status, output = utils.run(cmd, self.passwordObj)
1951            if callback is not None:
1952                callback(cmd, "Post-build step %d" % x)
1953
1954            x += 1
1955
1956    def post_build(self, distro_ver=None):
1957        cmds = []
1958        # Reload DBUS configuration if distro supports it and PolicyKit
1959        # support installed
1960        if self.reload_dbus and self.selected_options['policykit']:
1961            cmds.append(self.passwordObj.getAuthCmd() %
1962                        "sh /usr/local/etc/rc.d/dbus reload")
1963            log.debug("Will reload DBUS configuration for PolicyKit support")
1964
1965        # Kill any running hpssd.py instance from a previous install
1966        pid_list = get_ps_pid(['hp-systray', 'hpssd'])
1967
1968        kill_cmd = utils.which("kill", True)
1969        for pid in pid_list:
1970            log.debug("Found %s for %s process" % (pid, pid_list[pid]))
1971            kill = kill_cmd + " %s" % pid
1972            cmds.append(self.passwordObj.getAuthCmd() % kill)
1973
1974        return cmds
1975
1976    def remove_soT(self):
1977        #log.info("\n Clearing unnecessary sos: ")
1978        run_cmd = 'rm -rf .libs/libhpipp.so.0.0.1T .libs/libsane-hpaio.so.1.0.0T .libs/libhpmud.so.0.0.6T .libs/hpmudext.soT .libs/cupsext.soT'
1979        cmd = self.passwordObj.getAuthCmd() % run_cmd
1980        #run_cmd.append(self.passwordObj.getAuthCmd() % "rm -rf .libs/libhpipp.so.0.0.1T .libs/libsane-hpaio.so.1.0.0T .libs/libhpmud.so.0.0.6T .libs/hpmudext.soT .libs/cupsext.soT")
1981         #self.passwordObj.getAuthCmd() % run_cmd
1982        status, output = utils.run(cmd, self.passwordObj)
1983
1984    def remove_hplip(self, callback=None):
1985        failed = True
1986        hplip_remove_cmd = self.get_distro_ver_data('hplip_remove_cmd')
1987        if hplip_remove_cmd:
1988            if callback is not None:
1989                callback(hplip_remove_cmd, "Removing old HPLIP version")
1990
1991            status, output = utils.run(hplip_remove_cmd, self.passwordObj)
1992
1993            if status == 0:
1994                self.hplip_present = self.check_hplip()
1995
1996                if not self.hplip_present:
1997                    failed = False
1998
1999        return failed
2000
2001    def check_password(self):
2002        self.passwordObj.clearPassword()
2003        if self.passwordObj.getPassword() == "":
2004            return False
2005        else:
2006            return True
2007
2008    # PLUGIN HELPERS
2009
2010    def isErrorPage(self, page):
2011        """
2012        Example code from David Mertz' Text Processing in Python.
2013        Released in the Public Domain.
2014        """
2015        err_score = 0.0
2016
2017        for pat, prob in list(err_pats.items()):
2018            if err_score > 0.9:
2019                break
2020            if re.search(pat, page):
2021                err_score += prob
2022
2023        log.debug("File error page score: %f" % (err_score))
2024
2025        return err_score > 0.50
2026
2027    def validate_disto(self):
2028        if self.distro != DISTRO_UNKNOWN:
2029            return True
2030        else:
2031            return True
2032
2033    def validate_distro_version(self):
2034        if self.validate_disto():
2035            for vers in self.distros[self.distro_name]['versions']:
2036                if self.distro_version == vers:
2037                    return True
2038
2039        return False
2040
2041    def is_auto_installer_support(self, distro_version=DISTRO_VER_UNKNOWN):
2042        if not self.distro_name:
2043            self.get_distro()
2044            try:
2045                self.distro_name = self.distros_index[self.distro]
2046            except KeyError:
2047                log.debug("Auto installation is not supported as Distro Name can't find for distro index [%d]." % (
2048                    self.distro))
2049                return False
2050
2051        if distro_version == DISTRO_VER_UNKNOWN:
2052            distro_version = self.distro_version
2053
2054        if self.distro != DISTRO_UNKNOWN and distro_version != DISTRO_VER_UNKNOWN and self.get_ver_data('supported', False, distro_version):
2055            log.debug("Auto installation is supported for Distro =%s version =%s " % (
2056                self.distro_name, distro_version))
2057            return True
2058        else:
2059            log.debug("Auto installation is not supported for Distro =%s version =%s " % (
2060                self.distro_name, distro_version))
2061            return False
2062
2063    # Uninstalls the HPLIP package.
2064    # Input:
2065    #       mode --> INTERACTIVE_MODE, GUI_MODE
2066    #
2067    # Output:
2068    #       result --> returns True on success.
2069    def uninstall(self, mode=INTERACTIVE_MODE, callback=None):
2070        checkSudo = False
2071        if os.getuid() != 0:
2072            checkSudo = True
2073#            log.error("To run 'hp-uninstall' utility, you must have root privileges.")
2074#            return False
2075
2076        home_dir = sys_conf.get("dirs", "home", "")
2077        version = sys_conf.get("hplip", "version", "0.0.0")
2078        if home_dir == "":
2079            log.error("HPLIP is not installed.")
2080            return False
2081
2082        if mode != NON_INTERACTIVE_MODE:
2083            ok, choice = tui.enter_choice(
2084                "\nAre you sure to uninstall HPLIP-%s (y=yes, n=no*)?:" % version, ['y', 'n'], 'n')
2085            if not ok or choice == 'n':
2086                return False
2087
2088        hplip_remove_cmd = self.get_distro_data('hplip_remove_cmd')
2089        log.debug("hplip_remove_cmd =%s " % hplip_remove_cmd)
2090        # read conf file to enter into installed dir
2091        log.info("Starting uninstallation...")
2092
2093        plugin_state = sys_state.get(
2094            'plugin', 'installed', PLUGIN_NOT_INSTALLED)
2095
2096        # check systray is running?
2097        status, output = utils.Is_Process_Running('hp-systray')
2098        if status is True:
2099            if mode != NON_INTERACTIVE_MODE:
2100                ok, choice = tui.enter_choice(
2101                    "\nSome HPLIP applications are running. Press 'y' to close and proceed or Press 'n' to quit uninstall (y=yes*, n=no):", ['y', 'n'], 'y')
2102                if not ok or choice == 'n':
2103                    log.info(
2104                        "Quiting HPLIP unininstallation. Close application(s) manually and run again.")
2105                    return False
2106
2107            # Kill any running hpssd.py instance from a previous install
2108            pid_list = get_ps_pid(['hp-systray'])
2109            kill_cmd = utils.which("kill", True)
2110            for pid in pid_list:
2111                log.debug("Found %s for %s process" % (pid, pid_list[pid]))
2112                kill = kill_cmd + " %s" % pid
2113                sts, out = utils.run(kill)
2114                log.debug("sts =%s out=%s" % (sts, out))
2115
2116        toolbox_status, output = utils.Is_Process_Running('hp-toolbox')
2117        systray_status, output = utils.Is_Process_Running('hp-systray')
2118        if toolbox_status is True or systray_status is True:
2119            log.error(
2120                "Failed to close HP-Toolbox/HP-Systray. Close manually and run hp-uninstall again.")
2121            return False
2122
2123        if hplip_remove_cmd:
2124            User_exit, Is_pkg_mgr_running = self.close_package_managers()
2125            if User_exit:
2126                sys.exit(0)
2127            self.remove_hplip(callback)
2128
2129        # removing .hplip directory
2130        cmd = 'find /home -name .hplip'
2131        if checkSudo:
2132            cmd = self.passwordObj.getAuthCmd() % cmd
2133
2134        status, output = utils.run(cmd, self.passwordObj)
2135        if output is not None:
2136            for p in output.splitlines():
2137                if p.find("find:") != -1:
2138                    continue
2139
2140                utils.remove(p, self.passwordObj, checkSudo)
2141
2142        # Removing Security profiles/policies
2143        package_st = self.security_package_status()
2144        if package_st[1]:
2145            log.debug("Removing Security Profiles")
2146            [utils.remove(f, self.passwordObj, checkSudo)
2147             for f in SEC_DICT[package_st[0]][1]]
2148
2149        # remove the binaries and libraries
2150        pat = re.compile(r"""(\S.*)share\/hplip""")
2151        base = pat.match(home_dir)
2152        usrbin_dir = None
2153        if base is not None:
2154            usrbin_dir = base.group(1) + "bin/"
2155            usrlib_dir = base.group(1) + "lib/"
2156            cnt = 0
2157            BINS_LIST_FULL = utils.expandList(BINS_LIST, usrbin_dir)
2158            while cnt < len(BINS_LIST_FULL):
2159                utils.remove(BINS_LIST_FULL[cnt], self.passwordObj, checkSudo)
2160                cnt += 1
2161
2162            cnt = 0
2163            LIBS_LIST_FULL = utils.expandList(LIBS_LIST, usrlib_dir)
2164            while cnt < len(LIBS_LIST_FULL):
2165                utils.remove(LIBS_LIST_FULL[cnt], self.passwordObj, checkSudo)
2166                cnt += 1
2167
2168        remove_plugins = False
2169        if mode != NON_INTERACTIVE_MODE and plugin_state != PLUGIN_NOT_INSTALLED:
2170            ok, choice = tui.enter_choice(
2171                "\nDo you want to remove HP proprietary plug-ins (y=yes*, n=no)?:", ['y', 'n'], 'y')
2172            if ok and choice == 'y':
2173                remove_plugins = True
2174        else:
2175            remove_plugins = True
2176
2177        # removing HPLIP installed directories/files
2178        if remove_plugins is False:
2179            HPLIP_LIST_FULL = utils.expandList(HPLIP_LIST, home_dir)
2180        else:
2181            HPLIP_LIST_FULL = []
2182        cnt = 0
2183        while cnt < len(HPLIP_LIST_FULL):
2184            utils.remove(HPLIP_LIST_FULL[cnt], self.passwordObj, checkSudo)
2185            cnt += 1
2186
2187        # removing ppd directory
2188        ppd_dir = sys_conf.get('dirs', 'ppd', '')
2189        if ppd_dir:
2190            utils.remove(ppd_dir, self.passwordObj, checkSudo)
2191
2192        # removing configuration files
2193        FILES_LIST_FULL = utils.expandList(FILES_LIST)
2194        cnt = 0
2195        while cnt < len(FILES_LIST_FULL):
2196            utils.remove(FILES_LIST_FULL[cnt], self.passwordObj, checkSudo)
2197            cnt += 1
2198
2199        # removing Rules files
2200        RULES_LIST_FULL = utils.expandList(RULES_LIST, '/etc/udev/rules.d')
2201        for fl in RULES_LIST_FULL:
2202            utils.remove(fl, self.passwordObj, checkSudo)
2203
2204        RULES_LIST_FULL = utils.expandList(RULES_LIST, '/lib/udev/rules.d')
2205        for fl in RULES_LIST_FULL:
2206            utils.remove(fl, self.passwordObj, checkSudo)
2207
2208        # removing (unused) hplip folder from other location
2209        cmd = 'find /usr -type d -name hplip'
2210        cmd = self.passwordObj.getAuthCmd() % cmd
2211        status, output = utils.run(cmd, self.passwordObj, checkSudo)
2212        if status == 0:
2213            hplip_folders = output.splitlines()
2214            for hplip_d in hplip_folders:
2215                if hplip_d != home_dir:
2216                    utils.remove(hplip_d, self.passwordObj, checkSudo)
2217
2218        # removing all hplip extension libraries
2219        for ext_f in HPLIP_EXT_LIST:
2220            if ext_f:
2221                cmd = 'find /usr -type f -name %s -delete' % ext_f
2222                cmd = self.passwordObj.getAuthCmd() % cmd
2223                status, output = utils.run(cmd, self.passwordObj, checkSudo)
2224                if status != 0:
2225                    log.warn(
2226                        "Failed to delete %s library  [%s]" % (ext_f, output))
2227
2228        # removing Plug-in files
2229        if remove_plugins == True:
2230            cnt = 0
2231            PLUGIN_LIST_FULL = utils.expandList(PLUGIN_LIST, home_dir)
2232            while cnt < len(PLUGIN_LIST_FULL):
2233                utils.remove(PLUGIN_LIST_FULL[cnt],
2234                             self.passwordObj, checkSudo)
2235                cnt += 1
2236
2237            cnt = 0
2238            PLUGIN_STATE_FULL = utils.expandList(PLUGIN_STATE)
2239            while cnt < len(PLUGIN_STATE_FULL):
2240                utils.remove(PLUGIN_STATE_FULL[
2241                             cnt], self.passwordObj, checkSudo)
2242                cnt += 1
2243
2244            utils.remove(home_dir, self.passwordObj, checkSudo)
2245
2246        # removing HPLIP uninstall link
2247        if usrbin_dir is not None:
2248            hp_uninstall = usrbin_dir + "hp-unistall"
2249            utils.remove(hp_uninstall, self.passwordObj, checkSudo)
2250
2251        log.info("HPLIP uninstallation is completed")
2252        return True
2253
2254    # close_package_managers() closes the package managers, if running
2255    # Input:
2256    #       MODE --> INTERACTIVE_MODE, GUI_MODE (GUI_MODE is not yet supported).
2257    # Output:
2258    #       User_exit (bool) --> returns True, if user quits.
2259    #                        --> returns False, if user doesn't select quit option.
2260    #       Is_pkg_mgr_running (bool)  -->returns False, if no packages are running at end of function.
2261    #                                  -->returns True, if some package(s) is(are) running at end of function.
2262    def close_package_managers(self, MODE=INTERACTIVE_MODE):
2263        User_exit = False
2264        Is_pkg_mgr_running = False
2265        pid, cmdline = utils.check_pkg_mgr(self.package_mgrs)
2266        while pid:
2267            if MODE == INTERACTIVE_MODE:
2268                ok, user_input = tui.enter_choice(
2269                    "A package manager '%s' appears to be running. Please quit the package manager and press enter to continue (i=ignore, r=retry*, f=force, q=quit) :" % cmdline, ['i', 'r', 'q', 'f'], 'r')
2270                if not ok:
2271                    return True, True  # --> User_exit = True , Is_pkg_mgr_running =True
2272                if user_input == 'i':
2273                    log.warn(
2274                        "Ignoring running package manager. Some package operations may fail.")
2275                    break
2276                if user_input == 'f':
2277                    ok, ans = tui.enter_yes_no(
2278                        "\nForce quit of package manager '%s'" % cmdline, 'y')
2279                    if not ok:
2280                        return True, True  # --> User_exit = True , Is_pkg_mgr_running =True
2281                    if ans:
2282                        cmd = self.passwordObj.getAuthCmd() % ("kill %s" % pid)
2283                        status, output = utils.run(cmd, self.passwordObj)
2284                        if status != 0:
2285                            log.error(
2286                                "Failed to kill process. You may need to manually quit the program.")
2287            else:
2288                log.error("GUI is not yet supported")
2289                # TBD
2290                break
2291            pid, cmdline = utils.check_pkg_mgr(self.package_mgrs)
2292
2293        if pid:     # checking for still running package managers
2294            Is_pkg_mgr_running = True
2295
2296        return User_exit, Is_pkg_mgr_running
2297
2298    # add_groups_to_user()
2299    # Input:
2300    #      missing_user_groups (string) --> Contains only missing groups, to show to user.
2301    #      missing_user_groups_cmd (string) --> command as per distro.dat (i.e. add_user_to_group)
2302    #      mode  --> INTERACTIVE_MODE, GUI_MODE (GUI_MODE is not yet supported).
2303    # Output:
2304    #      ret_val (bool) --> returns True, if succeeded to add groups to user.
2305    #                     --> returns False, if Failed to add groups to user.
2306    # Move to utils
2307    def add_groups_to_user(self, missing_user_groups, missing_user_groups_cmd, mode=INTERACTIVE_MODE):
2308        ret_val = False
2309        if mode == INTERACTIVE_MODE:
2310            if not missing_user_groups or not missing_user_groups_cmd:
2311                return False
2312
2313            ok, user_input = tui.enter_choice(
2314                "Do you want to add missing groups %s to user?(y=yes*, n=no)" % missing_user_groups, ['y', 'n'], 'y')
2315            if ok and user_input == 'y':
2316                usermod_path = utils.which('usermod', True)
2317                if usermod_path:
2318                    cmd = "%s %s %s" % (
2319                        usermod_path, missing_user_groups_cmd, prop.username)
2320                    cmd = self.passwordObj.getAuthCmd() % cmd
2321                    log.debug("cmd =  %s" % cmd)
2322                    sts, out = utils.run(cmd, self.passwordObj)
2323                    if sts == 0:
2324                        ret_val = True
2325                else:
2326                    log.error("usermod command not found.")
2327            else:
2328                log.info(log.bold("Please add %s groups to %s user" %
2329                                  (missing_user_groups, prop.username)))
2330        else:
2331            log.error("GUI is not yet supported")
2332            # TBD
2333        return ret_val
2334
2335    # disable_SELinux()
2336    # Input:
2337    #      MODE  --> INTERACTIVE_MODE, GUI_MODE (GUI_MODE is not yet supported).
2338    # Output:
2339    #      ret_val (bool) --> returns True, if succeeded to disable SELinux.
2340    #                     --> returns False, if Failed to disable SELinux.
2341
2342    def disable_SELinux(self, mode=INTERACTIVE_MODE):       # Move to utils
2343        ret_val = False
2344        SELinux_file = '/etc/selinux/config'
2345        if mode == INTERACTIVE_MODE:
2346            ok, user_input = tui.enter_choice(
2347                "SELinux is currently enabled in your system. Device may not work properly. Do you want to disable SELinux?(y=yes, n=no*)", ['y', 'n'], 'n')
2348            if ok and user_input != 'n':
2349                if not os.path.exists(SELinux_file):
2350                    log.debug("File %s is not found" % SELinux_file)
2351                    return False
2352                cmd = self.passwordObj.getAuthCmd() % (
2353                    "vi -c %s/enforcing$/disabled -c wq " + SELinux_file)
2354                log.debug("cmd= %s " % cmd)
2355                sts, out = utils.run(cmd, self.passwordObj)
2356                if sts == 0:
2357                    ret_val = True
2358                if os.path.exists('/selinux/enforce'):
2359                    cmd = "echo 0 >/selinux/enforce"
2360                    cmd = self.passwordObj.getAuthCmd() % cmd
2361                    log.debug("cmd= %s " % cmd)
2362#                    utils.run(cmd, self.passwordObj)
2363                    os_utils.execute(cmd)
2364
2365        else:
2366            log.error("GUI is not yet supported")
2367            # TBD
2368
2369        return ret_val
2370
2371    # Move to core_install
2372    def install_missing_dependencies(self, mode=INTERACTIVE_MODE, required_dependencies=[], optional_dependencies=[], missing_cmd=[]):
2373        package_mgr_cmd = self.get_distro_ver_data('package_mgr_cmd')
2374        pre_depend_cmd = self.get_distro_ver_data('pre_depend_cmd')
2375        overall_install_cmds = {}
2376
2377        if len(required_dependencies):
2378            for packages_to_install in required_dependencies:
2379                if package_mgr_cmd:
2380                    overall_install_cmds[
2381                        packages_to_install] = utils.cat(package_mgr_cmd)
2382                else:
2383                    overall_install_cmds[
2384                        packages_to_install] = packages_to_install
2385
2386        if len(optional_dependencies):
2387            for packages_to_install in optional_dependencies:
2388                if package_mgr_cmd:
2389                    overall_install_cmds[
2390                        packages_to_install] = utils.cat(package_mgr_cmd)
2391                else:
2392                    overall_install_cmds[
2393                        packages_to_install] = packages_to_install
2394
2395        if len(overall_install_cmds) == 0 and len(missing_cmd) == 0:
2396            log.info("No missing dependencies")
2397            return 0
2398
2399        if mode == INTERACTIVE_MODE:
2400            ok, user_input = tui.enter_choice(
2401                "Do you want to update repository and Install missing/incompatible packages. (a=install all*, c=custom_install, s=skip):", ['a', 'c', 's'], 'a')
2402            if not ok or user_input == 'q':
2403                return 1
2404            elif user_input == 's':
2405                log.info(
2406                    log.bold("Install manually above missing/incompatible packages."))
2407            else:
2408                self.close_package_managers()
2409
2410                log.info(log.bold("Updating repository"))
2411                log.info(log.bold('-' * len("Updating repository")))
2412                if pre_depend_cmd:
2413                    for cmd in pre_depend_cmd:
2414                        log.info("cmd =%s" % (cmd))
2415                        sts, out = utils.run(cmd, self.passwordObj)
2416                        if sts != 0 or "Failed" in out:
2417                            log.warn(
2418                                "Failed to update Repository, check if any update/installation is running.")
2419
2420                if user_input == 'c':
2421                    log.info(log.bold("Installing missing/incompatible packages"))
2422                    log.info(
2423                        log.bold('-' * len("Installing missing/incompatible packages")))
2424                    for d in overall_install_cmds:
2425                        ok, user_input = tui.enter_choice(
2426                            "Do you want to install '%s' package?(y=yes*, n=no):" % d, ['y', 'n'], 'y')
2427                        if ok and user_input == 'y':
2428                            if 'hpaio' in overall_install_cmds[d]:
2429                                self.update_hpaio()
2430                            else:
2431                                log.info("cmd =%s" % overall_install_cmds[d])
2432                                sts, out = utils.run(overall_install_cmds[
2433                                                     d], self.passwordObj)
2434                                if sts != 0 or "Failed" in out:
2435                                    log.error(
2436                                        "Failed to install '%s' package, please install manually. " % d)
2437                    if 'cups' in d:
2438                        if not services.start_service('cups', self.passwordObj):
2439                            log.error(
2440                                "Failed to start CUPS service. Please start CUPS manually or restart system.")
2441                    for cmd in missing_cmd:
2442                        ok, user_input = tui.enter_choice(
2443                            "Do you want to run '%s' command?(y=yes*, n=no):" % d, ['y', 'n'], 'y')
2444                        if ok and user_input == 'y':
2445                            sts, out = utils.run(cmd, self.passwordObj)
2446                            if sts != 0 or "Failed" in out:
2447                                log.error(
2448                                    "Failed to run '%s' command, please run manually. " % d)
2449
2450                elif user_input == 'a':
2451                    log.info(log.bold("Installing Missing/Incompatible packages"))
2452                    log.info(
2453                        log.bold('-' * len("Installing Missing/Incompatible packages")))
2454                    for d in overall_install_cmds:
2455                        if 'hpaio' in overall_install_cmds[d]:
2456                            self.update_hpaio()
2457                        else:
2458                            log.info("cmd =%s" % overall_install_cmds[d])
2459                            sts, out = utils.run(overall_install_cmds[
2460                                                 d], self.passwordObj)
2461                            if sts != 0 or "Failed" in out:
2462                                log.error(
2463                                    "Failed to install '%s' package, please install manually. " % d)
2464                    if 'cups' in d:
2465                        if not services.start_service('cups', self.passwordObj):
2466                            log.error(
2467                                "Failed to start CUPS sevice. Please start CUPS manually or restart system.")
2468                    for cmd in missing_cmd:
2469                        sts, out = utils.run(cmd, self.passwordObj)
2470                        if sts != 0 or "Failed" in out:
2471                            log.error(
2472                                "Failed to run '%s' command, please run manually. " % d)
2473
2474        else:
2475            log.error("GUI is not yet supported..1")
2476            # TBD
2477
2478        return 0
2479