home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / hplip / installer / core_install.py < prev    next >
Encoding:
Python Source  |  2009-04-14  |  61.6 KB  |  1,819 lines

  1. # -*- coding: utf-8 -*-
  2. #
  3. # (c) Copyright 2003-2009 Hewlett-Packard Development Company, L.P.
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  18. #
  19. # Author: Don Welch
  20. #
  21.  
  22. # Std Lib
  23. import sys
  24. import os
  25. import os.path
  26. import re
  27. import time
  28. import cStringIO
  29. import grp
  30. import pwd
  31. import tarfile
  32.  
  33. try:
  34.     import hashlib # new in 2.5
  35.  
  36.     def get_checksum(s):
  37.         return hashlib.sha1(s).hexdigest()
  38.  
  39. except ImportError:
  40.     import sha # deprecated in 2.6/3.0
  41.  
  42.     def get_checksum(s):
  43.         return sha.new(s).hexdigest()
  44.  
  45.  
  46. import urllib # TODO: Replace with urllib2 (urllib is deprecated in Python 3.0)
  47.  
  48.  
  49. # Local
  50. from base.g import *
  51. from base.codes import *
  52. from base import utils, pexpect
  53. from dcheck import *
  54.  
  55.  
  56.  
  57. DISTRO_UNKNOWN = 0
  58. DISTRO_VER_UNKNOWN = '0.0'
  59.  
  60. MODE_INSTALLER = 0 # hplip-install/hp-setup
  61. MODE_CHECK = 1 # hp-check
  62. MODE_CREATE_DOCS = 2 # create_docs
  63.  
  64. TYPE_STRING = 1
  65. TYPE_LIST = 2
  66. TYPE_BOOL = 3
  67. TYPE_INT = 4
  68.  
  69. DEPENDENCY_RUN_TIME = 1
  70. DEPENDENCY_COMPILE_TIME = 2
  71. DEPENDENCY_RUN_AND_COMPILE_TIME = 3
  72.  
  73. PING_TARGET = "www.google.com"
  74. HTTP_GET_TARGET = "http://www.google.com"
  75.  
  76. PASSWORD_LIST = [
  77.     pexpect.EOF, # 0
  78.     pexpect.TIMEOUT, # 1
  79.     "passwor[dt]", # en/de/it/ru
  80.     "kennwort", # de?
  81.     "password for", # en
  82.     "mot de passe", # fr
  83.     "contrase√±a", # es
  84.     "palavra passe", # pt
  85.     "Â裉ª§", # zh
  86.     "wachtwoord", # nl
  87.     "heslo", # czech
  88. ]
  89.  
  90. PASSWORD_EXPECT_LIST = []
  91. for s in PASSWORD_LIST:
  92.     try:
  93.         p = re.compile(s, re.I)
  94.     except TypeError:
  95.         PASSWORD_EXPECT_LIST.append(s)
  96.     else:
  97.         PASSWORD_EXPECT_LIST.append(p)
  98.  
  99. OK_PROCESS_LIST = ['adpept-notifier',
  100.                    'yum-updatesd',
  101.                    ]
  102.  
  103. CONFIGURE_ERRORS = { 1 : "General/unknown error",
  104.                      2 : "libusb not found",
  105.                      3 : "cups-devel not found",
  106.                      4 : "libnetsnmp not found",
  107.                      5 : "netsnmp-devel not found",
  108.                      6 : "python-devel not found",
  109.                      7 : "pthread-devel not found",
  110.                      8 : "ppdev-devel not found",
  111.                      9 : "libcups not found",
  112.                      10 : "libm not found",
  113.                      11 : "libusb-devel not found",
  114.                      12 : "sane-backends-devel not found",
  115.                      13 : "libdbus not found",
  116.                      14 : "dbus-devel not found",
  117.                      15 : "fax requires dbus support",
  118.                      102 : "libjpeg not found",
  119.                      103 : "jpeg-devel not found",
  120.                      104 : "libdi not found",
  121.                    }
  122.  
  123.  
  124. try:
  125.     from functools import update_wrapper
  126. except ImportError: # using Python version < 2.5
  127.     def trace(f):
  128.         def newf(*args, **kw):
  129.            log.debug("TRACE: func=%s(), args=%s, kwargs=%s" % (f.__name__, args, kw))
  130.            return f(*args, **kw)
  131.         newf.__name__ = f.__name__
  132.         newf.__dict__.update(f.__dict__)
  133.         newf.__doc__ = f.__doc__
  134.         newf.__module__ = f.__module__
  135.         return newf
  136. else: # using Python 2.5+
  137.     def trace(f):
  138.         def newf(*args, **kw):
  139.             log.debug("TRACE: func=%s(), args=%s, kwargs=%s" % (f.__name__, args, kw))
  140.             return f(*args, **kw)
  141.         return update_wrapper(newf, f)
  142.  
  143.  
  144.  
  145. class CoreInstall(object):
  146.     def __init__(self, mode=MODE_INSTALLER, ui_mode=INTERACTIVE_MODE, ui_toolkit='qt4'):
  147.         os.umask(0022)
  148.         self.mode = mode
  149.         self.ui_mode = ui_mode
  150.         self.password = ''
  151.         self.version_description, self.version_public, self.version_internal = '', '', ''
  152.         self.bitness = 32
  153.         self.endian = utils.LITTLE_ENDIAN
  154.         self.distro, self.distro_name, self.distro_version = DISTRO_UNKNOWN, '', DISTRO_VER_UNKNOWN
  155.         self.distro_version_supported = False
  156.         self.install_location = '/usr'
  157.         self.hpoj_present = False
  158.         self.hplip_present = False
  159.         self.have_dependencies = {}
  160.         self.cups11 = False
  161.         self.hpijs_build = False
  162.         self.ppd_dir = None
  163.         self.drv_dir = None
  164.         self.distros = {}
  165.         self.logoff_required = False
  166.         self.restart_required = False
  167.         self.network_connected = False
  168.         self.ui_toolkit = ui_toolkit
  169.         self.enable = None
  170.         self.disable = None
  171.         self.plugin_path = os.path.join(prop.home_dir, "data", "plugin")
  172.         self.plugin_version = '0.0.0'
  173.         self.plugin_name = ''
  174.  
  175.  
  176.         self.FIELD_TYPES = {
  177.             'distros' : TYPE_LIST,
  178.             'index' : TYPE_INT,
  179.             'versions' : TYPE_LIST,
  180.             'display_name' : TYPE_STRING,
  181.             'alt_names': TYPE_LIST,
  182.             'display': TYPE_BOOL,
  183.             'notes': TYPE_STRING,
  184.             'package_mgrs': TYPE_LIST,
  185.             'package_mgr_cmd':TYPE_STRING,
  186.             'pre_install_cmd': TYPE_LIST,
  187.             'pre_depend_cmd': TYPE_LIST,
  188.             'post_depend_cmd': TYPE_LIST,
  189.             'hpoj_remove_cmd': TYPE_STRING,
  190.             'hplip_remove_cmd': TYPE_STRING,
  191.             'su_sudo': TYPE_STRING,
  192.             'ppd_install': TYPE_STRING,
  193.             'udev_mode_fix': TYPE_BOOL,
  194.             'ppd_dir': TYPE_STRING,
  195.             'drv_dir' : TYPE_STRING,
  196.             'fix_ppd_symlink': TYPE_BOOL,
  197.             'code_name': TYPE_STRING,
  198.             'supported': TYPE_BOOL, # Supported by installer
  199.             'release_date': TYPE_STRING,
  200.             'packages': TYPE_LIST,
  201.             'commands': TYPE_LIST,
  202.             'same_as_version' : TYPE_STRING,
  203.             'gui_supported' : TYPE_BOOL,
  204.             'scan_supported' : TYPE_BOOL,
  205.             'fax_supported' : TYPE_BOOL,
  206.             'pcard_supported' : TYPE_BOOL,
  207.             'network_supported' : TYPE_BOOL,
  208.             'parallel_supported' : TYPE_BOOL,
  209.             'usb_supported' : TYPE_BOOL,
  210.             'packaged_version': TYPE_STRING, # Version of HPLIP pre-packaged in distro
  211.             'cups_path_with_bitness' : TYPE_BOOL,
  212.             'ui_toolkit' : TYPE_STRING,  # qt3 or qt4 [or gtk]
  213.  
  214.         }
  215.  
  216.         # components
  217.         # 'name': ('description', [<option list>])
  218.         self.components = {
  219.             'hplip': ("HP Linux Imaging and Printing System", ['base', 'network', 'gui_qt3', 'gui_qt4',
  220.                                                                'fax', 'scan', 'parallel', 'docs']),
  221.             'hpijs': ("HP IJS Printer Driver", ['hpijs', 'hpijs-cups'])
  222.         }
  223.  
  224.         self.selected_component = 'hplip'
  225.  
  226.         # options
  227.         # name: (<required>, "<display_name>", [<dependency list>]), ...
  228.         self.options = {
  229.             'base':     (True,  'Required HPLIP base components', []), # HPLIP
  230.             'network' : (False, 'Network/JetDirect I/O', []),
  231.             'gui_qt3' : (False, 'Graphical User Interfaces (Qt3)', []),
  232.             'gui_qt4' : (False, 'Graphical User Interfaces (Qt4)', []),
  233.             'fax' :     (False, 'PC Send Fax support', []),
  234.             'scan':     (False, 'Scanning support', []),
  235.             'parallel': (False, 'Parallel I/O (LPT)', []),
  236.             'docs':     (False, 'HPLIP documentation (HTML)', []),
  237.  
  238.             # hpijs only
  239.             'hpijs':       (True,  'Required HPIJS base components', []),
  240.             'hpijs-cups' : (False, 'CUPS support for HPIJS', []),
  241.         }
  242.  
  243.  
  244.         # holds whether the user has selected (turned on each option)
  245.         # initial values are defaults (for GUI only)
  246.         self.selected_options = {
  247.             'base':        True,
  248.             'network':     True,
  249.             'gui_qt3':     False,
  250.             'gui_qt4':     True,
  251.             'fax':         True,
  252.             'scan':        True,
  253.             'parallel':    False,
  254.             'docs':        True,
  255.  
  256.             # hpijs only
  257.             'hpijs':       True,
  258.             'hpijs-cups' : True,
  259.         }
  260.  
  261.         # dependencies
  262.         # 'name': (<required for option>, [<option list>], <display_name>, <check_func>, <runtime/compiletime>), ...
  263.         # Note: any change to the list of dependencies must be reflected in base/distros.py
  264.         self.dependencies = {
  265.             # Required base packages
  266.             'libjpeg':          (True,  ['base', 'hpijs'], "libjpeg - JPEG library", self.check_libjpeg, DEPENDENCY_RUN_AND_COMPILE_TIME),
  267.             'libtool':          (True,  ['base'], "libtool - Library building support services", self.check_libtool, DEPENDENCY_COMPILE_TIME),
  268.             'cups' :            (True,  ['base', 'hpijs-cups'], 'CUPS - Common Unix Printing System', self.check_cups, DEPENDENCY_RUN_TIME),
  269.             'cups-devel':       (True,  ['base'], 'CUPS devel- Common Unix Printing System development files', self.check_cups_devel, DEPENDENCY_COMPILE_TIME),
  270.             'gcc' :             (True,  ['base', 'hpijs'], 'gcc - GNU Project C and C++ Compiler', self.check_gcc, DEPENDENCY_COMPILE_TIME),
  271.             'make' :            (True,  ['base', 'hpijs'], "make - GNU make utility to maintain groups of programs", self.check_make, DEPENDENCY_COMPILE_TIME),
  272.             'python-devel' :    (True,  ['base'], "Python devel - Python development files", self.check_python_devel, DEPENDENCY_COMPILE_TIME),
  273.             'libpthread' :      (True,  ['base'], "libpthread - POSIX threads library", self.check_libpthread, DEPENDENCY_RUN_AND_COMPILE_TIME),
  274.             'python2x':         (True,  ['base'], "Python 2.2 or greater - Python programming language", self.check_python2x, DEPENDENCY_RUN_AND_COMPILE_TIME),
  275.             'python-xml'  :     (True, ['base'], "Python XML libraries", self.check_python_xml, DEPENDENCY_RUN_TIME),
  276.             'gs':               (True,  ['base', 'hpijs'], "GhostScript - PostScript and PDF language interpreter and previewer", self.check_gs, DEPENDENCY_RUN_TIME),
  277.             'libusb':           (True,  ['base'], "libusb - USB library", self.check_libusb, DEPENDENCY_RUN_AND_COMPILE_TIME),
  278.  
  279.  
  280.  
  281.             # Optional base packages
  282.             'cups-ddk':          (False, ['base'], "CUPS DDK - CUPS driver development kit", self.check_cupsddk, DEPENDENCY_RUN_TIME), # req. for .drv PPD installs
  283.  
  284.             # Required scan packages
  285.             'sane':             (True,  ['scan'], "SANE - Scanning library", self.check_sane, DEPENDENCY_RUN_TIME),
  286.             'sane-devel' :      (True,  ['scan'], "SANE - Scanning library development files", self.check_sane_devel, DEPENDENCY_COMPILE_TIME),
  287.  
  288.             # Optional scan packages
  289.             'xsane':            (False, ['scan'], "xsane - Graphical scanner frontend for SANE", self.check_xsane, DEPENDENCY_RUN_TIME),
  290.             'scanimage':        (False, ['scan'], "scanimage - Shell scanning program", self.check_scanimage, DEPENDENCY_RUN_TIME),
  291.             'pil':              (False, ['scan'], "PIL - Python Imaging Library (required for commandline scanning with hp-scan)", self.check_pil, DEPENDENCY_RUN_TIME),
  292.  
  293.             # Required fax packages
  294.             'python23':         (True,  ['fax'], "Python 2.3 or greater - Required for fax functionality", self.check_python23, DEPENDENCY_RUN_TIME),
  295.             'dbus':             (True,  ['fax'], "DBus - Message bus system", self.check_dbus, DEPENDENCY_RUN_AND_COMPILE_TIME),
  296.             'python-dbus':      (True,  ['fax'], "Python DBus - Python bindings for DBus", self.check_python_dbus, DEPENDENCY_RUN_TIME),
  297.             'python-ctypes':    (True,  ['fax', 'gui_qt3'], "Python ctypes - A foreign function library for Python", self.check_python_ctypes, DEPENDENCY_RUN_TIME),
  298.  
  299.             # Optional fax packages
  300.             'reportlab':        (False, ['fax'], "Reportlab - PDF library for Python", self.check_reportlab, DEPENDENCY_RUN_TIME),
  301.  
  302.             # Required parallel I/O packages
  303.             'ppdev':            (True,  ['parallel'], "ppdev - Parallel port support kernel module.", self.check_ppdev, DEPENDENCY_RUN_TIME),
  304.  
  305.             # Required qt3 packages
  306.             'pyqt':             [True,  ['gui_qt3'], "PyQt 3- Qt interface for Python (for Qt version 3.x)", self.check_pyqt, DEPENDENCY_RUN_TIME], # PyQt 3.x
  307.  
  308.             # Required qt4 packages
  309.             'pyqt4':            [True,  ['gui_qt4'], "PyQt 4- Qt interface for Python (for Qt version 4.x)", self.check_pyqt4, DEPENDENCY_RUN_TIME], # PyQt 4.x )
  310.             'pyqt4-dbus' :      [True,  ['gui_qt4'], "PyQt 4 DBus - DBus Support for PyQt4", self.check_pyqt4_dbus, DEPENDENCY_RUN_TIME],
  311.  
  312.             # Required network I/O packages
  313.             'libnetsnmp-devel': (True,  ['network'], "libnetsnmp-devel - SNMP networking library development files", self.check_libnetsnmp, DEPENDENCY_RUN_AND_COMPILE_TIME),
  314.             'libcrypto':        (True,  ['network'], "libcrypto - OpenSSL cryptographic library", self.check_libcrypto, DEPENDENCY_RUN_AND_COMPILE_TIME),
  315.         }
  316.  
  317.         for opt in self.options:
  318.             update_spinner()
  319.             for d in self.dependencies:
  320.                 if opt in self.dependencies[d][1]:
  321.                     self.options[opt][2].append(d)
  322.  
  323.         self.load_distros()
  324.  
  325.         self.distros_index = {}
  326.         for d in self.distros:
  327.             self.distros_index[self.distros[d]['index']] = d
  328.  
  329.  
  330.     def init(self, callback=None):
  331.         if callback is not None:
  332.             callback("Init...\n")
  333.  
  334.         update_spinner()
  335.  
  336.         # Package manager names
  337.         self.package_mgrs = []
  338.         for d in self.distros:
  339.             update_spinner()
  340.  
  341.             for a in self.distros[d].get('package_mgrs', []):
  342.                 if a and a not in self.package_mgrs:
  343.                     self.package_mgrs.append(a)
  344.  
  345.         self.version_description, self.version_public, self.version_internal = self.get_hplip_version()
  346.         log.debug("HPLIP Description=%s Public version=%s Internal version = %s"  %
  347.             (self.version_description, self.version_public, self.version_internal))
  348.  
  349.         # have_dependencies
  350.         # is each dependency satisfied?
  351.         # start with each one 'No'
  352.         for d in self.dependencies:
  353.             update_spinner()
  354.             self.have_dependencies[d] = False
  355.  
  356.         self.get_distro()
  357.         self.distro_changed()
  358.  
  359.         if callback is not None:
  360.             callback("Distro: %s\n" % self.distro)
  361.  
  362.         self.check_dependencies(callback)
  363.  
  364.         for d in self.dependencies:
  365.             update_spinner()
  366.  
  367.             log.debug("have %s = %s" % (d, self.have_dependencies[d]))
  368.  
  369.             if callback is not None:
  370.                 callback("Result: %s = %s\n" % (d, self.have_dependencies[d]))
  371.  
  372.         pid, cmdline = self.check_pkg_mgr()
  373.         if pid:
  374.             log.debug("Running package manager: %s (%d)" % (cmdline, pid) )
  375.  
  376.         self.bitness = utils.getBitness()
  377.         log.debug("Bitness = %d" % self.bitness)
  378.  
  379.         update_spinner()
  380.  
  381.         self.endian = utils.getEndian()
  382.         log.debug("Endian = %d" % self.endian)
  383.  
  384.         update_spinner()
  385.  
  386.         self.distro_name = self.distros_index[self.distro]
  387.         self.distro_version_supported = self.get_distro_ver_data('supported', False)
  388.  
  389.         log.debug("Distro = %s Distro Name = %s Display Name= %s Version = %s Supported = %s" %
  390.             (self.distro, self.distro_name, self.distros[self.distro_name]['display_name'],
  391.              self.distro_version, self.distro_version_supported))
  392.  
  393.         self.hpoj_present = self.check_hpoj()
  394.         log.debug("HPOJ = %s" % self.hpoj_present)
  395.  
  396.         update_spinner()
  397.  
  398.         self.hplip_present = self.check_hplip()
  399.         log.debug("HPLIP (prev install) = %s" % self.hplip_present)
  400.  
  401.         status, output = self.run('cups-config --version')
  402.         self.cups_ver = output.strip()
  403.         log.debug("CUPS version = %s" % self.cups_ver)
  404.  
  405.         self.cups11 = output.startswith('1.1')
  406.         log.debug("Is CUPS 1.1.x? %s" % self.cups11)
  407.  
  408.         status, self.sys_uname_info = self.run('uname -a')
  409.         self.sys_uname_info = self.sys_uname_info.replace('\n', '')
  410.         log.debug(self.sys_uname_info)
  411.  
  412.         #self.distro_changed()
  413.  
  414.         # Record the installation time/date and version.
  415.         # Also has the effect of making the .hplip.conf file user r/w
  416.         # on the 1st run so that running hp-setup as root doesn't lock
  417.         # the user out of owning the file
  418.         user_conf.set('installation', 'date_time', time.strftime("%x %H:%M:%S", time.localtime()))
  419.         user_conf.set('installation', 'version', self.version_public)
  420.  
  421.         if callback is not None:
  422.             callback("Done")
  423.  
  424.  
  425.     def init_for_docs(self, distro_name, version, bitness=32):
  426.         self.distro_name = distro_name
  427.         self.distro_version = version
  428.  
  429.         try:
  430.             self.distro = self.distros[distro_name]['index']
  431.         except KeyError:
  432.             log.error("Invalid distro name: %s" % distro_name)
  433.             sys.exit(1)
  434.  
  435.         self.bitness = bitness
  436.  
  437.         for d in self.dependencies:
  438.             self.have_dependencies[d] = True
  439.  
  440.         self.enable_ppds = self.get_distro_ver_data('ppd_install', 'ppd') == 'ppd'
  441.         self.ppd_dir = self.get_distro_ver_data('ppd_dir')
  442.         self.drv_dir = self.get_distro_ver_data('drv_dir')
  443.  
  444.         self.distro_version_supported = True # for manual installs
  445.  
  446.  
  447.     def check_dependencies(self, callback=None):
  448.         update_ld_output()
  449.  
  450.         for d in self.dependencies:
  451.             update_spinner()
  452.  
  453.             log.debug("Checking for dependency '%s'...\n" % d)
  454.  
  455.             if callback is not None:
  456.                 callback("Checking: %s\n" % d)
  457.  
  458.             self.have_dependencies[d] = self.dependencies[d][3]()
  459.             log.debug("have %s = %s" % (d, self.have_dependencies[d]))
  460.  
  461.         cleanup_spinner()
  462.  
  463.  
  464.     def password_func(self):
  465.         if self.password:
  466.             return self.password
  467.         elif self.ui_mode == INTERACTIVE_MODE:
  468.             import getpass
  469.             return getpass.getpass("Enter password: ")
  470.         else:
  471.             return ''
  472.  
  473.  
  474.     def run(self, cmd, callback=None, timeout=300):
  475.         if cmd is None:
  476.             return 1, ''
  477.         output = cStringIO.StringIO()
  478.         ok, ret = False, ''
  479.         # Hack! TODO: Fix!
  480.         check_timeout = not (cmd.startswith('xterm') or cmd.startswith('gnome-terminal'))
  481.  
  482.         try:
  483.             child = pexpect.spawn(cmd, timeout=1)
  484.         except pexpect.ExceptionPexpect:
  485.             return 1, ''
  486.  
  487.         try:
  488.             try:
  489.                 start = time.time()
  490.  
  491.                 while True:
  492.                     update_spinner()
  493.  
  494.                     i = child.expect_list(PASSWORD_EXPECT_LIST)
  495.  
  496.                     cb = child.before
  497.                     if cb:
  498.                         # output
  499.                         start = time.time()
  500.                         log.log_to_file(cb)
  501.                         log.debug(cb)
  502.                         output.write(cb)
  503.  
  504.                         if callback is not None:
  505.                             if callback(cb): # cancel
  506.                                 break
  507.  
  508.                     elif check_timeout:
  509.                         # no output
  510.                         span = int(time.time()-start)
  511.  
  512.                         if span:
  513.                             if span % 5 == 0:
  514.                                 log.debug("No output seen in %d secs" % span)
  515.  
  516.                             if span > timeout:
  517.                                 log.error("No output seen in over %d sec... (Is the CD-ROM/DVD source repository enabled? It shouldn't be!)" % timeout)
  518.                                 child.close()
  519.                                 child.terminate(force=True)
  520.                                 break
  521.  
  522.                     if i == 0: # EOF
  523.                         ok, ret = True, output.getvalue()
  524.                         break
  525.  
  526.                     elif i == 1: # TIMEOUT
  527.                         continue
  528.  
  529.                     else: # password
  530.                         child.sendline(self.password)
  531.  
  532.             except (Exception, pexpect.ExceptionPexpect):
  533.                 log.exception()
  534.  
  535.         finally:
  536.             cleanup_spinner()
  537.  
  538.             try:
  539.                 child.close()
  540.             except OSError:
  541.                 pass
  542.  
  543.         if ok:
  544.             return child.exitstatus, ret
  545.         else:
  546.             return 1, ''
  547.  
  548.  
  549.     def get_distro(self):
  550.         log.debug("Determining distro...")
  551.         self.distro, self.distro_version = DISTRO_UNKNOWN, '0.0'
  552.  
  553.         found = False
  554.  
  555.         lsb_release = utils.which("lsb_release")
  556.  
  557.         if lsb_release:
  558.             log.debug("Using 'lsb_release -is/-rs'")
  559.             cmd = os.path.join(lsb_release, "lsb_release")
  560.             status, name = self.run(cmd + ' -is')
  561.             name = name.lower().strip()
  562.             log.debug("Distro name=%s" % name)
  563.  
  564.             if not status and name:
  565.                 status, ver = self.run(cmd + ' -rs')
  566.                 ver = ver.lower().strip()
  567.                 log.debug("Distro version=%s" % ver)
  568.  
  569.                 if not status and ver:
  570.                     for d in self.distros:
  571.                         if name.find(d) > -1:
  572.                             self.distro = self.distros[d]['index']
  573.                             found = True
  574.                             self.distro_version = ver
  575.                             break
  576.  
  577.         if not found:
  578.             try:
  579.                 name = file('/etc/issue', 'r').read().lower().strip()
  580.             except IOError:
  581.                 # Some O/Ss don't have /etc/issue (Mac)
  582.                 self.distro, self.distro_version = DISTRO_UNKNOWN, '0.0'
  583.             else:
  584.                 for d in self.distros:
  585.                     if name.find(d) > -1:
  586.                         self.distro = self.distros[d]['index']
  587.                         found = True
  588.                     else:
  589.                         for x in self.distros[d].get('alt_names', ''):
  590.                             if x and name.find(x) > -1:
  591.                                 self.distro = self.distros[d]['index']
  592.                                 found = True
  593.                                 break
  594.  
  595.                     if found:
  596.                         break
  597.  
  598.                 if found:
  599.                     for n in name.split():
  600.                         m= n
  601.                         if '.' in n:
  602.                             m = '.'.join(n.split('.')[:2])
  603.  
  604.                         try:
  605.                             float(m)
  606.                         except ValueError:
  607.                             try:
  608.                                 int(m)
  609.                             except ValueError:
  610.                                 self.distro_version = '0.0'
  611.                             else:
  612.                                 self.distro_version = m
  613.                                 break
  614.                         else:
  615.                             self.distro_version = m
  616.                             break
  617.  
  618.                     log.debug("/etc/issue: %s %s" % (name, self.distro_version))
  619.  
  620.         log.debug("distro=%d, distro_version=%s" % (self.distro, self.distro_version))
  621.  
  622.  
  623.     def distro_changed(self):
  624.         ppd_install = self.get_distro_ver_data('ppd_install', 'ppd')
  625.  
  626.         if ppd_install not in ('ppd', 'drv'):
  627.             log.warning("Invalid ppd_install value: %s" % ppd_install)
  628.  
  629.         if self.cups11:
  630.             self.enable_ppds = True
  631.         else:
  632.             self.enable_ppds = (ppd_install == 'ppd')
  633.  
  634.         log.debug("Enable PPD install: %s (False=drv)" % self.enable_ppds)
  635.  
  636.         self.ppd_dir = self.get_distro_ver_data('ppd_dir')
  637.  
  638.         #if not self.ppd_dir:
  639.         #    log.warning("Invalid ppd_dir value: %s" % self.ppd_dir)
  640.  
  641.         self.drv_dir = self.get_distro_ver_data('drv_dir')
  642.         if not self.enable_ppds and not self.drv_dir:
  643.             log.warning("Invalid drv_dir value: %s" % self.drv_dir)
  644.  
  645.         self.distro_version_supported = self.get_distro_ver_data('supported', False)
  646.         self.selected_options['fax'] = self.get_distro_ver_data('fax_supported', True)
  647.         self.selected_options['network'] = self.get_distro_ver_data('network_supported', True)
  648.         self.selected_options['scan'] = self.get_distro_ver_data('scan_supported', True)
  649.         self.selected_options['parallel'] = self.get_distro_ver_data('parallel_supported', False)
  650.  
  651.         gui_supported = self.get_distro_ver_data('gui_supported', True)
  652.  
  653.         if gui_supported:
  654.             # Adjust required flag based on the distro ver ui_toolkit value
  655.             ui_toolkit = self.get_distro_ver_data('ui_toolkit',  'qt4').lower()
  656.  
  657.             if ui_toolkit == 'qt4':
  658.                 log.debug("Default UI toolkit: Qt4")
  659.                 self.ui_toolkit = 'qt4'
  660.                 self.selected_options['gui_qt4'] = True
  661.                 self.selected_options['gui_qt3'] = False
  662.  
  663.             elif ui_toolkit == 'qt3':
  664.                 log.debug("Default UI toolkit: Qt3")
  665.                 self.ui_toolkit = 'qt3'
  666.                 self.selected_options['gui_qt4'] = False
  667.                 self.selected_options['gui_qt3'] = True
  668.             # todo: gtk
  669.         else:
  670.                 self.selected_options['gui_qt3'] = False
  671.                 self.selected_options['gui_qt4'] = False
  672.  
  673.         # Override with --qt3 or --qt4 command args
  674.         if self.enable is not None:
  675.             if 'qt3' in self.enable:
  676.                 log.debug("User selected UI toolkit: Qt3")
  677.                 self.ui_toolkit = 'qt3'
  678.                 self.selected_options['gui_qt3'] = True
  679.             if 'qt4' in self.enable:
  680.                 log.debug("User selected UI toolkit: Qt4")
  681.                 self.ui_toolkit = 'qt4'
  682.                 self.selected_options['gui_qt4'] = True
  683.  
  684.         if self.disable is not None:
  685.             if 'qt3' in self.disable:
  686.                 log.debug("User deselected UI toolkit: Qt3")
  687.                 self.selected_options['gui_qt3'] = False
  688.             if 'qt4' in self.disable:
  689.                 log.debug("User deselected UI toolkit: Qt4")
  690.                 self.selected_options['gui_qt4'] = False
  691.  
  692.  
  693.     def __fixup_data(self, key, data):
  694.         field_type = self.FIELD_TYPES.get(key, TYPE_STRING)
  695.         #log.debug("%s (%s) %d" % (key, data, field_type))
  696.  
  697.         if field_type == TYPE_BOOL:
  698.             return utils.to_bool(data)
  699.  
  700.         elif field_type == TYPE_STRING:
  701.             if type('') == type(data):
  702.                 return data.strip()
  703.             else:
  704.                 return data
  705.  
  706.         elif field_type == TYPE_INT:
  707.             try:
  708.                 return int(data)
  709.             except ValueError:
  710.                 return 0
  711.  
  712.         elif field_type == TYPE_LIST:
  713.             return [x for x in data.split(',') if x]
  714.  
  715.  
  716.     def load_distros(self):
  717.         if self.mode  == MODE_INSTALLER:
  718.             distros_dat_file = os.path.join('installer', 'distros.dat')
  719.  
  720.         elif self.mode == MODE_CREATE_DOCS:
  721.             distros_dat_file = os.path.join('..', '..', 'installer', 'distros.dat')
  722.  
  723.         else: # MODE_CHECK
  724.             distros_dat_file = os.path.join(prop.home_dir, 'installer', 'distros.dat')
  725.  
  726.             if not os.path.exists(distros_dat_file):
  727.                 log.debug("DAT file not found at %s. Using local relative path..." % distros_dat_file)
  728.                 distros_dat_file = os.path.join('installer', 'distros.dat')
  729.  
  730.         distros_dat = ConfigBase(distros_dat_file)
  731.         distros_list = self.__fixup_data('distros', distros_dat.get('distros', 'distros'))
  732.         log.debug(distros_list)
  733.  
  734.         for distro in distros_list:
  735.             update_spinner()
  736.             d = {}
  737.  
  738.             if not distros_dat.has_section(distro):
  739.                 log.debug("Missing distro section in distros.dat: [%s]" % distro)
  740.                 continue
  741.  
  742.             for key in distros_dat.keys(distro):
  743.                 d[key] = self.__fixup_data(key, distros_dat.get(distro, key))
  744.  
  745.             self.distros[distro] = d
  746.             versions = self.__fixup_data("versions", distros_dat.get(distro, 'versions'))
  747.             self.distros[distro]['versions'] = {}
  748.  
  749.             for ver in versions:
  750.                 same_as_version, supported = False, True
  751.                 v = {}
  752.                 ver_section = "%s:%s" % (distro, ver)
  753.  
  754.                 if not distros_dat.has_section(ver_section):
  755.                     log.error("Missing version section in distros.dat: [%s:%s]" % (distro, ver))
  756.                     continue
  757.  
  758.                 if 'same_as_version' in distros_dat.keys(ver_section):
  759.                     same_as_version = True
  760.  
  761.                 supported = self.__fixup_data('supported', distros_dat.get(ver_section, 'supported'))
  762.  
  763.                 for key in distros_dat.keys(ver_section):
  764.                     v[key] = self.__fixup_data(key, distros_dat.get(ver_section, key))
  765.  
  766.                 self.distros[distro]['versions'][ver] = v
  767.                 self.distros[distro]['versions'][ver]['dependency_cmds'] = {}
  768.  
  769.                 if same_as_version or not supported:
  770.                     continue
  771.  
  772.                 for dep in self.dependencies:
  773.                     dd = {}
  774.                     dep_section = "%s:%s:%s" % (distro, ver, dep)
  775.  
  776.                     if not distros_dat.has_section(dep_section) and not same_as_version:
  777.                         log.debug("Missing dependency section in distros.dat: [%s:%s:%s]" % (distro, ver, dep))
  778.                         continue
  779.  
  780.                     #if same_as_version:
  781.                     #    continue
  782.  
  783.                     for key in distros_dat.keys(dep_section):
  784.                         dd[key] = self.__fixup_data(key, distros_dat.get(dep_section, key))
  785.  
  786.                     self.distros[distro]['versions'][ver]['dependency_cmds'][dep] = dd
  787.  
  788.             versions = self.distros[distro]['versions']
  789.             for ver in versions:
  790.                 ver_section = "%s:%s" % (distro, ver)
  791.  
  792.                 if 'same_as_version' in distros_dat.keys(ver_section):
  793.                     v = self.__fixup_data("same_as_version", distros_dat.get(ver_section, 'same_as_version'))
  794.                     log.debug("Setting %s:%s to %s:%s" % (distro, ver, distro, v))
  795.  
  796.                     try:
  797.                         vv = self.distros[distro]['versions'][v].copy()
  798.                         vv['same_as_version'] = v
  799.                         self.distros[distro]['versions'][ver] = vv
  800.                     except KeyError:
  801.                         log.debug("Missing 'same_as_version=' version in distros.dat for section [%s:%s]." % (distro, v))
  802.                         continue
  803.  
  804.         #import pprint
  805.         #pprint.pprint(self.distros)
  806.  
  807.     def pre_install(self):
  808.         pass
  809.  
  810.  
  811.     def pre_depend(self):
  812.         pass
  813.  
  814.  
  815.     def check_python2x(self):
  816.         py_ver = sys.version_info
  817.         py_major_ver, py_minor_ver = py_ver[:2]
  818.         log.debug("Python ver=%d.%d" % (py_major_ver, py_minor_ver))
  819.         return py_major_ver >= 2
  820.  
  821.  
  822.     def check_gcc(self):
  823.         return check_tool('gcc --version', 0) and check_tool('g++ --version', 0)
  824.  
  825.  
  826.     def check_make(self):
  827.         return check_tool('make --version', 3.0)
  828.  
  829.  
  830.     def check_libusb(self):
  831.         if not check_lib('libusb'):
  832.             return False
  833.  
  834.         return len(locate_file_contains("usb.h", '/usr/include', 'usb_init(void)'))
  835.  
  836.  
  837.     def check_libjpeg(self):
  838.         return check_lib("libjpeg") and check_file("jpeglib.h")
  839.  
  840.  
  841.     def check_libcrypto(self):
  842.         return check_lib("libcrypto") and check_file("crypto.h")
  843.  
  844.  
  845.     def check_libpthread(self):
  846.         return check_lib("libpthread") and check_file("pthread.h")
  847.  
  848.  
  849.     def check_libnetsnmp(self):
  850.         return check_lib("libnetsnmp") and check_file("net-snmp-config.h")
  851.  
  852.  
  853.     def check_reportlab(self):
  854.         try:
  855.             log.debug("Trying to import 'reportlab'...")
  856.             import reportlab
  857.  
  858.             ver = reportlab.Version
  859.             try:
  860.                 ver_f = float(ver)
  861.             except ValueError:
  862.                 log.debug("Can't determine version.")
  863.                 return False
  864.             else:
  865.                 log.debug("Version: %.1f" % ver_f)
  866.                 if ver_f >= 2.0:
  867.                     log.debug("Success.")
  868.                     return True
  869.                 else:
  870.                     return False
  871.  
  872.         except ImportError:
  873.             log.debug("Failed.")
  874.             return False
  875.  
  876.  
  877.     def check_python23(self):
  878.         py_ver = sys.version_info
  879.         py_major_ver, py_minor_ver = py_ver[:2]
  880.         log.debug("Python ver=%d.%d" % (py_major_ver, py_minor_ver))
  881.         return py_major_ver >= 2 and py_minor_ver >= 3
  882.  
  883.  
  884.     def check_python_xml(self):
  885.         try:
  886.             import xml.parsers.expat
  887.         except ImportError:
  888.             return False
  889.         else:
  890.             return True
  891.  
  892.  
  893.     def check_sane(self):
  894.         return check_lib('libsane')
  895.  
  896.  
  897.     def check_sane_devel(self):
  898.         return len(locate_file_contains("sane.h", '/usr/include', 'extern SANE_Status sane_init'))
  899.  
  900.  
  901.     def check_xsane(self):
  902.         if os.getenv('DISPLAY'):
  903.             return check_tool('xsane --version', 0.9) # will fail if X not running...
  904.         else:
  905.             return bool(utils.which("xsane")) # ...so just see if it installed somewhere
  906.  
  907.  
  908.     def check_scanimage(self):
  909.         return check_tool('scanimage --version', 1.0)
  910.  
  911.  
  912.     def check_ppdev(self):
  913.         return check_lsmod('ppdev')
  914.  
  915.  
  916.     def check_gs(self):
  917.         return check_tool('gs -v', 7.05)
  918.  
  919.  
  920.     def check_pyqt(self):
  921.         if self.ui_toolkit == 'qt3':
  922.             try:
  923.                 import qt
  924.                 pyqtVersion = None
  925.                 try:
  926.                     pyqtVersion = qt.PYQT_VERSION_STR
  927.                     log.debug("PYQT_VERSION_STR = %s" % pyqtVersion)
  928.                 except AttributeError:
  929.                     try:
  930.                         pyqtVersion = qt.PYQT_VERSION
  931.                         log.debug("PYQT_VERSION = %s" % pyqtVersion)
  932.                     except AttributeError:
  933.                         pass
  934.  
  935.                 if pyqtVersion is not None:
  936.                     while pyqtVersion.count('.') < 2:
  937.                         pyqtVersion += '.0'
  938.  
  939.                     (maj_ver, min_ver, pat_ver) = pyqtVersion.split('.')
  940.  
  941.                     if pyqtVersion.find('snapshot') >= 0:
  942.                         log.debug("A non-stable snapshot version of PyQt is installed.")
  943.                         pass
  944.                     else:
  945.                         try:
  946.                             maj_ver = int(maj_ver)
  947.                             min_ver = int(min_ver)
  948.                             pat_ver = int(pat_ver)
  949.                         except ValueError:
  950.                             maj_ver, min_ver, pat_ver = 0, 0, 0
  951.                         else:
  952.                             log.debug("Version %d.%d.%d installed." % (maj_ver, min_ver, pat_ver))
  953.  
  954.                         if maj_ver < MINIMUM_PYQT_MAJOR_VER or \
  955.                             (maj_ver == MINIMUM_PYQT_MAJOR_VER and min_ver < MINIMUM_PYQT_MINOR_VER):
  956.                             log.debug("HPLIP may not function properly with the version of PyQt that is installed (%d.%d.%d)." % (maj_ver, min_ver, pat_ver))
  957.                             log.debug("Incorrect version of PyQt installed. Ver. %d.%d or greater required." % (MINIMUM_PYQT_MAJOR_VER, MINIMUM_PYQT_MINOR_VER))
  958.                             return True
  959.                         else:
  960.                             return True
  961.  
  962.             except (ImportError, TypeError): # Note: IGOS 1.0 produces a TypeError when importing Qt3
  963.                 return False
  964.  
  965.         else:
  966.             return False
  967.  
  968.  
  969.     def check_pyqt4(self):
  970.         if self.ui_toolkit == 'qt4':
  971.             try:
  972.                 import PyQt4
  973.             except ImportError:
  974.                 return False
  975.             else:
  976.                 return True
  977.  
  978.         else:
  979.             return False
  980.  
  981.  
  982.     def check_pyqt4_dbus(self):
  983.         if self.ui_toolkit == 'qt4':
  984.             try:
  985.                 from dbus.mainloop.qt import DBusQtMainLoop
  986.             except ImportError:
  987.                 return False
  988.             else:
  989.                 return True
  990.         else:
  991.             return False
  992.  
  993.  
  994.     def check_python_devel(self):
  995.         return check_file('Python.h')
  996.  
  997.  
  998.     def check_python_dbus(self):
  999.         log.debug("Checking for python-dbus (>= 0.80)...")
  1000.         try:
  1001.             import dbus
  1002.             try:
  1003.                 ver = dbus.version
  1004.                 log.debug("Version: %s" % '.'.join([str(x) for x in dbus.version]))
  1005.                 return ver >= (0,80,0)
  1006.  
  1007.             except AttributeError:
  1008.                 try:
  1009.                     ver = dbus.__version__
  1010.                     log.debug("Version: %s" % dbus.__version__)
  1011.                     log.debug("HPLIP requires dbus version > 0.80.")
  1012.                     return False
  1013.  
  1014.                 except AttributeError:
  1015.                     log.debug("Unknown version. HPLIP requires dbus version > 0.80.")
  1016.                     return False
  1017.  
  1018.         except ImportError:
  1019.             return False
  1020.  
  1021.  
  1022.     def check_python_ctypes(self):
  1023.         try:
  1024.             import ctypes
  1025.             return True
  1026.         except ImportError:
  1027.             return False
  1028.  
  1029.  
  1030.     def check_dbus(self):
  1031.         log.debug("Checking for dbus running and header files present (dbus-devel)...")
  1032.         return check_ps(['dbus-daemon'])  and \
  1033.             len(locate_file_contains("dbus-message.h", '/usr/include', 'dbus_message_new_signal'))
  1034.  
  1035.         #try:
  1036.             #from dbus import lowlevel, SystemBus, SessionBus
  1037.             #import dbus.service
  1038.             #from dbus.mainloop.glib import DBusGMainLoop
  1039.             #from gobject import MainLoop
  1040.             #python_dbus = True
  1041.         #except ImportError:
  1042.             #python_dbus = False
  1043.  
  1044.         #return dbus_running and python_dbus
  1045.  
  1046.  
  1047.     def check_cups_devel(self):
  1048.         return check_file('cups.h') and bool(utils.which('lpr'))
  1049.  
  1050.  
  1051.     def check_cups(self):
  1052.         status, output = self.run('lpstat -r')
  1053.         if status > 0:
  1054.             log.debug("CUPS is not running.")
  1055.             return False
  1056.         else:
  1057.             log.debug("CUPS is running.")
  1058.             return True
  1059.  
  1060.  
  1061.     def check_hpoj(self):
  1062.         log.debug("Checking for 'HPOJ'...")
  1063.         return check_ps(['ptal-mlcd', 'ptal-printd', 'ptal-photod']) or \
  1064.             bool(utils.which("ptal-init"))
  1065.  
  1066.  
  1067.     def check_hplip(self):
  1068.         log.debug("Checking for HPLIP...")
  1069.         return locate_files('hplip.conf', '/etc/hp')
  1070.  
  1071.  
  1072.     def check_hpssd(self):
  1073.         log.debug("Checking for hpssd...")
  1074.         return check_ps(['hpssd'])
  1075.  
  1076.  
  1077.     def check_libtool(self):
  1078.         log.debug("Checking for libtool...")
  1079.         return check_tool('libtool --version')
  1080.  
  1081.  
  1082.     def check_pil(self):
  1083.         log.debug("Checking for PIL...")
  1084.         try:
  1085.             import Image
  1086.             return True
  1087.         except ImportError:
  1088.             return False
  1089.  
  1090.  
  1091.     def check_cupsddk(self):
  1092.         log.debug("Checking for cups-ddk...")
  1093.         # TODO: Compute these paths some way or another...
  1094.         #return check_tool("/usr/lib/cups/driver/drv list") and os.path.exists("/usr/share/cupsddk/include/media.defs")
  1095.         return (check_file('drv', "/usr/lib/cups/driver") or check_file('drv', "/usr/lib64/cups/driver")) and \
  1096.             check_file('media.defs', "/usr/share/cupsddk/include")
  1097.  
  1098.  
  1099.     def check_pkg_mgr(self):
  1100.         """
  1101.             Check if any pkg mgr processes are running
  1102.         """
  1103.         log.debug("Searching for '%s' in running processes..." % self.package_mgrs)
  1104.  
  1105.         processes = get_process_list()
  1106.  
  1107.         for pid, cmdline in processes:
  1108.             for p in self.package_mgrs:
  1109.                 if p in cmdline:
  1110.                     for k in OK_PROCESS_LIST:
  1111.                         if k not in cmdline:
  1112.                             log.debug("Found: %s (%d)" % (cmdline, pid))
  1113.                             return (pid, cmdline)
  1114.  
  1115.         log.debug("Not found")
  1116.         return (0, '')
  1117.  
  1118.  
  1119.     def get_hplip_version(self):
  1120.         self.version_description, self.version_public, self.version_internal = '', '', ''
  1121.  
  1122.         if self.mode == MODE_INSTALLER:
  1123.             ac_init_pat = re.compile(r"""AC_INIT\(\[(.*?)\], *\[(.*?)\], *\[(.*?)\], *\[(.*?)\] *\)""", re.IGNORECASE)
  1124.  
  1125.             try:
  1126.                 config_in = open('./configure.in', 'r')
  1127.             except IOError:
  1128.                 self.version_description, self.version_public, self.version_internal = \
  1129.                     '', sys_conf.get('configure', 'internal-tag', '0.0.0'), prop.installed_version
  1130.             else:
  1131.                 for c in config_in:
  1132.                     if c.startswith("AC_INIT"):
  1133.                         match_obj = ac_init_pat.search(c)
  1134.                         self.version_description = match_obj.group(1)
  1135.                         self.version_public = match_obj.group(2)
  1136.                         self.version_internal = match_obj.group(3)
  1137.                         name = match_obj.group(4)
  1138.                         break
  1139.  
  1140.                 config_in.close()
  1141.  
  1142.                 if name != 'hplip':
  1143.                     log.error("Invalid archive!")
  1144.  
  1145.  
  1146.         else: # MODE_CHECK
  1147.             try:
  1148.                 self.version_description, self.version_public, self.version_internal = \
  1149.                     '', sys_conf.get('configure', 'internal-tag', '0.0.0'), prop.installed_version
  1150.             except KeyError:
  1151.                 self.version_description, self.version_public, self.version_internal = '', '', ''
  1152.  
  1153.         return self.version_description, self.version_public, self.version_internal
  1154.  
  1155.  
  1156.     def configure(self):
  1157.         configure_cmd = './configure'
  1158.         configuration = {}
  1159.  
  1160.         dbus_avail = self.have_dependencies['dbus'] and self.have_dependencies['python-dbus']
  1161.  
  1162.         configuration['network-build'] = self.selected_options['network']
  1163.         configuration['pp-build'] = self.selected_options['parallel']
  1164.         configuration['fax-build'] = self.selected_options['fax'] and dbus_avail
  1165.         configuration['dbus-build'] = dbus_avail
  1166.         configuration['qt3'] = self.selected_options['gui_qt3']
  1167.         configuration['qt4'] = self.selected_options['gui_qt4']
  1168.         configuration['scan-build'] = self.selected_options['scan']
  1169.         configuration['doc-build'] = self.selected_options['docs']
  1170.  
  1171.         if self.enable_ppds: # Use ppd install if cups 1.1 or ppd_install=ppd
  1172.             configuration['foomatic-ppd-install'] = True
  1173.             configuration['foomatic-drv-install'] = False
  1174.  
  1175.         else: # otherwise, use drv if cups ddk is avail, otherwise fall back to .ppds
  1176.             if self.have_dependencies['cups-ddk']:
  1177.                 configuration['foomatic-ppd-install'] = False
  1178.                 configuration['foomatic-drv-install'] = True
  1179.  
  1180.                 if self.drv_dir is not None:
  1181.                     configure_cmd += ' --with-drvdir=%s' % self.drv_dir
  1182.  
  1183.             else:
  1184.                 configuration['foomatic-ppd-install'] = True
  1185.                 configuration['foomatic-drv-install'] = False
  1186.  
  1187.         if self.ppd_dir is not None:
  1188.             configure_cmd += ' --with-hpppddir=%s' % self.ppd_dir
  1189.  
  1190.         configuration['hpijs-only-build'] = self.hpijs_build
  1191.  
  1192.         if self.bitness == 64:
  1193.             configure_cmd += ' --libdir=/usr/lib64'
  1194.  
  1195.         configure_cmd += ' --prefix=%s' % self.install_location
  1196.  
  1197.         if self.cups11:
  1198.             configuration['cups11-build'] = True
  1199.  
  1200.         if self.get_distro_ver_data('cups_path_with_bitness', False) and self.bitness == 64:
  1201.             configure_cmd += ' --with-cupsbackenddir=/usr/lib64/cups/backend --with-cupsfilterdir=/usr/lib64/cups/filter'
  1202.  
  1203.         if self.enable is not None:
  1204.             for c in self.enable:
  1205.                 configuration[c] = True
  1206.  
  1207.         if self.disable is not None:
  1208.             for c in self.disable:
  1209.                 configuration[c] = False
  1210.  
  1211.         for c in configuration:
  1212.             if configuration[c]:
  1213.                 configure_cmd += ' --enable-%s' % c
  1214.             else:
  1215.                 configure_cmd += ' --disable-%s' % c
  1216.  
  1217.         return configure_cmd
  1218.  
  1219.  
  1220.     def restart_cups(self):
  1221.         if os.path.exists('/etc/init.d/cups'):
  1222.             cmd = self.su_sudo() % '/etc/init.d/cups restart'
  1223.  
  1224.         elif os.path.exists('/etc/init.d/cupsys'):
  1225.             cmd = self.su_sudo() % '/etc/init.d/cupsys restart'
  1226.  
  1227.         else:
  1228.             cmd = self.su_sudo() % 'killall -HUP cupsd'
  1229.  
  1230.         self.run(cmd)
  1231.  
  1232.  
  1233.     def stop_hplip(self):
  1234.         return self.su_sudo() % "/etc/init.d/hplip stop"
  1235.  
  1236.  
  1237.     def su_sudo(self):
  1238.         if os.geteuid() == 0:
  1239.             return '%s'
  1240.         else:
  1241.             try:
  1242.                 cmd = self.distros[self.distro_name]['su_sudo']
  1243.             except KeyError:
  1244.                 cmd = 'su'
  1245.  
  1246.             if cmd == 'su':
  1247.                 return 'su -c "%s"'
  1248.             else:
  1249.                 return 'sudo %s'
  1250.  
  1251.     def su_sudo_str(self):
  1252.         return self.get_distro_data('su_sudo', 'su')
  1253.  
  1254.  
  1255.     def build_cmds(self):
  1256.         return [self.configure(),
  1257.                 'make clean',
  1258.                 'make',
  1259.                 self.su_sudo() % 'make install']
  1260.  
  1261.  
  1262.     def get_distro_ver_data(self, key, default=None):
  1263.         try:
  1264.             return self.distros[self.distro_name]['versions'][self.distro_version].get(key, None) or \
  1265.                 self.distros[self.distro_name].get(key, None) or default
  1266.         except KeyError:
  1267.             return default
  1268.  
  1269.         return value
  1270.  
  1271.  
  1272.     def get_distro_data(self, key, default=None):
  1273.         try:
  1274.             return self.distros[self.distro_name].get(key, None) or default
  1275.         except KeyError:
  1276.             return default
  1277.  
  1278.  
  1279.     def get_ver_data(self, key, default=None):
  1280.         try:
  1281.             return self.distros[self.distro_name]['versions'][self.distro_version].get(key, None) or default
  1282.         except KeyError:
  1283.             return default
  1284.  
  1285.         return value
  1286.  
  1287.  
  1288.     def get_dependency_data(self, dependency):
  1289.         dependency_cmds = self.get_ver_data("dependency_cmds", {})
  1290.         dependency_data = dependency_cmds.get(dependency, {})
  1291.         packages = dependency_data.get('packages', [])
  1292.         commands = dependency_data.get('commands', [])
  1293.         return packages, commands
  1294.  
  1295.  
  1296.     def get_dependency_commands(self):
  1297.         dd = self.dependencies.keys()
  1298.         dd.sort()
  1299.         commands_to_run = []
  1300.         packages_to_install = []
  1301.         overall_commands_to_run = []
  1302.         for d in dd:
  1303.             include = False
  1304.             for opt in self.dependencies[d][1]:
  1305.                 if self.selected_options[opt]:
  1306.                     include = True
  1307.             if include:
  1308.                 pkgs, cmds = self.get_dependency_data(d)
  1309.  
  1310.                 if pkgs:
  1311.                     packages_to_install.extend(pkgs)
  1312.  
  1313.                 if cmds:
  1314.                     commands_to_run.extend(cmds)
  1315.  
  1316.         package_mgr_cmd = self.get_distro_data('package_mgr_cmd')
  1317.  
  1318.         overall_commands_to_run.extend(commands_to_run)
  1319.  
  1320.         if package_mgr_cmd:
  1321.             packages_to_install = ' '.join(packages_to_install)
  1322.             overall_commands_to_run.append(utils.cat(package_mgr_cmd))
  1323.  
  1324.         if not overall_commands_to_run:
  1325.             log.error("No cmds/pkgs")
  1326.  
  1327.         return overall_commands_to_run
  1328.  
  1329.  
  1330.     def distro_known(self):
  1331.         return self.distro != DISTRO_UNKNOWN and self.distro_version != DISTRO_VER_UNKNOWN
  1332.  
  1333.  
  1334.     def distro_supported(self):
  1335.         if self.mode == MODE_INSTALLER:
  1336.             return self.distro != DISTRO_UNKNOWN and self.distro_version != DISTRO_VER_UNKNOWN and self.get_ver_data('supported', False)
  1337.         else:
  1338.             return True # For docs (manual install)
  1339.  
  1340.  
  1341.     def sort_vers(self, x, y):
  1342.         try:
  1343.             return cmp(float(x), float(y))
  1344.         except ValueError:
  1345.             return cmp(x, y)
  1346.  
  1347.  
  1348.     def running_as_root(self):
  1349.         return os.geteuid() == 0
  1350.  
  1351.  
  1352.     def show_release_notes_in_browser(self):
  1353.         url = "file://%s" % os.path.join(os.getcwd(), 'doc', 'release_notes.html')
  1354.         log.debug(url)
  1355.         status, output = self.run("xhost +")
  1356.         utils.openURL(url)
  1357.  
  1358.  
  1359.     def count_num_required_missing_dependencies(self):
  1360.         num_req_missing = 0
  1361.         for d, desc, opt in self.missing_required_dependencies():
  1362.             num_req_missing += 1
  1363.         return num_req_missing
  1364.  
  1365.  
  1366.     def count_num_optional_missing_dependencies(self):
  1367.         num_opt_missing = 0
  1368.         for d, desc, req, opt in self.missing_optional_dependencies():
  1369.             num_opt_missing += 1
  1370.         return num_opt_missing
  1371.  
  1372.  
  1373.     def missing_required_dependencies(self): # missing req. deps in req. options
  1374.         for opt in self.components[self.selected_component][1]:
  1375.             if self.options[opt][0]: # required options
  1376.                 for d in self.options[opt][2]: # dependencies for option
  1377.                     if self.dependencies[d][0]: # required option
  1378.                         if not self.have_dependencies[d]: # missing
  1379.                             log.debug("Missing required dependency: %s" % d)
  1380.                             yield d, self.dependencies[d][2], opt
  1381.                             # depend, desc, option
  1382.  
  1383.  
  1384.     def missing_optional_dependencies(self):
  1385.         # missing deps in opt. options
  1386.         for opt in self.components[self.selected_component][1]:
  1387.             if not self.options[opt][0]: # not required option
  1388.                 if self.selected_options[opt]: # only for options that are ON
  1389.                     for d in self.options[opt][2]: # dependencies
  1390.                         if not self.have_dependencies[d]: # missing dependency
  1391.                             log.debug("Missing optional dependency: %s" % d)
  1392.                             yield d, self.dependencies[d][2], self.dependencies[d][0], opt
  1393.                             # depend, desc, required_for_opt, opt
  1394.  
  1395.         # opt. deps in req. options
  1396.         for opt in self.components[self.selected_component][1]:
  1397.               if self.options[opt][0]: # required options
  1398.                   for d in self.options[opt][2]: # dependencies for option
  1399.                       if not self.dependencies[d][0]: # optional dep
  1400.                           if not self.have_dependencies[d]: # missing
  1401.                               log.debug("Missing optional dependency: %s" % d)
  1402.                               yield d, self.dependencies[d][2], self.dependencies[d][0], opt
  1403.                               # depend, desc, option
  1404.  
  1405.  
  1406.     def select_options(self, answer_callback):
  1407.         num_opt_missing = 0
  1408.         # not-required options
  1409.         for opt in self.components[self.selected_component][1]:
  1410.             if not self.options[opt][0]: # not required
  1411.                 default = 'y'
  1412.  
  1413.                 if not self.selected_options[opt]:
  1414.                     default = 'n'
  1415.  
  1416.                 self.selected_options[opt] = answer_callback(opt, self.options[opt][1], default)
  1417.  
  1418.                 if self.selected_options[opt]: # only for options that are ON
  1419.                     for d in self.options[opt][2]: # dependencies
  1420.                         if not self.have_dependencies[d]: # missing dependency
  1421.                             log.debug("Missing optional dependency: %s" % d)
  1422.                             num_opt_missing += 1
  1423.  
  1424.         return num_opt_missing
  1425.  
  1426.  
  1427.     def check_network_connection(self):
  1428.         self.network_connected = False
  1429.  
  1430.         wget = utils.which("wget")
  1431.         if wget:
  1432.             wget = os.path.join(wget, "wget")
  1433.             cmd = "%s --timeout=10 %s" % (wget, HTTP_GET_TARGET)
  1434.             log.debug(cmd)
  1435.             status, output = self.run(cmd)
  1436.             log.debug("wget returned: %d" % status)
  1437.             self.network_connected = (status == 0)
  1438.  
  1439.         else:
  1440.             curl = utils.which("curl")
  1441.             if curl:
  1442.                 curl = os.path.join(curl, "curl")
  1443.                 cmd = "%s --connect-timeout 5 --max-time 10 %s" % (curl, HTTP_GET_TARGET)
  1444.                 log.debug(cmd)
  1445.                 status, output = self.run(cmd)
  1446.                 log.debug("curl returned: %d" % status)
  1447.                 self.network_connected = (status == 0)
  1448.  
  1449.             else:
  1450.                 ping = utils.which("ping")
  1451.  
  1452.                 if ping:
  1453.                     ping = os.path.join(ping, "ping")
  1454.                     cmd = "%s -c1 -W1 -w10 %s" % (ping, PING_TARGET)
  1455.                     log.debug(cmd)
  1456.                     status, output = self.run(cmd)
  1457.                     log.debug("ping returned: %d" % status)
  1458.                     self.network_connected = (status == 0)
  1459.  
  1460.         return self.network_connected
  1461.  
  1462.  
  1463.     def run_pre_install(self, callback=None):
  1464.         pre_cmd = self.get_distro_ver_data('pre_install_cmd')
  1465.         log.debug(pre_cmd)
  1466.         if pre_cmd:
  1467.             x = 1
  1468.             for cmd in pre_cmd:
  1469.                 status, output = self.run(cmd)
  1470.  
  1471.                 if status != 0:
  1472.                     log.warn("An error occurred running '%s'" % cmd)
  1473.  
  1474.                 if callback is not None:
  1475.                     callback(cmd, "Pre-install step %d" % x)
  1476.  
  1477.                 x += 1
  1478.  
  1479.             return True
  1480.  
  1481.         else:
  1482.             return False
  1483.  
  1484.     def run_pre_depend(self, callback=None):
  1485.         pre_cmd = self.get_distro_ver_data('pre_depend_cmd')
  1486.         log.debug(pre_cmd)
  1487.         if pre_cmd:
  1488.             x = 1
  1489.             for cmd in pre_cmd:
  1490.                 status, output = self.run(cmd)
  1491.  
  1492.                 if status != 0:
  1493.                     log.warn("An error occurred running '%s'" % cmd)
  1494.  
  1495.                 if callback is not None:
  1496.                     callback(cmd, "Pre-depend step %d" % x)
  1497.  
  1498.                 x += 1
  1499.  
  1500.     def run_post_depend(self, callback=None):
  1501.         post_cmd = self.get_distro_ver_data('post_depend_cmd')
  1502.         log.debug(post_cmd)
  1503.         if post_cmd:
  1504.             x = 1
  1505.             for cmd in post_cmd:
  1506.                 status, output = self.run(cmd)
  1507.  
  1508.                 if status != 0:
  1509.                     log.warn("An error occurred running '%s'" % cmd)
  1510.  
  1511.                 if callback is not None:
  1512.                     callback(cmd, "Post-depend step %d" % x)
  1513.  
  1514.                 x += 1
  1515.  
  1516.  
  1517.     def pre_build(self):
  1518.         cmds = []
  1519.         if self.get_distro_ver_data('fix_ppd_symlink', False):
  1520.             cmds.append(self.su_sudo() % 'python ./installer/fix_symlink.py')
  1521.  
  1522.         return cmds
  1523.  
  1524.     def run_pre_build(self, callback=None):
  1525.         x = 1
  1526.         for cmd in self.pre_build():
  1527.             status, output = self.run(cmd)
  1528.             if callback is not None:
  1529.                 callback(cmd, "Pre-build step %d"  % x)
  1530.  
  1531.             x += 1
  1532.  
  1533.  
  1534.     def run_post_build(self, callback=None):
  1535.         x = 1
  1536.         for cmd in self.post_build():
  1537.             status, output = self.run(cmd)
  1538.             if callback is not None:
  1539.                 callback(cmd, "Post-build step %d"  % x)
  1540.  
  1541.             x += 1
  1542.  
  1543.  
  1544.     def post_build(self):
  1545.         cmds = []
  1546.         self.logoff_required = False
  1547.         self.restart_required = True
  1548.         trigger_required = True
  1549.  
  1550.         # Restart CUPS if necessary
  1551.         if self.cups11:
  1552.             cmds.append(self.restart_cups())
  1553.  
  1554.         # Kill any running hpssd.py instance from a previous install
  1555.         if self.check_hpssd():
  1556.             pid = get_ps_pid('hpssd')
  1557.             if pid:
  1558.                 kill = os.path.join(utils.which("kill"), "kill") + " %d" % pid
  1559.  
  1560.                 cmds.append(self.su_sudo() % kill)
  1561.  
  1562.         return cmds
  1563.  
  1564.  
  1565.     def logoff(self):
  1566.         ok = False
  1567.         pkill = utils.which('pkill')
  1568.         if pkill:
  1569.             cmd = "%s -KILL -u %s" % (os.path.join(pkill, "pkill"), prop.username)
  1570.             cmd = self.su_sudo() % cmd
  1571.             status, output = self.run(cmd)
  1572.  
  1573.             ok = (status == 0)
  1574.  
  1575.         return ok
  1576.  
  1577.  
  1578.     def restart(self):
  1579.         ok = False
  1580.         shutdown = utils.which('shutdown')
  1581.         if shutdown:
  1582.             cmd = "%s -r now" % (os.path.join(shutdown, "shutdown"))
  1583.             cmd = self.su_sudo() % cmd
  1584.             status, output = self.run(cmd)
  1585.  
  1586.             ok = (status == 0)
  1587.  
  1588.         return ok
  1589.  
  1590.  
  1591.     def run_hp_setup(self):
  1592.         status = 0
  1593.         hpsetup = utils.which("hp-setup")
  1594.  
  1595.         if hpsetup:
  1596.             c = 'hp-setup'
  1597.         else:
  1598.             c = './setup.py'
  1599.  
  1600.         cmd = self.su_sudo() % c
  1601.         log.debug(cmd)
  1602.         status, output = self.run(cmd)
  1603.         return status == 0
  1604.  
  1605.  
  1606.     def remove_hplip(self, callback=None):
  1607.         failed = True
  1608.         self.stop_pre_2x_hplip(callback)
  1609.  
  1610.         hplip_remove_cmd = self.get_distro_data('hplip_remove_cmd')
  1611.         if hplip_remove_cmd:
  1612.             if callback is not None:
  1613.                 callback(hplip_remove_cmd, "Removing old HPLIP version")
  1614.  
  1615.                 status, output = self.run(hplip_remove_cmd)
  1616.  
  1617.             if status == 0:
  1618.                 self.hplip_present = self.check_hplip()
  1619.  
  1620.                 if not self.hplip_present:
  1621.                     failed = False
  1622.  
  1623.         return failed
  1624.  
  1625.  
  1626.     def stop_pre_2x_hplip(self, callback=None):
  1627.         hplip_init_script = '/etc/init.d/hplip stop'
  1628.         if os.path.exists(hplip_init_script):
  1629.             cmd = self.su_sudo() % hplip_init_script
  1630.  
  1631.             if callback is not None:
  1632.                 callback(cmd, "Stopping old HPLIP version.")
  1633.  
  1634.             status, output = self.run(cmd)
  1635.  
  1636.  
  1637.     def remove_hpoj(self, callback=None):
  1638.         # TODO: Must stop PTAL?
  1639.         hpoj_remove_cmd = self.get_distro_data('hpoj_remove_cmd')
  1640.         if hpoj_remove_cmd:
  1641.             if callback is not None:
  1642.                 callback(hpoj_remove_cmd, "Removing HPOJ")
  1643.  
  1644.                 status, output = self.run(hpoj_remove_cmd)
  1645.  
  1646.             if status == 0:
  1647.                 self.hpoj_present = check_hpoj()
  1648.  
  1649.                 if not self.hpoj_present:
  1650.                     failed = False
  1651.  
  1652.         return failed
  1653.  
  1654.  
  1655.     def check_password(self, password_entry_callback, callback=None):
  1656.         self.clear_su_sudo_password()
  1657.         x = 1
  1658.         while True:
  1659.             self.password = password_entry_callback()
  1660.             cmd = self.su_sudo() % "true"
  1661.  
  1662.             log.debug(cmd)
  1663.  
  1664.             status, output = self.run(cmd)
  1665.  
  1666.             log.debug(status)
  1667.             log.debug(output)
  1668.  
  1669.             if status == 0:
  1670.                 if callback is not None:
  1671.                     callback("", "Password accepted")
  1672.                 return True
  1673.  
  1674.             if callback is not None:
  1675.                 callback("", "Password incorrect. %d attempt(s) left." % (3-x))
  1676.  
  1677.             x += 1
  1678.  
  1679.             if x > 3:
  1680.                 return False
  1681.  
  1682.  
  1683.     def clear_su_sudo_password(self):
  1684.         if self.su_sudo_str() == 'sudo':
  1685.             log.debug("Clearing password...")
  1686.             self.run("sudo -K")
  1687.  
  1688.  
  1689.  
  1690.     # PLUGIN HELPERS
  1691.  
  1692.     def set_plugin_version(self):
  1693.         self.plugin_version = prop.installed_version
  1694.         log.debug("Plug-in version=%s" % self.plugin_version)
  1695.         self.plugin_name = 'hplip-%s-plugin.run' % self.plugin_version
  1696.         log.debug("Plug-in=%s" % self.plugin_name)
  1697.  
  1698.  
  1699.     def get_plugin_conf_url(self):
  1700.         url = "http://hplip.sf.net/plugin.conf"
  1701.         home = sys_conf.get('dirs', 'home')
  1702.  
  1703.         if os.path.exists('/etc/hp/plugin.conf'):
  1704.             url = "file:///etc/hp/plugin.conf"
  1705.  
  1706.         elif os.path.exists(os.path.join(home, 'plugin.conf')):
  1707.             url = "file://" + os.path.join(home, 'plugin.conf')
  1708.  
  1709.         log.debug("Plugin.conf url: %s" % url)
  1710.         return url
  1711.  
  1712.  
  1713.     def get_plugin_info(self, plugin_conf_url, callback):
  1714.         ok, size, checksum, timestamp, url = False, 0, 0, 0.0, ''
  1715.  
  1716.         if not self.create_plugin_dir():
  1717.             log.error("Could not create plug-in directory.")
  1718.             return '', 0, 0, 0, False
  1719.  
  1720.         local_conf_fp, local_conf = utils.make_temp_file()
  1721.  
  1722.         if os.path.exists(local_conf):
  1723.             os.remove(local_conf)
  1724.  
  1725.         try:
  1726.             try:
  1727.                 filename, headers = urllib.urlretrieve(plugin_conf_url, local_conf, callback)
  1728.             except IOError, e:
  1729.                 log.error("I/O Error: %s" % e.strerror)
  1730.                 return '', 0, 0, 0, False
  1731.  
  1732.             if not os.path.exists(local_conf):
  1733.                 log.error("plugin.conf not found.")
  1734.                 return '', 0, 0, 0, False
  1735.  
  1736.             plugin_conf_p = ConfigParser.ConfigParser()
  1737.  
  1738.             try:
  1739.                 plugin_conf_p.read(local_conf)
  1740.             except (ConfigParser.MissingSectionHeaderError, ConfigParser.ParsingError):
  1741.                 log.error("Error parsing file - 404 error?")
  1742.                 return '', 0, 0, 0, False
  1743.  
  1744.             try:
  1745.                 url = plugin_conf_p.get(self.plugin_version, 'url')
  1746.                 size = plugin_conf_p.getint(self.plugin_version, 'size')
  1747.                 checksum = plugin_conf_p.get(self.plugin_version, 'checksum')
  1748.                 timestamp = plugin_conf_p.getfloat(self.plugin_version, 'timestamp')
  1749.                 ok = True
  1750.             except (KeyError, ConfigParser.NoSectionError):
  1751.                 log.error("Error reading plugin.conf: Missing section [%s]" % self.plugin_version)
  1752.                 return '', 0, 0, 0, False
  1753.  
  1754.         finally:
  1755.             os.close(local_conf_fp)
  1756.             os.remove(local_conf)
  1757.  
  1758.         return url, size, checksum, timestamp, ok
  1759.  
  1760.  
  1761.     def create_plugin_dir(self):
  1762.         if not os.path.exists(self.plugin_path):
  1763.             try:
  1764.                 log.debug("Creating plugin directory: %s" % self.plugin_path)
  1765.                 os.umask(0)
  1766.                 os.makedirs(self.plugin_path, 0755)
  1767.                 return True
  1768.             except (OSError, IOError), e:
  1769.                 log.error("Unable to create directory: %s" % e.strerror)
  1770.                 return False
  1771.  
  1772.         return True
  1773.  
  1774.  
  1775.     def download_plugin(self, url, size, checksum, timestamp, callback=None):
  1776.         log.debug("Downloading %s plug-in from %s to %s" % (self.plugin_version, url, self.plugin_path))
  1777.  
  1778.         if not self.create_plugin_dir():
  1779.             return False, "Failed to create plug-in directory: %s" % self.plugin_path
  1780.  
  1781.         plugin_file = os.path.join(self.plugin_path, self.plugin_name)
  1782.  
  1783.         try:
  1784.             filename, headers = urllib.urlretrieve(url, plugin_file, callback)
  1785.         except IOError, e:
  1786.             log.error("Plug-in download failed: %s" % e.strerror)
  1787.             return False, e.strerror
  1788.  
  1789.         calc_checksum = get_checksum(file(plugin_file, 'r').read())
  1790.         log.debug("D/L file checksum=%s" % calc_checksum)
  1791.  
  1792.         return True, plugin_file
  1793.  
  1794.  
  1795.     def check_for_plugin(self):
  1796.         if not self.plugin_name:
  1797.             self.plugin_name = 'hplip-%s-plugin.run' % self.plugin_version
  1798.  
  1799.         plugin = os.path.join(self.plugin_path, self.plugin_name)
  1800.         log.debug("Checking for plugin: %s" % plugin)
  1801.         return os.path.exists(plugin) and \
  1802.             utils.to_bool(sys_state.get('plugin', 'installed', '0'))
  1803.  
  1804.  
  1805.     def run_plugin(self, mode=GUI_MODE, callback=None):
  1806.         plugin_file = os.path.join(self.plugin_path, self.plugin_name)
  1807.  
  1808.         if not os.path.exists(plugin_file):
  1809.             return False
  1810.  
  1811.         if mode == GUI_MODE:
  1812.             return os.system("sh %s -- -u" % plugin_file) == 0
  1813.         else:
  1814.             return os.system("sh %s -- -i" % plugin_file) == 0
  1815.  
  1816.  
  1817.  
  1818.  
  1819.