1#!/usr/local/bin/python3.8
2# -*- coding: utf-8 -*-
3#
4# (c) Copyright 2001-2018 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, Naga Samrat Chowdary Narla, Goutam Kodu, Amarnath Chitumalla
21#
22# Thanks to Henrique M. Holschuh <hmh@debian.org> for various security patches
23#
24
25
26
27# Std Lib
28import sys
29import os
30from subprocess import Popen, PIPE
31import grp
32import fnmatch
33import tempfile
34import socket
35import struct
36import select
37import time
38import fcntl
39import errno
40import stat
41import string
42import glob
43import re
44import datetime
45from .g import *
46import locale
47from .sixext.moves import html_entities, urllib2_request, urllib2_parse, urllib2_error
48from .sixext import PY3, to_unicode, to_bytes_utf8, to_string_utf8, BytesIO, StringIO, subprocess
49from . import os_utils
50try:
51    import xml.parsers.expat as expat
52    xml_expat_avail = True
53except ImportError:
54    xml_expat_avail = False
55
56try:
57    import platform
58    platform_avail = True
59except ImportError:
60    platform_avail = False
61
62try:
63    import dbus
64    from dbus import SystemBus, lowlevel, SessionBus
65    dbus_avail=True
66except ImportError:
67    dbus_avail=False
68
69try:
70    import hashlib # new in 2.5
71
72    def get_checksum(s):
73        return hashlib.sha1(s).hexdigest()
74
75except ImportError:
76    import sha # deprecated in 2.6/3.0
77
78    def get_checksum(s):
79        return sha.new(s).hexdigest()
80
81
82
83
84# Local
85from .g import *
86from .codes import *
87from . import pexpect
88
89
90BIG_ENDIAN = 0
91LITTLE_ENDIAN = 1
92
93RMDIR="rm -rf"
94RM="rm -f"
95
96DBUS_SERVICE='com.hplip.StatusService'
97
98HPLIP_WEB_SITE ="http://hplipopensource.com/hplip-web/index.html"
99HTTP_CHECK_TARGET = "http://www.hp.com"
100PING_CHECK_TARGET = "www.hp.com"
101
102ERROR_NONE = 0
103ERROR_FILE_CHECKSUM = 1
104ERROR_UNABLE_TO_RECV_KEYS =2
105ERROR_DIGITAL_SIGN_BAD =3
106
107MAJ_VER = sys.version_info[0]
108MIN_VER = sys.version_info[1]
109
110EXPECT_WORD_LIST = [
111    pexpect.EOF, # 0
112    pexpect.TIMEOUT, # 1
113    u"Continue?", # 2 (for zypper)
114    u"passwor[dt]:", # en/de/it/ru
115    u"kennwort", # de?
116    u"password for", # en
117    u"mot de passe", # fr
118    u"contraseña", # es
119    u"palavra passe", # pt
120    u"口令", # zh
121    u"wachtwoord", # nl
122    u"heslo", # czech
123    u"密码",
124    u"Lösenord", #sv
125]
126
127
128EXPECT_LIST = []
129for s in EXPECT_WORD_LIST:
130    try:
131        p = re.compile(s, re.I)
132    except TypeError:
133        EXPECT_LIST.append(s)
134    else:
135        EXPECT_LIST.append(p)
136
137
138def get_cups_systemgroup_list():
139    lis = []
140    try:
141        fp=open('/usr/local/etc/cups/cupsd.conf')
142    except IOError:
143        try:
144            if "root" != grp.getgrgid(os.stat('/usr/local/etc/cups/cupsd.conf').st_gid).gr_name:
145                return [grp.getgrgid(os.stat('/usr/local/etc/cups/cupsd.conf').st_gid).gr_name]
146        except OSError:
147            return lis
148
149    try:
150        lis = ((re.findall('SystemGroup [\w* ]*',fp.read()))[0].replace('SystemGroup ','')).split(' ')
151    except IndexError:
152        return lis
153
154    if 'root' in lis:
155        lis.remove('root')
156    fp.close()
157    return lis
158
159def lock(f):
160    log.debug("Locking: %s" % f.name)
161    try:
162        fcntl.flock(f.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
163        return True
164    except (IOError, OSError):
165        log.debug("Failed to unlock %s." % f.name)
166        return False
167
168
169def unlock(f):
170    if f is not None:
171        log.debug("Unlocking: %s" % f.name)
172        try:
173            fcntl.flock(f.fileno(), fcntl.LOCK_UN)
174            os.remove(f.name)
175        except (IOError, OSError):
176            pass
177
178
179def lock_app(application, suppress_error=False):
180    dir = prop.user_dir
181    if os.geteuid() == 0:
182        dir = '/var'
183
184    elif not os.path.exists(dir):
185        os.makedirs(dir)
186
187    lock_file = os.path.join(dir, '.'.join([application, 'lock']))
188    try:
189        lock_file_f = open(lock_file, "w")
190    except IOError:
191        if not suppress_error:
192            log.error("Unable to open %s lock file." % lock_file)
193        return False, None
194
195    #log.debug("Locking file: %s" % lock_file)
196
197    if not lock(lock_file_f):
198        if not suppress_error:
199            log.error("Unable to lock %s. Is %s already running?" % (lock_file, application))
200        return False, None
201
202    return True, lock_file_f
203
204
205#xml_basename_pat = re.compile(r"""HPLIP-(\d*)_(\d*)_(\d*).xml""", re.IGNORECASE)
206
207
208def Translator(frm=to_bytes_utf8(''), to=to_bytes_utf8(''), delete=to_bytes_utf8(''), keep=None):  #Need Revisit
209    if len(to) == 1:
210        to = to * len(frm)
211
212    if PY3:
213        data_types = bytes
214    else:
215        data_types = string
216
217    allchars = data_types.maketrans(to_bytes_utf8(''), to_bytes_utf8(''))
218    trans = data_types.maketrans(frm, to)
219
220    if keep is not None:
221        delete = allchars.translate(allchars, keep.translate(allchars, delete))
222
223    def callable(s):
224        return s.translate(trans, delete)
225
226    return callable
227
228
229def list_to_string(lis):
230    if len(lis) == 0:
231        return ""
232    if len(lis) == 1:
233        return str("\""+lis[0]+"\"")
234    if len(lis) >= 1:
235        return "\""+"\", \"".join(lis)+"\" and \""+str(lis.pop())+"\""
236
237def to_bool_str(s, default='0'):
238    """ Convert an arbitrary 0/1/T/F/Y/N string to a normalized string 0/1."""
239    if isinstance(s, str) and s:
240        if s[0].lower() in ['1', 't', 'y']:
241            return to_unicode('1')
242        elif s[0].lower() in ['0', 'f', 'n']:
243            return to_unicode('0')
244
245    return default
246
247def to_bool(s, default=False):
248    """ Convert an arbitrary 0/1/T/F/Y/N string to a boolean True/False value."""
249    if isinstance(s, str) and s:
250        if s[0].lower() in ['1', 't', 'y']:
251            return True
252        elif s[0].lower() in ['0', 'f', 'n']:
253            return False
254    elif isinstance(s, bool):
255        return s
256
257    return default
258
259
260# Compare with os.walk()
261def walkFiles(root, recurse=True, abs_paths=False, return_folders=False, pattern='*', path=None):
262    if path is None:
263        path = root
264
265    try:
266        names = os.listdir(root)
267    except os.error:
268       #raise StopIteration
269        return
270
271    pattern = pattern or '*'
272    pat_list = pattern.split(';')
273
274    for name in names:
275        fullname = os.path.normpath(os.path.join(root, name))
276
277        for pat in pat_list:
278            if fnmatch.fnmatch(name, pat):
279                if return_folders or not os.path.isdir(fullname):
280                    if abs_paths:
281                        yield fullname
282                    else:
283                        try:
284                            yield os.path.basename(fullname)
285                        except ValueError:
286                            yield fullname
287
288        #if os.path.islink(fullname):
289        #    fullname = os.path.realpath(os.readlink(fullname))
290
291        if recurse and os.path.isdir(fullname): # or os.path.islink(fullname):
292            for f in walkFiles(fullname, recurse, abs_paths, return_folders, pattern, path):
293                yield f
294
295
296def is_path_writable(path):
297    if os.path.exists(path):
298        s = os.stat(path)
299        mode = s[stat.ST_MODE] & 0o777
300
301        if mode & 0o2:
302            return True
303        elif s[stat.ST_GID] == os.getgid() and mode & 0o20:
304            return True
305        elif s[stat.ST_UID] == os.getuid() and mode & 0o200:
306            return True
307
308    return False
309
310
311# Provides the TextFormatter class for formatting text into columns.
312# Original Author: Hamish B Lawson, 1999
313# Modified by: Don Welch, 2003
314class TextFormatter:
315
316    LEFT  = 0
317    CENTER = 1
318    RIGHT  = 2
319
320    def __init__(self, colspeclist):
321        self.columns = []
322        for colspec in colspeclist:
323            self.columns.append(Column(**colspec))
324
325    def compose(self, textlist, add_newline=False):
326        numlines = 0
327        textlist = list(textlist)
328        if len(textlist) != len(self.columns):
329            log.error("Formatter: Number of text items does not match columns")
330            return
331        for text, column in list(map(lambda *x: x, textlist, self.columns)):
332            column.wrap(text)
333            numlines = max(numlines, len(column.lines))
334        complines = [''] * numlines
335        for ln in range(numlines):
336            for column in self.columns:
337                complines[ln] = complines[ln] + column.getline(ln)
338        if add_newline:
339            return '\n'.join(complines) + '\n'
340        else:
341            return '\n'.join(complines)
342
343class Column:
344
345    def __init__(self, width=78, alignment=TextFormatter.LEFT, margin=0):
346        self.width = int(width)
347        self.alignment = alignment
348        self.margin = margin
349        self.lines = []
350
351    def align(self, line):
352        if self.alignment == TextFormatter.CENTER:
353            return line.center(self.width)
354        elif self.alignment == TextFormatter.RIGHT:
355            return line.rjust(self.width)
356        else:
357            return line.ljust(self.width)
358
359    def wrap(self, text):
360        self.lines = []
361        words = []
362        for word in text.split():
363            if word <= str(self.width):
364                words.append(word)
365            else:
366                for i in range(0, len(word), self.width):
367                    words.append(word[i:i+self.width])
368        if not len(words): return
369        current = words.pop(0)
370        for word in words:
371            increment = 1 + len(word)
372            if len(current) + increment > self.width:
373                self.lines.append(self.align(current))
374                current = word
375            else:
376                current = current + ' ' + word
377        self.lines.append(self.align(current))
378
379    def getline(self, index):
380        if index < len(self.lines):
381            return ' '*self.margin + self.lines[index]
382        else:
383            return ' ' * (self.margin + self.width)
384
385
386
387class Stack:
388    def __init__(self):
389        self.stack = []
390
391    def pop(self):
392        return self.stack.pop()
393
394    def push(self, value):
395        self.stack.append(value)
396
397    def as_list(self):
398        return self.stack
399
400    def clear(self):
401        self.stack = []
402
403    def __len__(self):
404        return len(self.stack)
405
406
407
408class Queue(Stack):
409    def __init__(self):
410        Stack.__init__(self)
411
412    def get(self):
413        return self.stack.pop(0)
414
415    def put(self, value):
416        Stack.push(self, value)
417
418
419
420# RingBuffer class
421# Source: Python Cookbook 1st Ed., sec. 5.18, pg. 201
422# Credit: Sebastien Keim
423# License: Modified BSD
424class RingBuffer:
425    def __init__(self, size_max=50):
426        self.max = size_max
427        self.data = []
428
429    def append(self,x):
430        """append an element at the end of the buffer"""
431        self.data.append(x)
432
433        if len(self.data) == self.max:
434            self.cur = 0
435            self.__class__ = RingBufferFull
436
437    def replace(self, x):
438        """replace the last element instead off appending"""
439        self.data[-1] = x
440
441    def get(self):
442        """ return a list of elements from the oldest to the newest"""
443        return self.data
444
445
446class RingBufferFull:
447    def __init__(self, n):
448        #raise "you should use RingBuffer"
449        pass
450
451    def append(self, x):
452        self.data[self.cur] = x
453        self.cur = (self.cur+1) % self.max
454
455    def replace(self, x):
456        # back up 1 position to previous location
457        self.cur = (self.cur-1) % self.max
458        self.data[self.cur] = x
459        # setup for next item
460        self.cur = (self.cur+1) % self.max
461
462    def get(self):
463        return self.data[self.cur:] + self.data[:self.cur]
464
465
466
467def sort_dict_by_value(d):
468    """ Returns the keys of dictionary d sorted by their values """
469    items=list(d.items())
470    backitems=[[v[1],v[0]] for v in items]
471    backitems.sort()
472    return [backitems[i][1] for i in range(0, len(backitems))]
473
474
475def commafy(val):
476    return locale.format("%s", val, grouping=True)
477
478
479def format_bytes(s, show_bytes=False):
480    if s < 1024:
481        return ''.join([commafy(s), ' B'])
482    elif 1024 < s < 1048576:
483        if show_bytes:
484            return ''.join([to_unicode(round(s/1024.0, 1)) , to_unicode(' KB ('),  commafy(s), ')'])
485        else:
486            return ''.join([to_unicode(round(s/1024.0, 1)) , to_unicode(' KB')])
487    elif 1048576 < s < 1073741824:
488        if show_bytes:
489            return ''.join([to_unicode(round(s/1048576.0, 1)), to_unicode(' MB ('),  commafy(s), ')'])
490        else:
491            return ''.join([to_unicode(round(s/1048576.0, 1)), to_unicode(' MB')])
492    else:
493        if show_bytes:
494            return ''.join([to_unicode(round(s/1073741824.0, 1)), to_unicode(' GB ('),  commafy(s), ')'])
495        else:
496            return ''.join([to_unicode(round(s/1073741824.0, 1)), to_unicode(' GB')])
497
498
499
500try:
501    make_temp_file = tempfile.mkstemp # 2.3+
502except AttributeError:
503    def make_temp_file(suffix='', prefix='', dir='', text=False): # pre-2.3
504        path = tempfile.mktemp(suffix)
505        fd = os.open(path, os.O_RDWR|os.O_CREAT|os.O_EXCL, 0o700)
506        return ( os.fdopen( fd, 'w+b' ), path )
507
508
509
510def which(command, return_full_path=False):
511    path=[]
512    path_val = os.getenv('PATH')
513    if path_val:
514        path = path_val.split(':')
515
516    path.append('/usr/bin')
517    path.append('/usr/local/bin')
518    # Add these paths for Fedora
519    path.append('/sbin')
520    path.append('/usr/sbin')
521    path.append('/usr/local/sbin')
522
523    found_path = ''
524    for p in path:
525        try:
526            files = os.listdir(p)
527        except OSError:
528            continue
529        else:
530            if command in files:
531                found_path = p
532                break
533
534    if return_full_path:
535        if found_path:
536            return os.path.join(found_path, command)
537        else:
538            return ''
539    else:
540        return found_path
541
542
543class UserSettings(object): # Note: Deprecated after 2.8.8 in Qt4 (see ui4/ui_utils.py)
544    def __init__(self):
545        self.load()
546
547    def loadDefaults(self):
548        # Print
549        self.cmd_print = ''
550        path = which('hp-print')
551
552        if len(path) > 0:
553            self.cmd_print = 'hp-print -p%PRINTER%'
554        else:
555            path = which('kprinter')
556            if len(path) > 0:
557                self.cmd_print = 'kprinter -P%PRINTER% --system cups'
558            else:
559                path = which('gtklp')
560                if len(path) > 0:
561                    self.cmd_print = 'gtklp -P%PRINTER%'
562                else:
563                    path = which('xpp')
564                    if len(path) > 0:
565                        self.cmd_print = 'xpp -P%PRINTER%'
566
567        # Scan
568        self.cmd_scan = ''
569        path = which('xsane')
570
571        if len(path) > 0:
572            self.cmd_scan = 'xsane -V %SANE_URI%'
573        else:
574            path = which('kooka')
575            if len(path) > 0:
576                self.cmd_scan = 'kooka'
577            else:
578                path = which('xscanimage')
579                if len(path) > 0:
580                    self.cmd_scan = 'xscanimage'
581
582        # Photo Card
583        path = which('hp-unload')
584
585        if len(path):
586            self.cmd_pcard = 'hp-unload -d %DEVICE_URI%'
587        else:
588            self.cmd_pcard = 'python %HOME%/unload.py -d %DEVICE_URI%'
589
590        # Copy
591        path = which('hp-makecopies')
592
593        if len(path):
594            self.cmd_copy = 'hp-makecopies -d %DEVICE_URI%'
595        else:
596            self.cmd_copy = 'python %HOME%/makecopies.py -d %DEVICE_URI%'
597
598        # Fax
599        path = which('hp-sendfax')
600
601        if len(path):
602            self.cmd_fax = 'hp-sendfax -d %FAX_URI%'
603        else:
604            self.cmd_fax = 'python %HOME%/sendfax.py -d %FAX_URI%'
605
606        # Fax Address Book
607        path = which('hp-fab')
608
609        if len(path):
610            self.cmd_fab = 'hp-fab'
611        else:
612            self.cmd_fab = 'python %HOME%/fab.py'
613
614    def load(self):
615        self.loadDefaults()
616        log.debug("Loading user settings...")
617        self.auto_refresh = to_bool(user_conf.get('refresh', 'enable', '0'))
618
619        try:
620            self.auto_refresh_rate = int(user_conf.get('refresh', 'rate', '30'))
621        except ValueError:
622            self.auto_refresh_rate = 30 # (secs)
623
624        try:
625            self.auto_refresh_type = int(user_conf.get('refresh', 'type', '0'))
626        except ValueError:
627            self.auto_refresh_type = 0 # refresh 1 (1=refresh all)
628
629        self.cmd_print = user_conf.get('commands', 'prnt', self.cmd_print)
630        self.cmd_scan = user_conf.get('commands', 'scan', self.cmd_scan)
631        self.cmd_pcard = user_conf.get('commands', 'pcard', self.cmd_pcard)
632        self.cmd_copy = user_conf.get('commands', 'cpy', self.cmd_copy)
633        self.cmd_fax = user_conf.get('commands', 'fax', self.cmd_fax)
634        self.cmd_fab = user_conf.get('commands', 'fab', self.cmd_fab)
635
636        self.upgrade_notify= to_bool(user_conf.get('upgrade', 'notify_upgrade', '0'))
637        self.upgrade_last_update_time = int(user_conf.get('upgrade','last_upgraded_time', '0'))
638        self.upgrade_pending_update_time =int(user_conf.get('upgrade', 'pending_upgrade_time', '0'))
639        self.latest_available_version=str(user_conf.get('upgrade', 'latest_available_version',''))
640        self.debug()
641
642    def debug(self):
643        log.debug("Print command: %s" % self.cmd_print)
644        log.debug("PCard command: %s" % self.cmd_pcard)
645        log.debug("Fax command: %s" % self.cmd_fax)
646        log.debug("FAB command: %s" % self.cmd_fab)
647        log.debug("Copy command: %s " % self.cmd_copy)
648        log.debug("Scan command: %s" % self.cmd_scan)
649        log.debug("Auto refresh: %s" % self.auto_refresh)
650        log.debug("Auto refresh rate: %s" % self.auto_refresh_rate)
651        log.debug("Auto refresh type: %s" % self.auto_refresh_type)
652        log.debug("Upgrade notification:%d"  %self.upgrade_notify)
653        log.debug("Last Installed time:%d" %self.upgrade_last_update_time)
654        log.debug("Next scheduled installation time:%d" % self.upgrade_pending_update_time)
655
656
657    def save(self):
658        log.debug("Saving user settings...")
659        user_conf.set('commands', 'prnt', self.cmd_print)
660        user_conf.set('commands', 'pcard', self.cmd_pcard)
661        user_conf.set('commands', 'fax', self.cmd_fax)
662        user_conf.set('commands', 'scan', self.cmd_scan)
663        user_conf.set('commands', 'cpy', self.cmd_copy)
664        user_conf.set('refresh', 'enable',self.auto_refresh)
665        user_conf.set('refresh', 'rate', self.auto_refresh_rate)
666        user_conf.set('refresh', 'type', self.auto_refresh_type)
667        user_conf.set('upgrade', 'notify_upgrade', self.upgrade_notify)
668        user_conf.set('upgrade','last_upgraded_time', self.upgrade_last_update_time)
669        user_conf.set('upgrade', 'pending_upgrade_time', self.upgrade_pending_update_time)
670        user_conf.set('upgrade', 'latest_available_version', self.latest_available_version)
671
672        self.debug()
673
674
675
676def no_qt_message_gtk():
677    try:
678        import gtk
679        w = gtk.Window()
680        dialog = gtk.MessageDialog(w, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
681                                   gtk.MESSAGE_WARNING, gtk.BUTTONS_OK,
682                                   "PyQt not installed. GUI not available. Please check that the PyQt package is installed. Exiting.")
683        dialog.run()
684        dialog.destroy()
685
686    except ImportError:
687        log.error("PyQt not installed. GUI not available. Please check that the PyQt package is installed. Exiting.")
688
689
690def canEnterGUIMode(): # qt3
691    if not prop.gui_build:
692        log.warn("GUI mode disabled in build.")
693        return False
694
695    elif not os.getenv('DISPLAY'):
696        log.warn("No display found.")
697        return False
698
699    elif not checkPyQtImport():
700        log.warn("Qt/PyQt 3 initialization failed.")
701        return False
702
703    return True
704
705
706def canEnterGUIMode4(): # qt4
707    if not prop.gui_build:
708        log.warn("GUI mode disabled in build.")
709        return False
710
711    elif not os.getenv('DISPLAY'):
712        log.warn("No display found.")
713        return False
714
715    # elif not checkPyQtImport4():
716    #     log.warn("Qt4/PyQt 4 initialization failed.")
717    #     return False
718    else:
719        try:
720            checkPyQtImport45()
721        except ImportError as e:
722            log.warn(e)
723            return False
724
725    return True
726
727
728def checkPyQtImport(): # qt3
729    # PyQt
730    try:
731        import qt
732        import ui
733    except ImportError:
734        if os.getenv('DISPLAY') and os.getenv('STARTED_FROM_MENU'):
735            no_qt_message_gtk()
736
737        log.error("PyQt not installed. GUI not available. Exiting.")
738        return False
739
740    # check version of Qt
741    qtMajor = int(qt.qVersion().split('.')[0])
742
743    if qtMajor < MINIMUM_QT_MAJOR_VER:
744
745        log.error("Incorrect version of Qt installed. Ver. 3.0.0 or greater required.")
746        return False
747
748    #check version of PyQt
749    try:
750        pyqtVersion = qt.PYQT_VERSION_STR
751    except AttributeError:
752        pyqtVersion = qt.PYQT_VERSION
753
754    while pyqtVersion.count('.') < 2:
755        pyqtVersion += '.0'
756
757    (maj_ver, min_ver, pat_ver) = pyqtVersion.split('.')
758
759    if pyqtVersion.find('snapshot') >= 0:
760        log.warning("A non-stable snapshot version of PyQt is installed.")
761    else:
762        try:
763            maj_ver = int(maj_ver)
764            min_ver = int(min_ver)
765            pat_ver = int(pat_ver)
766        except ValueError:
767            maj_ver, min_ver, pat_ver = 0, 0, 0
768
769        if maj_ver < MINIMUM_PYQT_MAJOR_VER or \
770            (maj_ver == MINIMUM_PYQT_MAJOR_VER and min_ver < MINIMUM_PYQT_MINOR_VER):
771            log.error("This program may not function properly with the version of PyQt that is installed (%d.%d.%d)." % (maj_ver, min_ver, pat_ver))
772            log.error("Incorrect version of pyQt installed. Ver. %d.%d or greater required." % (MINIMUM_PYQT_MAJOR_VER, MINIMUM_PYQT_MINOR_VER))
773            log.error("This program will continue, but you may experience errors, crashes or other problems.")
774            return True
775
776    return True
777
778
779def checkPyQtImport4():
780    try:
781        import PyQt4
782        import ui4
783    except ImportError:
784        import PyQt5
785        import ui5
786    else:
787        log.debug("HPLIP is not installed properly or is installed without graphical support. Please reinstall HPLIP again")
788        return False
789    return True
790
791# def checkPyQtImport5():
792#     try:
793#         import PyQt5
794#         import ui5
795#     except ImportError:
796#         log.error("HPLIP is not installed properly or is installed without graphical support PyQt5. Please reinstall HPLIP")
797#         return False
798#     else:
799#         return True
800
801
802try:
803    from string import Template # will fail in Python <= 2.3
804except ImportError:
805    # Code from Python 2.4 string.py
806    #import re as _re
807
808    class _multimap:
809        """Helper class for combining multiple mappings.
810
811        Used by .{safe_,}substitute() to combine the mapping and keyword
812        arguments.
813        """
814        def __init__(self, primary, secondary):
815            self._primary = primary
816            self._secondary = secondary
817
818        def __getitem__(self, key):
819            try:
820                return self._primary[key]
821            except KeyError:
822                return self._secondary[key]
823
824
825    class _TemplateMetaclass(type):
826        pattern = r"""
827        %(delim)s(?:
828          (?P<escaped>%(delim)s) |   # Escape sequence of two delimiters
829          (?P<named>%(id)s)      |   # delimiter and a Python identifier
830          {(?P<braced>%(id)s)}   |   # delimiter and a braced identifier
831          (?P<invalid>)              # Other ill-formed delimiter exprs
832        )
833        """
834
835        def __init__(cls, name, bases, dct):
836            super(_TemplateMetaclass, cls).__init__(name, bases, dct)
837            if 'pattern' in dct:
838                pattern = cls.pattern
839            else:
840                pattern = _TemplateMetaclass.pattern % {
841                    'delim' : re.escape(cls.delimiter),
842                    'id'    : cls.idpattern,
843                    }
844            cls.pattern = re.compile(pattern, re.IGNORECASE | re.VERBOSE)
845
846    # if PY3:
847    #     class Template(metaclass=_TemplateMetaclass):
848    #         """A string class for supporting $-substitutions."""
849    # else:
850    class Template:
851        """A string class for supporting $-substitutions."""
852        __metaclass__ = _TemplateMetaclass
853
854        delimiter = '$'
855        idpattern = r'[_a-z][_a-z0-9]*'
856
857        def __init__(self, template):
858            self.template = template
859
860        # Search for $$, $identifier, ${identifier}, and any bare $'s
861        def _invalid(self, mo):
862            i = mo.start('invalid')
863            lines = self.template[:i].splitlines(True)
864            if not lines:
865                colno = 1
866                lineno = 1
867            else:
868                colno = i - len(''.join(lines[:-1]))
869                lineno = len(lines)
870            raise ValueError('Invalid placeholder in string: line %d, col %d' %
871                             (lineno, colno))
872
873        def substitute(self, *args, **kws):
874            if len(args) > 1:
875                raise TypeError('Too many positional arguments')
876            if not args:
877                mapping = kws
878            elif kws:
879                mapping = _multimap(kws, args[0])
880            else:
881                mapping = args[0]
882            # Helper function for .sub()
883            def convert(mo):
884                # Check the most common path first.
885                named = mo.group('named') or mo.group('braced')
886                if named is not None:
887                    val = mapping[named]
888                    # We use this idiom instead of str() because the latter will
889                    # fail if val is a Unicode containing non-ASCII characters.
890                    return '%s' % val
891                if mo.group('escaped') is not None:
892                    return self.delimiter
893                if mo.group('invalid') is not None:
894                    self._invalid(mo)
895                raise ValueError('Unrecognized named group in pattern',
896                                 self.pattern)
897            return self.pattern.sub(convert, self.template)
898
899
900        def safe_substitute(self, *args, **kws):
901            if len(args) > 1:
902                raise TypeError('Too many positional arguments')
903            if not args:
904                mapping = kws
905            elif kws:
906                mapping = _multimap(kws, args[0])
907            else:
908                mapping = args[0]
909            # Helper function for .sub()
910            def convert(mo):
911                named = mo.group('named')
912                if named is not None:
913                    try:
914                        # We use this idiom instead of str() because the latter
915                        # will fail if val is a Unicode containing non-ASCII
916                        return '%s' % mapping[named]
917                    except KeyError:
918                        return self.delimiter + named
919                braced = mo.group('braced')
920                if braced is not None:
921                    try:
922                        return '%s' % mapping[braced]
923                    except KeyError:
924                        return self.delimiter + '{' + braced + '}'
925                if mo.group('escaped') is not None:
926                    return self.delimiter
927                if mo.group('invalid') is not None:
928                    return self.delimiter
929                raise ValueError('Unrecognized named group in pattern',
930                                 self.pattern)
931            return self.pattern.sub(convert, self.template)
932
933
934
935#cat = lambda _ : Template(_).substitute(sys._getframe(1).f_globals, **sys._getframe(1).f_locals)
936
937def cat(s):
938    globals = sys._getframe(1).f_globals.copy()
939    if 'self' in globals:
940        del globals['self']
941
942    locals = sys._getframe(1).f_locals.copy()
943    if 'self' in locals:
944        del locals['self']
945
946    return Template(s).substitute(sys._getframe(1).f_globals, **locals)
947
948if PY3:
949    identity = bytes.maketrans(b'', b'')
950    unprintable = identity.translate(identity, string.printable.encode('utf-8'))
951else:
952    identity = string.maketrans('','')
953    unprintable = identity.translate(identity, string.printable)
954
955
956def printable(s):
957    if s:
958        return s.translate(identity, unprintable)
959    else:
960        return ""
961
962
963def any(S,f=lambda x:x):
964    for x in S:
965        if f(x): return True
966    return False
967
968
969def all(S,f=lambda x:x):
970    for x in S:
971        if not f(x): return False
972    return True
973
974BROWSERS = ['firefox', 'mozilla', 'konqueror', 'epiphany', 'skipstone'] # in preferred order
975BROWSER_OPTS = {'firefox': '-new-tab', 'mozilla': '', 'konqueror': '', 'epiphany': '--new-tab', 'skipstone': ''}
976
977
978def find_browser():
979    if platform_avail and platform.system() == 'Darwin':
980        return "open"
981    elif which("xdg-open"):
982        return "xdg-open"
983    else:
984        for b in BROWSERS:
985            if which(b):
986                return b
987        else:
988            return None
989
990
991def openURL(url, use_browser_opts=True):
992    if platform_avail and platform.system() == 'Darwin':
993        cmd = 'open "%s"' % url
994        os_utils.execute(cmd)
995    elif which("xdg-open"):
996        cmd = 'xdg-open "%s"' % url
997        os_utils.execute(cmd)
998    else:
999        for b in BROWSERS:
1000            bb = which(b, return_full_path='True')
1001            if bb:
1002                if use_browser_opts:
1003                    cmd = """%s %s "%s" &""" % (bb, BROWSER_OPTS[b], url)
1004                else:
1005                    cmd = """%s "%s" &""" % (bb, url)
1006                os_utils.execute(cmd)
1007                break
1008        else:
1009            log.warn("Unable to open URL: %s" % url)
1010
1011
1012def uniqueList(input):
1013    temp = []
1014    [temp.append(i) for i in input if not temp.count(i)]
1015    return temp
1016
1017
1018def list_move_up(l, m, cmp=None):
1019    if cmp is None:
1020        f = lambda x: l[x] == m
1021    else:
1022        f = lambda x: cmp(l[x], m)
1023
1024    for i in range(1, len(l)):
1025        if f(i):
1026            l[i-1], l[i] = l[i], l[i-1]
1027
1028
1029def list_move_down(l, m, cmp=None):
1030    if cmp is None:
1031        f = lambda x: l[x] == m
1032    else:
1033        f = lambda x: cmp(l[x], m)
1034
1035    for i in range(len(l)-2, -1, -1):
1036        if f(i):
1037            l[i], l[i+1] = l[i+1], l[i]
1038
1039
1040
1041class XMLToDictParser:
1042    def __init__(self):
1043        self.stack = []
1044        self.data = {}
1045        self.last_start = ''
1046
1047    def startElement(self, name, attrs):
1048        #print "START:", name, attrs
1049        self.stack.append(to_unicode(name).lower())
1050        self.last_start = to_unicode(name).lower()
1051
1052        if len(attrs):
1053            for a in attrs:
1054                self.stack.append(to_unicode(a).lower())
1055                self.addData(attrs[a])
1056                self.stack.pop()
1057
1058    def endElement(self, name):
1059        if name.lower() == self.last_start:
1060            self.addData('')
1061
1062        #print "END:", name
1063        self.stack.pop()
1064
1065    def charData(self, data):
1066        data = to_unicode(data).strip()
1067
1068        if data and self.stack:
1069            self.addData(data)
1070
1071    def addData(self, data):
1072        #print("DATA:%s" % data)
1073        self.last_start = ''
1074        try:
1075            data = int(data)
1076        except ValueError:
1077            data = to_unicode(data)
1078
1079        stack_str = '-'.join(self.stack)
1080        stack_str_0 = '-'.join([stack_str, '0'])
1081
1082        try:
1083            self.data[stack_str]
1084        except KeyError:
1085            try:
1086                self.data[stack_str_0]
1087            except KeyError:
1088                self.data[stack_str] = data
1089            else:
1090                j = 2
1091                while True:
1092                    try:
1093                        self.data['-'.join([stack_str, to_unicode(j)])]
1094                    except KeyError:
1095                        self.data['-'.join([stack_str, to_unicode(j)])] = data
1096                        break
1097                    j += 1
1098
1099        else:
1100            self.data[stack_str_0] = self.data[stack_str]
1101            self.data['-'.join([stack_str, '1'])] = data
1102            del self.data[stack_str]
1103
1104
1105    def parseXML(self, text):
1106        if xml_expat_avail:
1107            parser = expat.ParserCreate()
1108
1109            parser.StartElementHandler = self.startElement
1110            parser.EndElementHandler = self.endElement
1111            parser.CharacterDataHandler = self.charData
1112
1113            parser.Parse(text, True)
1114
1115        else:
1116            log.error("Failed to import expat module , check python-xml/python3-xml package installation.")
1117
1118        return self.data
1119
1120
1121class Element:
1122    def __init__(self,name,attributes):
1123        self.name = name
1124        self.attributes = attributes
1125        self.chardata = ''
1126        self.children = []
1127
1128    def AddChild(self,element):
1129        self.children.append(element)
1130
1131    def getAttribute(self,key):
1132        return self.attributes.get(key)
1133
1134    def getData(self):
1135        return self.chardata
1136
1137    def getElementsByTagName(self,name='',ElementNode=None):
1138        if ElementNode:
1139            Children_list = ElementNode.children
1140        else:
1141            Children_list = self.children
1142        if not name:
1143            return self.children
1144        else:
1145            elements = []
1146            for element in Children_list:
1147                if element.name == name:
1148                    elements.append(element)
1149
1150                rec_elements = self.getElementsByTagName (name,element)
1151                for a in rec_elements:
1152                    elements.append(a)
1153            return elements
1154
1155    def getChildElements(self,name=''):
1156        if not name:
1157            return self.children
1158        else:
1159            elements = []
1160            for element in self.children:
1161                if element.name == name:
1162                    elements.append(element)
1163            return elements
1164
1165    def toString(self, level=0):
1166        retval = " " * level
1167        retval += "<%s" % self.name
1168        for attribute in self.attributes:
1169            retval += " %s=\"%s\"" % (attribute, self.attributes[attribute])
1170        c = ""
1171        for child in self.children:
1172            c += child.toString(level+1)
1173        if c == "":
1174            if self.chardata:
1175                retval += ">"+self.chardata + ("</%s>" % self.name)
1176            else:
1177                retval += "/>"
1178        else:
1179            retval += ">" + c + ("</%s>" % self.name)
1180        return retval
1181
1182class  extendedExpat:
1183    def __init__(self):
1184        self.root = None
1185        self.nodeStack = []
1186
1187    def StartElement_EE(self,name,attributes):
1188        element = Element(name, attributes)
1189
1190        if len(self.nodeStack) > 0:
1191            parent = self.nodeStack[-1]
1192            parent.AddChild(element)
1193        else:
1194            self.root = element
1195        self.nodeStack.append(element)
1196
1197    def EndElement_EE(self,name):
1198        self.nodeStack = self.nodeStack[:-1]
1199
1200    def charData_EE(self,data):
1201        if data:
1202            element = self.nodeStack[-1]
1203            element.chardata += data
1204            return
1205
1206    def Parse(self,xmlString):
1207        if xml_expat_avail:
1208            Parser = expat.ParserCreate()
1209
1210            Parser.StartElementHandler = self.StartElement_EE
1211            Parser.EndElementHandler = self.EndElement_EE
1212            Parser.CharacterDataHandler = self.charData_EE
1213
1214            Parser.Parse(xmlString, True)
1215        else:
1216            log.error("Failed to import expat module , check python-xml/python3-xml package installation.")
1217
1218        return self.root
1219
1220
1221
1222def dquote(s):
1223    return ''.join(['"', s, '"'])
1224
1225
1226# Python 2.2.x compatibility functions (strip() family with char argument added in Python 2.2.3)
1227if sys.hexversion < 0x020203f0:
1228    def xlstrip(s, chars=' '):
1229        i = 0
1230        for c, i in zip(s, list(range(len(s)))):
1231            if c not in chars:
1232                break
1233
1234        return s[i:]
1235
1236    def xrstrip(s, chars=' '):
1237        return xreverse(xlstrip(xreverse(s), chars))
1238
1239    def xreverse(s):
1240        l = list(s)
1241        l.reverse()
1242        return ''.join(l)
1243
1244    def xstrip(s, chars=' '):
1245        return xreverse(xlstrip(xreverse(xlstrip(s, chars)), chars))
1246
1247else:
1248    xlstrip = str.lstrip
1249    xrstrip = str.rstrip
1250    xstrip = str.strip
1251
1252
1253def getBitness():
1254    if platform_avail:
1255        return int(platform.architecture()[0][:-3])
1256    else:
1257        return struct.calcsize("P") << 3
1258
1259
1260def getProcessor():
1261    if platform_avail:
1262        return platform.machine().replace(' ', '_').lower() # i386, i686, power_macintosh, etc.
1263    else:
1264        return "i686" # TODO: Need a fix here
1265
1266
1267def getEndian():
1268    if sys.byteorder == 'big':
1269        return BIG_ENDIAN
1270    else:
1271        return LITTLE_ENDIAN
1272
1273
1274#
1275# Function: run()
1276#   Note:- to run su/sudo commands, caller needs to pass passwordObj.
1277#          password object can be created from base.password.py
1278
1279def run(cmd, passwordObj = None, pswd_msg='', log_output=True, spinner=True, timeout=1):
1280    import io
1281    output = io.StringIO()
1282
1283    pwd_prompt_str = ""
1284    if passwordObj and ('su' in cmd or 'sudo' in cmd) and os.geteuid() != 0:
1285        pwd_prompt_str = passwordObj.getPasswordPromptString()
1286        log.debug("cmd = %s pwd_prompt_str = [%s]"%(cmd, pwd_prompt_str))
1287        if(pwd_prompt_str == ""):
1288            passwd = passwordObj.getPassword(pswd_msg, 0)
1289            pwd_prompt_str = passwordObj.getPasswordPromptString()
1290            log.debug("pwd_prompt_str2 = [%s]"%(pwd_prompt_str))
1291            if(passwd == ""):
1292               return 127, ""
1293
1294    try:
1295        child = pexpect.spawnu(cmd, timeout=timeout)
1296    except pexpect.ExceptionPexpect as e:
1297        return -1, ''
1298
1299    try:
1300        pswd_queried_cnt = 0
1301        while True:
1302            if spinner:
1303                update_spinner()
1304
1305            try:
1306                i = child.expect(EXPECT_LIST)
1307            except Exception:
1308                continue
1309
1310            if child.before:
1311                if(pwd_prompt_str and pwd_prompt_str not in EXPECT_LIST):
1312                    log.debug("Adding %s to EXPECT LIST"%pwd_prompt_str)
1313                    try:
1314                        p = re.compile(pwd_prompt_str, re.I)
1315                    except TypeError:
1316                        EXPECT_LIST.append(pwd_prompt_str)
1317                    else:
1318                        EXPECT_LIST.append(p)
1319                        EXPECT_LIST.append(pwd_prompt_str)
1320
1321                try:
1322                    output.write(child.before)
1323                    if log_output:
1324                        log.debug(child.before)
1325                except Exception:
1326                    pass
1327
1328            if i == 0: # EOF
1329                break
1330
1331            elif i == 1: # TIMEOUT
1332                continue
1333
1334            elif i == 2:    # zypper
1335                child.sendline("YES")
1336
1337            else: # Password:
1338                if not passwordObj :
1339                    raise Exception("password Object(i.e. passwordObj) is not valid")
1340
1341                child.sendline(passwordObj.getPassword(pswd_msg, pswd_queried_cnt))
1342                pswd_queried_cnt += 1
1343
1344    except Exception as e:
1345        log.error("Exception: %s" % e)
1346    if spinner:
1347        cleanup_spinner()
1348    try:
1349        child.close()
1350    except pexpect.ExceptionPexpect as e:
1351        pass
1352
1353
1354    return child.exitstatus, output.getvalue()
1355
1356
1357
1358def expand_range(ns): # ns -> string repr. of numeric range, e.g. "1-4, 7, 9-12"
1359    """Credit: Jean Brouwers, comp.lang.python 16-7-2004
1360       Convert a string representation of a set of ranges into a
1361       list of ints, e.g.
1362       u"1-4, 7, 9-12" --> [1,2,3,4,7,9,10,11,12]
1363    """
1364    fs = []
1365    for n in ns.split(to_unicode(',')):
1366        n = n.strip()
1367        r = n.split('-')
1368        if len(r) == 2:  # expand name with range
1369            h = r[0].rstrip(to_unicode('0123456789'))  # header
1370            r[0] = r[0][len(h):]
1371             # range can't be empty
1372            if not (r[0] and r[1]):
1373                raise ValueError('empty range: ' + n)
1374             # handle leading zeros
1375            if r[0] == to_unicode('0') or to_unicode(r[0][0]) != '0':
1376                h += '%d'
1377            else:
1378                w = [len(i) for i in r]
1379                if w[1] > w[0]:
1380                   raise ValueError('wide range: ' + n)
1381                h += to_unicode('%%0%dd') % max(w)
1382             # check range
1383            r = [int(i, 10) for i in r]
1384            if r[0] > r[1]:
1385               raise ValueError('bad range: ' + n)
1386            for i in range(r[0], r[1]+1):
1387                fs.append(h % i)
1388        else:  # simple name
1389            fs.append(n)
1390
1391     # remove duplicates
1392    fs = list(dict([(n, i) for i, n in enumerate(fs)]).keys())
1393     # convert to ints and sort
1394    fs = [int(x) for x in fs if x]
1395    fs.sort()
1396
1397    return fs
1398
1399
1400def collapse_range(x): # x --> sorted list of ints
1401    """ Convert a list of integers into a string
1402        range representation:
1403        [1,2,3,4,7,9,10,11,12] --> u"1-4,7,9-12"
1404    """
1405    if not x:
1406        return ''
1407
1408    s, c, r = [str(x[0])], x[0], False
1409
1410    for i in x[1:]:
1411        if i == (c+1):
1412            r = True
1413        else:
1414            if r:
1415                s.append(to_unicode('-%s,%s') % (c,i))
1416                r = False
1417            else:
1418                s.append(to_unicode(',%s') % i)
1419
1420        c = i
1421
1422    if r:
1423        s.append(to_unicode('-%s') % i)
1424
1425    return ''.join(s)
1426
1427def createBBSequencedFilename(basename, ext, dir=None, digits=3):
1428    if dir is None:
1429        dir = os.getcwd()
1430
1431    m = 0
1432    for f in walkFiles(dir, recurse=False, abs_paths=False, return_folders=False, pattern='*', path=None):
1433        r, e = os.path.splitext(f)
1434
1435        if r.startswith(basename) and ext == e:
1436            try:
1437                i = int(r[len(basename):])
1438            except ValueError:
1439                continue
1440            else:
1441                m = max(m, i)
1442
1443    return os.path.join(dir, "%s%0*d%s" % (basename, digits, m+1, ext))
1444
1445
1446def createSequencedFilename(basename, ext, dir=None, digits=3):
1447    if dir is None:
1448        dir = os.getcwd()
1449
1450    m = 0
1451    for f in walkFiles(dir, recurse=False, abs_paths=False, return_folders=False, pattern='*', path=None):
1452        r, e = os.path.splitext(f)
1453
1454        if r.startswith(basename) and ext == e:
1455            try:
1456                i = int(r[len(basename):])
1457            except ValueError:
1458                continue
1459            else:
1460                m = max(m, i)
1461
1462    return os.path.join(dir, "%s%0*d%s" % (basename, digits, m+1, ext))
1463
1464def validate_language(lang):
1465    if lang is None:
1466        loc = os_utils.getSystemLocale()
1467    else:
1468        lang = lang.lower().strip()
1469        for loc, ll in list(supported_locales.items()):
1470            if lang in ll:
1471                break
1472        else:
1473            loc = 'en_US'
1474            log.warn("Unknown lang/locale. Using default of %s." % loc)
1475
1476    return loc
1477
1478
1479def gen_random_uuid():
1480    try:
1481        import uuid # requires Python 2.5+
1482        return str(uuid.uuid4())
1483
1484    except ImportError:
1485        uuidgen = which("uuidgen")
1486        if uuidgen:
1487            uuidgen = os.path.join(uuidgen, "uuidgen")
1488            return subprocess.getoutput(uuidgen)
1489        else:
1490            return ''
1491
1492
1493class RestTableFormatter(object):
1494    def __init__(self, header=None):
1495        self.header = header # tuple of strings
1496        self.rows = [] # list of tuples
1497
1498    def add(self, row_data): # tuple of strings
1499        self.rows.append(row_data)
1500
1501    def output(self, w):
1502        if self.rows:
1503            num_cols = len(self.rows[0])
1504            for r in self.rows:
1505                if len(r) != num_cols:
1506                    log.error("Invalid number of items in row: %s" % r)
1507                    return
1508
1509            if len(self.header) != num_cols:
1510                log.error("Invalid number of items in header.")
1511
1512            col_widths = []
1513            for x, c in enumerate(self.header):
1514                max_width = len(c)
1515                for r in self.rows:
1516                    max_width = max(max_width, len(r[x]))
1517
1518                col_widths.append(max_width+2)
1519
1520            x = '+'
1521            for c in col_widths:
1522                x = ''.join([x, '-' * (c+2), '+'])
1523
1524            x = ''.join([x, '\n'])
1525            w.write(x)
1526
1527            # header
1528            if self.header:
1529                x = '|'
1530                for i, c in enumerate(col_widths):
1531                    x = ''.join([x, ' ', self.header[i], ' ' * (c+1-len(self.header[i])), '|'])
1532
1533                x = ''.join([x, '\n'])
1534                w.write(x)
1535
1536                x = '+'
1537                for c in col_widths:
1538                    x = ''.join([x, '=' * (c+2), '+'])
1539
1540                x = ''.join([x, '\n'])
1541                w.write(x)
1542
1543            # data rows
1544            for j, r in enumerate(self.rows):
1545                x = '|'
1546                for i, c in enumerate(col_widths):
1547                    x = ''.join([x, ' ', self.rows[j][i], ' ' * (c+1-len(self.rows[j][i])), '|'])
1548
1549                x = ''.join([x, '\n'])
1550                w.write(x)
1551
1552                x = '+'
1553                for c in col_widths:
1554                    x = ''.join([x, '-' * (c+2), '+'])
1555
1556                x = ''.join([x, '\n'])
1557                w.write(x)
1558
1559        else:
1560            log.error("No data rows")
1561
1562
1563def mixin(cls):
1564    import inspect
1565
1566    locals = inspect.stack()[1][0].f_locals
1567    if "__module__" not in locals:
1568        raise TypeError("Must call mixin() from within class def.")
1569
1570    dict = cls.__dict__.copy()
1571    dict.pop("__doc__", None)
1572    dict.pop("__module__", None)
1573
1574    locals.update(dict)
1575
1576
1577
1578# TODO: Move usage stuff to to base/module/Module class
1579
1580
1581 # ------------------------- Usage Help
1582USAGE_OPTIONS = ("[OPTIONS]", "", "heading", False)
1583USAGE_LOGGING1 = ("Set the logging level:", "-l<level> or --logging=<level>", 'option', False)
1584USAGE_LOGGING2 = ("", "<level>: none, info\*, error, warn, debug (\*default)", "option", False)
1585USAGE_LOGGING3 = ("Run in debug mode:", "-g (same as option: -ldebug)", "option", False)
1586USAGE_LOGGING_PLAIN = ("Output plain text only:", "-t", "option", False)
1587USAGE_ARGS = ("[PRINTER|DEVICE-URI]", "", "heading", False)
1588USAGE_ARGS2 = ("[PRINTER]", "", "heading", False)
1589USAGE_DEVICE = ("To specify a device-URI:", "-d<device-uri> or --device=<device-uri>", "option", False)
1590USAGE_PRINTER = ("To specify a CUPS printer:", "-p<printer> or --printer=<printer>", "option", False)
1591USAGE_BUS1 = ("Bus to probe (if device not specified):", "-b<bus> or --bus=<bus>", "option", False)
1592USAGE_BUS2 = ("", "<bus>: cups\*, usb\*, net, bt, fw, par\* (\*defaults) (Note: bt and fw not supported in this release.)", 'option', False)
1593USAGE_HELP = ("This help information:", "-h or --help", "option", True)
1594USAGE_SPACE = ("", "", "space", False)
1595USAGE_EXAMPLES = ("Examples:", "", "heading", False)
1596USAGE_NOTES = ("Notes:", "", "heading", False)
1597USAGE_STD_NOTES1 = ("If device or printer is not specified, the local device bus is probed and the program enters interactive mode.", "", "note", False)
1598USAGE_STD_NOTES2 = ("If -p\* is specified, the default CUPS printer will be used.", "", "note", False)
1599USAGE_SEEALSO = ("See Also:", "", "heading", False)
1600USAGE_LANGUAGE = ("Set the language:", "--loc=<lang> or --lang=<lang>. Use --loc=? or --lang=? to see a list of available language codes.", "option", False)
1601USAGE_LANGUAGE2 = ("Set the language:", "--lang=<lang>. Use --lang=? to see a list of available language codes.", "option", False)
1602USAGE_MODE = ("[MODE]", "", "header", False)
1603USAGE_NON_INTERACTIVE_MODE = ("Run in non-interactive mode:", "-n or --non-interactive", "option", False)
1604USAGE_GUI_MODE = ("Run in graphical UI mode:", "-u or --gui (Default)", "option", False)
1605USAGE_INTERACTIVE_MODE = ("Run in interactive mode:", "-i or --interactive", "option", False)
1606
1607if sys_conf.get('configure', 'ui-toolkit', 'qt3') == 'qt3':
1608    USAGE_USE_QT3 = ("Use Qt3:",  "--qt3 (Default)",  "option",  False)
1609    USAGE_USE_QT4 = ("Use Qt4:",  "--qt4",  "option",  False)
1610    USAGE_USE_QT5 = ("Use Qt5:",  "--qt5",  "option",  False)
1611elif sys_conf.get('configure', 'ui-toolkit', 'qt4') == 'qt4':
1612    USAGE_USE_QT3 = ("Use Qt3:",  "--qt3",  "option",  False)
1613    USAGE_USE_QT4 = ("Use Qt4:",  "--qt4 (Default)",  "option",  False)
1614    USAGE_USE_QT5 = ("Use Qt5:",  "--qt5",  "option",  False)
1615elif sys_conf.get('configure', 'ui-toolkit', 'qt5') == 'qt5':
1616    USAGE_USE_QT3 = ("Use Qt3:",  "--qt3",  "option",  False)
1617    USAGE_USE_QT4 = ("Use Qt4:",  "--qt4",  "option",  False)
1618    USAGE_USE_QT5 = ("Use Qt5:",  "--qt5 (Default)",  "option",  False)
1619
1620def ttysize(): # TODO: Move to base/tui
1621    ln1 = subprocess.getoutput('stty -a').splitlines()[0]
1622    vals = {'rows':None, 'columns':None}
1623    for ph in ln1.split(';'):
1624        x = ph.split()
1625        if len(x) == 2:
1626            vals[x[0]] = x[1]
1627            vals[x[1]] = x[0]
1628    try:
1629        rows, cols = int(vals['rows']), int(vals['columns'])
1630    except TypeError:
1631        rows, cols = 25, 80
1632
1633    return rows, cols
1634
1635
1636def usage_formatter(override=0): # TODO: Move to base/module/Module class
1637    rows, cols = ttysize()
1638
1639    if override:
1640        col1 = override
1641        col2 = cols - col1 - 8
1642    else:
1643        col1 = int(cols / 3) - 8
1644        col2 = cols - col1 - 8
1645
1646    return TextFormatter(({'width': col1, 'margin' : 2},
1647                            {'width': col2, 'margin' : 2},))
1648
1649
1650def format_text(text_list, typ='text', title='', crumb='', version=''): # TODO: Move to base/module/Module class
1651    """
1652    Format usage text in multiple formats:
1653        text: for --help in the console
1654        rest: for conversion with rst2web for the website
1655        man: for manpages
1656    """
1657    if typ == 'text':
1658        formatter = usage_formatter()
1659
1660        for line in text_list:
1661            text1, text2, format, trailing_space = line
1662
1663            # remove any reST/man escapes
1664            text1 = text1.replace("\\", "")
1665            text2 = text2.replace("\\", "")
1666
1667            if format == 'summary':
1668                log.info(log.bold(text1))
1669                log.info("")
1670
1671            elif format in ('para', 'name', 'seealso'):
1672                log.info(text1)
1673
1674                if trailing_space:
1675                    log.info("")
1676
1677            elif format in ('heading', 'header'):
1678                log.info(log.bold(text1))
1679
1680            elif format in ('option', 'example'):
1681                log.info(formatter.compose((text1, text2), trailing_space))
1682
1683            elif format == 'note':
1684                if text1.startswith(' '):
1685                    log.info('\t' + text1.lstrip())
1686                else:
1687                    log.info(text1)
1688
1689            elif format == 'space':
1690                log.info("")
1691
1692        log.info("")
1693
1694
1695    elif typ == 'rest':
1696        opt_colwidth1, opt_colwidth2 = 0, 0
1697        exmpl_colwidth1, exmpl_colwidth2 = 0, 0
1698        note_colwidth1, note_colwidth2 = 0, 0
1699
1700        for line in text_list:
1701            text1, text2, format, trailing_space = line
1702
1703            if format  == 'option':
1704                opt_colwidth1 = max(len(text1), opt_colwidth1)
1705                opt_colwidth2 = max(len(text2), opt_colwidth2)
1706
1707            elif format == 'example':
1708                exmpl_colwidth1 = max(len(text1), exmpl_colwidth1)
1709                exmpl_colwidth2 = max(len(text2), exmpl_colwidth2)
1710
1711            elif format == 'note':
1712                note_colwidth1 = max(len(text1), note_colwidth1)
1713                note_colwidth2 = max(len(text2), note_colwidth2)
1714
1715        opt_colwidth1 += 4
1716        opt_colwidth2 += 4
1717        exmpl_colwidth1 += 4
1718        exmpl_colwidth2 += 4
1719        note_colwidth1 += 4
1720        note_colwidth2 += 4
1721        opt_tablewidth = opt_colwidth1 + opt_colwidth2
1722        exmpl_tablewidth = exmpl_colwidth1 + exmpl_colwidth2
1723        note_tablewidth = note_colwidth1 + note_colwidth2
1724
1725        # write the rst2web header
1726        log.info("""restindex
1727page-title: %s
1728crumb: %s
1729format: rest
1730file-extension: html
1731encoding: utf8
1732/restindex\n""" % (title, crumb))
1733
1734        t = "%s: %s (ver. %s)" % (crumb, title, version)
1735        log.info(t)
1736        log.info("="*len(t))
1737        log.info("")
1738
1739        links = []
1740        needs_header = False
1741        for line in text_list:
1742            text1, text2, format, trailing_space = line
1743
1744            if format == 'seealso':
1745                links.append(text1)
1746                text1 = "`%s`_" % text1
1747
1748            len1, len2 = len(text1), len(text2)
1749
1750            if format == 'summary':
1751                log.info(''.join(["**", text1, "**"]))
1752                log.info("")
1753
1754            elif format in ('para', 'name'):
1755                log.info("")
1756                log.info(text1)
1757                log.info("")
1758
1759            elif format in ('heading', 'header'):
1760
1761                log.info("")
1762                log.info("**" + text1 + "**")
1763                log.info("")
1764                needs_header = True
1765
1766            elif format == 'option':
1767                if needs_header:
1768                    log.info(".. class:: borderless")
1769                    log.info("")
1770                    log.info(''.join(["+", "-"*opt_colwidth1, "+", "-"*opt_colwidth2, "+"]))
1771                    needs_header = False
1772
1773                if text1 and '`_' not in text1:
1774                    log.info(''.join(["| *", text1, '*', " "*(opt_colwidth1-len1-3), "|", text2, " "*(opt_colwidth2-len2), "|"]))
1775                elif text1:
1776                    log.info(''.join(["|", text1, " "*(opt_colwidth1-len1), "|", text2, " "*(opt_colwidth2-len2), "|"]))
1777                else:
1778                    log.info(''.join(["|", " "*(opt_colwidth1), "|", text2, " "*(opt_colwidth2-len2), "|"]))
1779
1780                log.info(''.join(["+", "-"*opt_colwidth1, "+", "-"*opt_colwidth2, "+"]))
1781
1782            elif format == 'example':
1783                if needs_header:
1784                    log.info(".. class:: borderless")
1785                    log.info("")
1786                    log.info(''.join(["+", "-"*exmpl_colwidth1, "+", "-"*exmpl_colwidth2, "+"]))
1787                    needs_header = False
1788
1789                if text1 and '`_' not in text1:
1790                    log.info(''.join(["| *", text1, '*', " "*(exmpl_colwidth1-len1-3), "|", text2, " "*(exmpl_colwidth2-len2), "|"]))
1791                elif text1:
1792                    log.info(''.join(["|", text1, " "*(exmpl_colwidth1-len1), "|", text2, " "*(exmpl_colwidth2-len2), "|"]))
1793                else:
1794                    log.info(''.join(["|", " "*(exmpl_colwidth1), "|", text2, " "*(exmpl_colwidth2-len2), "|"]))
1795
1796                log.info(''.join(["+", "-"*exmpl_colwidth1, "+", "-"*exmpl_colwidth2, "+"]))
1797
1798            elif format == 'seealso':
1799                if text1 and '`_' not in text1:
1800                    log.info(text1)
1801
1802
1803            elif format == 'note':
1804                if needs_header:
1805                    log.info(".. class:: borderless")
1806                    log.info("")
1807                    log.info(''.join(["+", "-"*note_colwidth1, "+", "-"*note_colwidth2, "+"]))
1808                    needs_header = False
1809
1810                if text1.startswith(' '):
1811                    log.info(''.join(["|", " "*(note_tablewidth+1), "|"]))
1812
1813                log.info(''.join(["|", text1, " "*(note_tablewidth-len1+1), "|"]))
1814                log.info(''.join(["+", "-"*note_colwidth1, "+", "-"*note_colwidth2, "+"]))
1815
1816            elif format == 'space':
1817                log.info("")
1818
1819        for l in links:
1820            log.info("\n.. _`%s`: %s.html\n" % (l, l.replace('hp-', '')))
1821
1822        log.info("")
1823
1824    elif typ == 'man':
1825        log.info('.TH "%s" 1 "%s" Linux "User Manuals"' % (crumb, version))
1826        log.info(".SH NAME\n%s \- %s" % (crumb, title))
1827
1828        for line in text_list:
1829            text1, text2, format, trailing_space = line
1830
1831            text1 = text1.replace("\\*", "*")
1832            text2 = text2.replace("\\*", "*")
1833
1834            len1, len2 = len(text1), len(text2)
1835
1836            if format == 'summary':
1837                log.info(".SH SYNOPSIS")
1838                log.info(".B %s" % text1.replace('Usage:', ''))
1839
1840            elif format == 'name':
1841                if text1:
1842                    log.info(".SH DESCRIPTION\n%s" % text1)
1843
1844            elif format in ('option', 'example', 'note'):
1845                if text1:
1846                    log.info('.IP "%s"\n%s' % (text1, text2))
1847                else:
1848                    log.info(text2)
1849
1850            elif format in ('header', 'heading'):
1851                log.info(".SH %s" % text1.upper().replace(':', '').replace('[', '').replace(']', ''))
1852
1853            elif format in ('seealso, para'):
1854                log.info(text1)
1855
1856        log.info(".SH AUTHOR")
1857        log.info("HPLIP (HP Linux Imaging and Printing) is an")
1858        log.info("HP developed solution for printing, scanning, and faxing with")
1859        log.info("HP inkjet and laser based printers in Linux.")
1860
1861        log.info(".SH REPORTING BUGS")
1862        log.info("The HPLIP Launchpad.net site")
1863        log.info(".B https://launchpad.net/hplip")
1864        log.info("is available to get help, report")
1865        log.info("bugs, make suggestions, discuss the HPLIP project or otherwise")
1866        log.info("contact the HPLIP Team.")
1867
1868        log.info(".SH COPYRIGHT")
1869        log.info("Copyright (c) 2001-18 HP Development Company, L.P.")
1870        log.info(".LP")
1871        log.info("This software comes with ABSOLUTELY NO WARRANTY.")
1872        log.info("This is free software, and you are welcome to distribute it")
1873        log.info("under certain conditions. See COPYING file for more details.")
1874
1875        log.info("")
1876
1877
1878def log_title(program_name, version, show_ver=True): # TODO: Move to base/module/Module class
1879    log.info("")
1880
1881    if show_ver:
1882        log.info(log.bold("HP Linux Imaging and Printing System (ver. %s)" % prop.version))
1883    else:
1884        log.info(log.bold("HP Linux Imaging and Printing System"))
1885
1886    log.info(log.bold("%s ver. %s" % (program_name, version)))
1887    log.info("")
1888    log.info("Copyright (c) 2001-18 HP Development Company, LP")
1889    log.info("This software comes with ABSOLUTELY NO WARRANTY.")
1890    log.info("This is free software, and you are welcome to distribute it")
1891    log.info("under certain conditions. See COPYING file for more details.")
1892    log.info("")
1893
1894
1895def ireplace(old, search, replace):
1896    regex = '(?i)' + re.escape(search)
1897    return re.sub(regex, replace, old)
1898
1899#
1900# Removes HTML or XML character references and entities from a text string.
1901#
1902
1903def unescape(text):
1904    def fixup(m):
1905        text = m.group(0)
1906        if text[:2] == "&#":
1907            # character reference
1908            try:
1909                if text[:3] == "&#x":
1910                    #return unichr(int(text[3:-1], 16))
1911                    return chr(int(text[3:-1], 16))
1912                else:
1913                    #return unichr(int(text[2:-1]))
1914                    return chr(int(text[2:-1]))
1915            except ValueError:
1916                pass
1917        else:
1918            # named entity
1919            try:
1920                #text = unichr(htmlentitydefs.name2codepoint[text[1:-1]])
1921                text = chr(html_entities.name2codepoint[text[1:-1]])
1922            except KeyError:
1923                pass
1924        return text # leave as is
1925    return re.sub("&#?\w+;", fixup, text)
1926
1927
1928# Adds HTML or XML character references and entities from a text string
1929
1930def escape(s):
1931    if not isinstance(s, str):
1932        s = to_unicode(s)
1933
1934    s = s.replace("&", "&amp;")
1935
1936    for c in html_entities.codepoint2name:
1937        if c != 0x26: # exclude &
1938            s = s.replace(chr(c), "&%s;" % html_entities.codepoint2name[c])
1939
1940    for c in list(range(0x20)) + list(range(0x7f, 0xa0)):
1941        s = s.replace(chr(c), "&#%d;" % c)
1942
1943    return s
1944
1945
1946#return tye: strings
1947#Return values.
1948#   None --> on error.
1949#  "terminal name"-->success
1950def get_terminal():
1951    terminal_list=['gnome-terminal', 'konsole','x-terminal-emulator', 'xterm', 'gtkterm']
1952    terminal_cmd = None
1953    for cmd in terminal_list:
1954        if which(cmd):
1955            terminal_cmd = cmd +" -e "
1956            log.debug("Available Terminal = %s " %terminal_cmd)
1957            break
1958
1959    return terminal_cmd
1960
1961#Return Type: bool
1962# Return values:
1963#      True --> if it is older version
1964#      False  --> if it is same or later version.
1965
1966def Is_HPLIP_older_version(installed_version, available_version):
1967
1968    if available_version == "" or available_version == None or installed_version == "" or installed_version == None:
1969        log.debug("available_version is ''")
1970        return False
1971
1972    installed_array=installed_version.split('.')
1973    available_array=available_version.split('.')
1974
1975    log.debug("HPLIP Installed_version=%s  Available_version=%s"%(installed_version,available_version))
1976    cnt = 0
1977    Is_older = False
1978    pat=re.compile('''(\d{1,})([a-z]{1,})''')
1979    try:
1980        while cnt <len(installed_array) and cnt <len(available_array):
1981
1982            installed_ver_dig=0
1983            installed_ver_alph=' '
1984            available_ver_dig=0
1985            available_ver_alph=' '
1986            if pat.search(installed_array[cnt]):
1987                installed_ver_dig = int(pat.search(installed_array[cnt]).group(1))
1988                installed_ver_alph = pat.search(installed_array[cnt]).group(2)
1989            else:
1990                installed_ver_dig = int(installed_array[cnt])
1991
1992            if pat.search(available_array[cnt]):
1993                available_ver_dig = int(pat.search(available_array[cnt]).group(1))
1994                available_ver_alph = pat.search(available_array[cnt]).group(2)
1995            else:
1996                available_ver_dig = int(available_array[cnt])
1997
1998            if (installed_ver_dig < available_ver_dig):
1999                Is_older = True
2000                break
2001            elif (installed_ver_dig > available_ver_dig):
2002                log.debug("Already new verison is installed")
2003                return False
2004            #checking sub minor versions .. e.g "3.12.10a" vs "3.12.10".... "3.12.10a" --> latest
2005            else:
2006                if (installed_ver_alph.lower() < available_ver_alph.lower()):
2007                    Is_older = True
2008                    break
2009                elif (installed_ver_alph.lower() > available_ver_alph.lower()):
2010                    log.debug("Already new verison is installed")
2011                    return False
2012
2013            cnt += 1
2014
2015        # To check version is installed. e.g. "3.12.10" vs "3.12.10.1".... "3.12.10.1"-->latest
2016        if Is_older is False and len(installed_array) < len(available_array):
2017            Is_older = True
2018
2019    except:
2020        log.error("Failed to get the latest version. Check out %s for manually installing latest version of HPLIP."%HPLIP_WEB_SITE)
2021        return False
2022
2023    return Is_older
2024
2025
2026def downLoad_status(count, blockSize, totalSize):
2027    percent = int(count*blockSize*100/totalSize)
2028    if count != 0:
2029        sys.stdout.write("\b\b\b")
2030    sys.stdout.write("%s" %(log.color("%2d%%"%percent, 'bold')))
2031    sys.stdout.flush()
2032
2033def chunk_write(response, out_fd, chunk_size =8192, status_bar = downLoad_status):
2034   if response.info() and response.info().get('Content-Length'):
2035       total_size = int(response.info().get('Content-Length').strip())
2036   else:
2037       log.debug("Ignoring progres bar")
2038       status_bar = None
2039
2040   bytes_so_far = 0
2041   while 1:
2042      chunk = response.read(chunk_size)
2043      if not chunk:
2044         break
2045
2046      out_fd.write(chunk)
2047      bytes_so_far += len(chunk)
2048
2049      if status_bar:
2050         status_bar(bytes_so_far, 1, total_size)
2051
2052
2053# Return Values. Sts, outFile
2054# Sts =  0             --> Success
2055#        other thatn 0  --> Fail
2056# outFile = downloaded Filename.
2057#            empty file on Failure case
2058def download_from_network(weburl, outputFile = None, useURLLIB=False):
2059    retValue = -1
2060
2061    if weburl == "" or weburl == None:
2062        log.error("URL is empty")
2063        return retValue, ""
2064
2065    if outputFile is None:
2066        fp, outputFile = make_temp_file()
2067
2068    try:
2069        if useURLLIB is False:
2070            wget = which("wget")
2071            if wget:
2072                wget = os.path.join(wget, "wget")
2073                status, output = run("%s --cache=off --tries=3 --timeout=60 --output-document=%s %s" %(wget, outputFile, weburl))
2074                if status:
2075                    log.error("Failed to connect to HPLIP site. Error code = %d" %status)
2076                    return retValue, ""
2077            else:
2078                useURLLIB = True
2079
2080        if useURLLIB:
2081
2082            #sys.stdout.write("Download in progress..........")
2083            try:
2084                response = urllib2_request.urlopen(weburl)
2085                file_fd = open(outputFile, 'wb')
2086                chunk_write(response, file_fd)
2087                file_fd.close()
2088            except urllib2_error.URLError as e:
2089                log.error("Failed to open URL: %s" % weburl)
2090                return retValue, ""
2091
2092    except IOError as e:
2093        log.error("I/O Error: %s" % e.strerror)
2094        return retValue, ""
2095
2096    if not os.path.exists(outputFile):
2097        log.error("Failed to get hplip version/ %s file not found."%hplip_version_file)
2098        return retValue, ""
2099
2100    return 0, outputFile
2101
2102
2103
2104
2105
2106class Sync_Lock:
2107    def __init__(self, filename):
2108        self.Lock_filename = filename
2109        self.handler = open(self.Lock_filename, 'w')
2110
2111# Wait for another process to release resource and acquires the resource.
2112    def acquire(self):
2113        fcntl.flock(self.handler, fcntl.LOCK_EX)
2114
2115    def release(self):
2116        fcntl.flock(self.handler, fcntl.LOCK_UN)
2117
2118    def __del__(self):
2119        self.handler.close()
2120
2121def sendEvent(event_code,device_uri, printer_name, username="", job_id=0, title="", pipe_name=''):
2122
2123    if not dbus_avail:
2124        log.debug("Failed to import dbus, lowlevel")
2125        return
2126
2127    log.debug("send_message() entered")
2128    args = [device_uri, printer_name, event_code, username, job_id, title, pipe_name]
2129    msg = lowlevel.SignalMessage(path='/', interface=DBUS_SERVICE, name='Event')
2130    msg.append(signature='ssisiss', *args)
2131    SystemBus().send_message(msg)
2132    log.debug("send_message() returning")
2133
2134def expand_list(File_exp):
2135   File_list = glob.glob(File_exp)
2136   if File_list:
2137      File_list_str = ' '.join(File_list)
2138      return File_list, File_list_str
2139   else:
2140      return [],""
2141
2142
2143
2144def unchunck_xml_data(src_data):
2145    index = 0
2146    dst_data=""
2147    # src_data contains HTTP data + xmlpayload. delimter is '\r\n\r\n'.
2148    while 1:
2149        if src_data.find('\r\n\r\n') != -1:
2150            src_data = src_data.split('\r\n\r\n', 1)[1]
2151            if not src_data.startswith("HTTP"):
2152                break
2153        else:
2154            return dst_data
2155
2156    if len(src_data) <= 0:
2157        return dst_data
2158
2159    #If xmlpayload doesn't have chuncksize embedded, returning same xml.
2160    if src_data[index] == '<':
2161        dst_data = src_data
2162    else:  # Removing chunck size from xmlpayload
2163        try:
2164           while index < len(src_data):
2165             buf_len = 0
2166             while src_data[index] == ' ' or src_data[index] == '\r' or src_data[index] == '\n':
2167               index = index +1
2168             while src_data[index] != '\n' and src_data[index] != '\r':
2169               buf_len = buf_len *16 + int(src_data[index], 16)
2170               index = index +1
2171
2172             if buf_len == 0:
2173                 break;
2174
2175             dst_data = dst_data+ src_data[index:buf_len+index+2]
2176
2177             index = buf_len + index + 2  # 2 for after size '\r\n' chars.
2178        except IndexError:
2179            pass
2180    return dst_data
2181
2182
2183#check_user_groups function checks required groups and returns missing list.
2184#Input:
2185#       required_grps_str --> required groups from distro.dat
2186#       avl_grps    --> Current groups list (as a string) for this user.
2187# Output:
2188#       result  --> Returns True, if required groups are present
2189#               --> Returns False, if required groups are not present
2190#       missing_groups_str --> Returns the missing groups list (as a string)
2191#
2192def check_user_groups(required_grps_str, avl_grps):
2193    result = False
2194    exp_grp_list=[]
2195    exp_pat =re.compile('''.*-G(.*)''')
2196    if required_grps_str and exp_pat.search(required_grps_str):
2197        grps = exp_pat.search(required_grps_str).group(1)
2198        grps =re.sub(r'\s', '', str(grps))
2199        exp_grp_list = grps.split(',')
2200    else:
2201        exp_grp_list.append('lp')
2202
2203    log.debug("Requied groups list =[%s]"%exp_grp_list)
2204
2205    avl_grps = avl_grps.rstrip('\r\n')
2206    grp_list= avl_grps.split(' ')
2207    for  g in grp_list:
2208        grp_index = 0
2209        for p in exp_grp_list:
2210            if g == p:
2211                del exp_grp_list[grp_index]
2212                break
2213            grp_index +=1
2214
2215    if len(exp_grp_list) == 0:
2216        result = True
2217    missing_groups_str=''
2218    for a in exp_grp_list:
2219        if missing_groups_str:
2220            missing_groups_str += ','
2221        missing_groups_str += a
2222    return result ,missing_groups_str
2223
2224
2225def check_library( so_file_path):
2226    ret_val = False
2227    if not os.path.exists(so_file_path):
2228        log.debug("Either %s file is not present or symbolic link is missing" %(so_file_path))
2229    else:
2230        # capturing real file path
2231        if os.path.islink(so_file_path):
2232            real_file = os.path.realpath(so_file_path)
2233        else:
2234            real_file = so_file_path
2235
2236        if not os.path.exists(real_file):
2237            log.debug("%s library file is missing." % (real_file))
2238        elif (os.stat(so_file_path).st_mode & 72) != 72:
2239            log.debug("%s library file doesn't have user/group execute permission." % (so_file_path))
2240        else:
2241            log.debug("%s library file present." % (so_file_path))
2242            ret_val = True
2243
2244    log.debug("%s library status: %d" % (so_file_path, ret_val))
2245    return ret_val
2246
2247
2248def download_via_wget(target):
2249    status = -1
2250    wget = which("wget")
2251    if target and wget:
2252        wget = os.path.join(wget, "wget")
2253        cmd = "%s --cache=off --tries=3 --timeout=60 --output-document=- %s" % (wget, target)
2254        log.debug(cmd)
2255        status, output = run(cmd)
2256        log.debug("wget returned: %d" % status)
2257    else:
2258        log.debug("wget not found")
2259    return status
2260
2261def download_via_curl(target):
2262    status = -1
2263    curl = which("curl")
2264    if target and curl:
2265        curl = os.path.join(curl, "curl")
2266        cmd = "%s --output - --connect-timeout 5 --max-time 10 %s" % (curl, target)
2267        log.debug(cmd)
2268        status, output = run(cmd)
2269        log.debug("curl returned: %d" % status)
2270    else:
2271        log.debug("curl not found")
2272    return status
2273
2274def check_network_via_ping(target):
2275    status = -1
2276    ping = which("ping")
2277    if target and ping:
2278        ping = os.path.join(ping, "ping")
2279        cmd = "%s -c1 -W1 -w10 %s" % (ping, target)
2280        log.debug(cmd)
2281        status, output = run(cmd)
2282        log.debug("ping returned: %d" % status)
2283    else:
2284        log.debug("ping not found")
2285    return status
2286
2287def check_network_connection(url=HTTP_CHECK_TARGET, ping_server=PING_CHECK_TARGET):
2288    status = download_via_wget(url)
2289    if (status != 0):
2290        status = download_via_curl(url)
2291        if (status != 0):
2292            status = check_network_via_ping(ping_server)
2293    return (status == 0)
2294
2295#Expands '*' in File/Dir names.
2296def expandList(Files_List, prefix_dir=None):
2297    Expanded_Files_list=[]
2298    for f in Files_List:
2299        if prefix_dir:
2300            f= prefix_dir + '/' + f
2301        if '*' in f:
2302            f_full = glob.glob(f)
2303            for file in f_full:
2304              Expanded_Files_list.append(file)
2305        else:
2306            Expanded_Files_list.append(f)
2307    return Expanded_Files_list
2308
2309def compare(x, y):
2310    try:
2311        return cmp(float(x), float(y))
2312    except ValueError:
2313        return cmp(x, y)
2314
2315
2316def check_pkg_mgr( package_mgrs = None):
2317    if package_mgrs is not None:
2318        log.debug("Searching for '%s' in running processes..." % package_mgrs)
2319        for p in package_mgrs:
2320                status,process = Is_Process_Running(p)
2321                if status is True:
2322                    for pid in process:
2323                        log.debug("Found: %s (%s)" % (process[pid], pid))
2324                        return (pid, process[pid])
2325
2326    log.debug("Not found")
2327    return (0, '')
2328
2329# checks if given process is running.
2330#return value:
2331#    True or False
2332#    None - if process is not running
2333#    grep output - if process is running
2334
2335def Is_Process_Running(process_name):
2336    if not process_name:
2337        return False, {}
2338
2339    try:
2340        process = {}
2341        p1 = Popen(["ps", "-w", "-w", "aux"], stdout=PIPE)
2342        p2 = Popen(["grep", process_name], stdin=p1.stdout, stdout=PIPE)
2343        p3 = Popen(["grep", "-v", "grep"], stdin=p2.stdout, stdout=PIPE)
2344        output = p3.communicate()[0]
2345        log.debug("Is_Process_Running output = %s " %output)
2346
2347        if output:
2348            for p in output.splitlines():
2349                cmd = "echo '%s' | awk {'print $2'}" %p
2350                status,pid = subprocess.getstatusoutput(cmd)
2351                cmd = "echo '%s' | awk {'print $11,$12'}" %p
2352                status,cmdline = subprocess.getstatusoutput(cmd)
2353                if pid :
2354                    process[pid] = cmdline
2355
2356            return True, process
2357        else:
2358            return False, {}
2359
2360    except Exception as e:
2361        log.error("Execution failed: process Name[%s]" %process_name)
2362        print >>sys.stderr, "Execution failed:", e
2363        return False, {}
2364
2365
2366def remove(path, passwordObj = None, cksudo = False):
2367    cmd= RMDIR + " " + path
2368    if cksudo and passwordObj:
2369        cmd= passwordObj.getAuthCmd() %cmd
2370
2371    log.debug("Removing %s cmd = %s " %(path, cmd))
2372    status, output = run(cmd, passwordObj)
2373    if 0 != status:
2374        log.debug("Failed to remove=%s "%path)
2375
2376# This is operator overloading function for compare..
2377def cmp_to_key(mycmp):
2378    'Convert a cmp= function into a key= function'
2379    class K(object):
2380        def __init__(self, obj, *args):
2381            self.obj = obj
2382        def __lt__(self, other):
2383            return mycmp(self.obj, other.obj) < 0
2384        def __gt__(self, other):
2385            return mycmp(self.obj, other.obj) > 0
2386        def __eq__(self, other):
2387            return mycmp(self.obj, other.obj) == 0
2388        def __le__(self, other):
2389            return mycmp(self.obj, other.obj) <= 0
2390        def __ge__(self, other):
2391            return mycmp(self.obj, other.obj) >= 0
2392        def __ne__(self, other):
2393            return mycmp(self.obj, other.obj) != 0
2394    return K
2395
2396# This is operator overloading function for compare.. for level functionality.
2397def levelsCmp(x, y):
2398    return (x[1] > y[1]) - (x[1] < y[1]) or (x[3] > y[3]) - (x[3] < y[3])
2399
2400
2401def find_pip():
2402    '''Determine the pip command syntax available for a particular distro.
2403    since it varies across distros'''
2404
2405    if which('pip-%s'%(str(MAJ_VER)+'.'+str(MIN_VER))):
2406        return 'pip-%s'%(str(MAJ_VER)+'.'+str(MIN_VER))
2407    elif which('pip-%s'%str(MAJ_VER)):
2408        return 'pip-%s'%str(MAJ_VER)
2409    elif which('pip%s'%str(MAJ_VER)):
2410        return 'pip%s'%str(MAJ_VER)
2411    elif which('pip%s'%(str(MAJ_VER)+'.'+str(MIN_VER))):
2412        return 'pip%s'%(str(MAJ_VER)+'.'+str(MIN_VER))
2413    elif which('pip-python%s'%str(MAJ_VER)):
2414        return 'pip-python%s'%str(MAJ_VER)
2415    elif which('pip-python'):
2416        return 'pip-python'
2417    else:
2418        log.error("python pip command not found. Please install '%s' package(s) manually"%depends_to_install_using_pip)
2419
2420
2421def check_lan():
2422    try:
2423        x = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
2424        x.connect(('1.2.3.4', 56))
2425        x.close()
2426        return True
2427    except socket.error:
2428        return False
2429
2430def extract_xml_chunk(data):
2431    if data.find('\r\n\r\n'):
2432        index = data.find('\r\n\r\n')
2433        data = data[index+4:]
2434    if data[0:1] != '<':            # Check for source encoding chunked or content length in http respose header.
2435        size = -1
2436        temp = ""
2437        while size:
2438            index = data.find('\r\n')
2439            size = int(data[0:index+1], 16)
2440            temp = temp + data[index+2:index+2+size]
2441            data = data[index+2+size+2:len(data)]
2442        data = temp
2443    return data
2444
2445
2446def checkPyQtImport45():
2447    try:
2448        import PyQt5
2449        return "PyQt5"
2450    except ImportError as e:
2451        log.debug(e)
2452
2453    try:
2454        import PyQt4
2455        return "PyQt4"
2456    except ImportError as e:
2457        log.debug(e)
2458
2459    raise ImportError("GUI Modules PyQt4 and PyQt5 are not installed")
2460
2461
2462def ui_status():
2463    _ui_status = ""
2464    try:
2465        _ui_status = checkPyQtImport45()
2466        log.note("Using GUI Module %s" % _ui_status)
2467        return _ui_status
2468    except ImportError as e:
2469        log.error(e)
2470
2471
2472def import_dialog(ui_toolkit):
2473    if ui_toolkit == "qt4":
2474        try:
2475            from PyQt4.QtGui import QApplication
2476            log.debug("Using PyQt4")
2477            return  (QApplication, "ui4")
2478        except ImportError as e:
2479            log.error(e)
2480            sys.exit(1)
2481    elif ui_toolkit == "qt5":
2482        try:
2483            from PyQt5.QtWidgets import QApplication
2484            log.debug("Using PyQt5")
2485            return (QApplication, "ui5")
2486        except ImportError as e:
2487            log.error(e)
2488            sys.exit(1)
2489        else:
2490            log.error("Unable to load Qt support. Is it installed?")
2491            sys.exit(1)
2492
2493
2494def dyn_import_mod(mod_name_as_str):
2495    components = mod_name_as_str.split('.')
2496    mod = __import__(mod_name_as_str)
2497    for comp in components[1:]:
2498        mod = getattr(mod, comp)
2499    return mod
2500