1
# -*- coding: utf-8 -*-
3
# (c) Copyright 2001-2009 Hewlett-Packard Development Company, L.P.
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.
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.
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
19
# Author: Don Welch, Naga Samrat Chowdary Narla, Goutam Kodu, Amarnath Chitumalla
21
# Thanks to Henrique M. Holschuh <hmh@debian.org> for various security patches
24
from __future__ import generators
29
from subprocess import Popen, PIPE
41
import commands # TODO: Replace with subprocess (commands is deprecated in Python 3.0)
44
import xml.parsers.expat as expat
54
platform_avail = False
67
fp=open('/etc/cups/cupsd.conf')
70
if "root" != grp.getgrgid(os.stat('/etc/cups/cupsd.conf').st_gid).gr_name:
71
return [grp.getgrgid(os.stat('/etc/cups/cupsd.conf').st_gid).gr_name]
76
lis = ((re.findall('SystemGroup [\w* ]*',fp.read()))[0].replace('SystemGroup ','')).split(' ')
85
def list_to_string(lis):
89
return str("\""+lis[0]+"\"")
91
return "\""+"\", \"".join(lis)+"\" and \""+str(lis.pop())+"\""
94
log.debug("Locking: %s" % f.name)
96
fcntl.flock(f.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
98
except (IOError, OSError):
99
log.debug("Failed to unlock %s." % f.name)
105
log.debug("Unlocking: %s" % f.name)
107
fcntl.flock(f.fileno(), fcntl.LOCK_UN)
109
except (IOError, OSError):
113
def lock_app(application, suppress_error=False):
115
if os.geteuid() == 0:
118
elif not os.path.exists(dir):
121
lock_file = os.path.join(dir, '.'.join([application, 'lock']))
123
lock_file_f = open(lock_file, "w")
125
if not suppress_error:
126
log.error("Unable to open %s lock file." % lock_file)
129
#log.debug("Locking file: %s" % lock_file)
131
if not lock(lock_file_f):
132
if not suppress_error:
133
log.error("Unable to lock %s. Is %s already running?" % (lock_file, application))
136
return True, lock_file_f
139
#xml_basename_pat = re.compile(r"""HPLIP-(\d*)_(\d*)_(\d*).xml""", re.IGNORECASE)
142
def Translator(frm='', to='', delete='', keep=None):
143
allchars = string.maketrans('','')
147
trans = string.maketrans(frm, to)
150
delete = allchars.translate(allchars, keep.translate(allchars, delete))
153
return s.translate(trans, delete)
158
def to_bool_str(s, default='0'):
159
""" Convert an arbitrary 0/1/T/F/Y/N string to a normalized string 0/1."""
160
if isinstance(s, str) and s:
161
if s[0].lower() in ['1', 't', 'y']:
163
elif s[0].lower() in ['0', 'f', 'n']:
168
def to_bool(s, default=False):
169
""" Convert an arbitrary 0/1/T/F/Y/N string to a boolean True/False value."""
170
if isinstance(s, str) and s:
171
if s[0].lower() in ['1', 't', 'y']:
173
elif s[0].lower() in ['0', 'f', 'n']:
175
elif isinstance(s, bool):
181
def walkFiles(root, recurse=True, abs_paths=False, return_folders=False, pattern='*', path=None):
186
names = os.listdir(root)
190
pattern = pattern or '*'
191
pat_list = pattern.split(';')
194
fullname = os.path.normpath(os.path.join(root, name))
197
if fnmatch.fnmatch(name, pat):
198
if return_folders or not os.path.isdir(fullname):
203
yield os.path.basename(fullname)
207
#if os.path.islink(fullname):
208
# fullname = os.path.realpath(os.readlink(fullname))
210
if recurse and os.path.isdir(fullname): # or os.path.islink(fullname):
211
for f in walkFiles(fullname, recurse, abs_paths, return_folders, pattern, path):
215
def is_path_writable(path):
216
if os.path.exists(path):
218
mode = s[stat.ST_MODE] & 0777
222
elif s[stat.ST_GID] == os.getgid() and mode & 020:
224
elif s[stat.ST_UID] == os.getuid() and mode & 0200:
230
# Provides the TextFormatter class for formatting text into columns.
231
# Original Author: Hamish B Lawson, 1999
232
# Modified by: Don Welch, 2003
239
def __init__(self, colspeclist):
241
for colspec in colspeclist:
242
self.columns.append(Column(**colspec))
244
def compose(self, textlist, add_newline=False):
246
textlist = list(textlist)
247
if len(textlist) != len(self.columns):
248
log.error("Formatter: Number of text items does not match columns")
250
for text, column in map(None, textlist, self.columns):
252
numlines = max(numlines, len(column.lines))
253
complines = [''] * numlines
254
for ln in range(numlines):
255
for column in self.columns:
256
complines[ln] = complines[ln] + column.getline(ln)
258
return '\n'.join(complines) + '\n'
260
return '\n'.join(complines)
264
def __init__(self, width=78, alignment=TextFormatter.LEFT, margin=0):
266
self.alignment = alignment
270
def align(self, line):
271
if self.alignment == TextFormatter.CENTER:
272
return line.center(self.width)
273
elif self.alignment == TextFormatter.RIGHT:
274
return line.rjust(self.width)
276
return line.ljust(self.width)
278
def wrap(self, text):
281
for word in text.split():
282
if word <= self.width:
285
for i in range(0, len(word), self.width):
286
words.append(word[i:i+self.width])
287
if not len(words): return
288
current = words.pop(0)
290
increment = 1 + len(word)
291
if len(current) + increment > self.width:
292
self.lines.append(self.align(current))
295
current = current + ' ' + word
296
self.lines.append(self.align(current))
298
def getline(self, index):
299
if index < len(self.lines):
300
return ' '*self.margin + self.lines[index]
302
return ' ' * (self.margin + self.width)
311
return self.stack.pop()
313
def push(self, value):
314
self.stack.append(value)
323
return len(self.stack)
332
return self.stack.pop(0)
334
def put(self, value):
335
Stack.push(self, value)
340
# Source: Python Cookbook 1st Ed., sec. 5.18, pg. 201
341
# Credit: Sebastien Keim
342
# License: Modified BSD
344
def __init__(self, size_max=50):
349
"""append an element at the end of the buffer"""
352
if len(self.data) == self.max:
354
self.__class__ = RingBufferFull
356
def replace(self, x):
357
"""replace the last element instead off appending"""
361
""" return a list of elements from the oldest to the newest"""
365
class RingBufferFull:
366
def __init__(self, n):
367
#raise "you should use RingBuffer"
371
self.data[self.cur] = x
372
self.cur = (self.cur+1) % self.max
374
def replace(self, x):
375
# back up 1 position to previous location
376
self.cur = (self.cur-1) % self.max
377
self.data[self.cur] = x
378
# setup for next item
379
self.cur = (self.cur+1) % self.max
382
return self.data[self.cur:] + self.data[:self.cur]
386
def sort_dict_by_value(d):
387
""" Returns the keys of dictionary d sorted by their values """
389
backitems=[[v[1],v[0]] for v in items]
391
return [backitems[i][1] for i in range(0, len(backitems))]
395
return unicode(locale.format("%d", val, grouping=True))
398
def format_bytes(s, show_bytes=False):
400
return ''.join([commafy(s), ' B'])
401
elif 1024 < s < 1048576:
403
return ''.join([unicode(round(s/1024.0, 1)) , u' KB (', commafy(s), ')'])
405
return ''.join([unicode(round(s/1024.0, 1)) , u' KB'])
406
elif 1048576 < s < 1073741824:
408
return ''.join([unicode(round(s/1048576.0, 1)), u' MB (', commafy(s), ')'])
410
return ''.join([unicode(round(s/1048576.0, 1)), u' MB'])
413
return ''.join([unicode(round(s/1073741824.0, 1)), u' GB (', commafy(s), ')'])
415
return ''.join([unicode(round(s/1073741824.0, 1)), u' GB'])
420
make_temp_file = tempfile.mkstemp # 2.3+
421
except AttributeError:
422
def make_temp_file(suffix='', prefix='', dir='', text=False): # pre-2.3
423
path = tempfile.mktemp(suffix)
424
fd = os.open(path, os.O_RDWR|os.O_CREAT|os.O_EXCL, 0700)
425
return ( os.fdopen( fd, 'w+b' ), path )
429
def which(command, return_full_path=False):
430
path = os.getenv('PATH').split(':')
432
# Add these paths for Fedora
434
path.append('/usr/sbin')
435
path.append('/usr/local/sbin')
440
files = os.listdir(p)
450
return os.path.join(found_path, command)
457
class UserSettings(object): # Note: Deprecated after 2.8.8 in Qt4 (see ui4/ui_utils.py)
461
def loadDefaults(self):
464
path = which('hp-print')
467
self.cmd_print = 'hp-print -p%PRINTER%'
469
path = which('kprinter')
471
self.cmd_print = 'kprinter -P%PRINTER% --system cups'
473
path = which('gtklp')
475
self.cmd_print = 'gtklp -P%PRINTER%'
479
self.cmd_print = 'xpp -P%PRINTER%'
483
path = which('xsane')
486
self.cmd_scan = 'xsane -V %SANE_URI%'
488
path = which('kooka')
490
self.cmd_scan = 'kooka'
492
path = which('xscanimage')
494
self.cmd_scan = 'xscanimage'
497
path = which('hp-unload')
500
self.cmd_pcard = 'hp-unload -d %DEVICE_URI%'
502
self.cmd_pcard = 'python %HOME%/unload.py -d %DEVICE_URI%'
505
path = which('hp-makecopies')
508
self.cmd_copy = 'hp-makecopies -d %DEVICE_URI%'
510
self.cmd_copy = 'python %HOME%/makecopies.py -d %DEVICE_URI%'
513
path = which('hp-sendfax')
516
self.cmd_fax = 'hp-sendfax -d %FAX_URI%'
518
self.cmd_fax = 'python %HOME%/sendfax.py -d %FAX_URI%'
521
path = which('hp-fab')
524
self.cmd_fab = 'hp-fab'
526
self.cmd_fab = 'python %HOME%/fab.py'
530
log.debug("Loading user settings...")
531
self.auto_refresh = to_bool(user_conf.get('refresh', 'enable', '0'))
534
self.auto_refresh_rate = int(user_conf.get('refresh', 'rate', '30'))
536
self.auto_refresh_rate = 30 # (secs)
539
self.auto_refresh_type = int(user_conf.get('refresh', 'type', '0'))
541
self.auto_refresh_type = 0 # refresh 1 (1=refresh all)
543
self.cmd_print = user_conf.get('commands', 'prnt', self.cmd_print)
544
self.cmd_scan = user_conf.get('commands', 'scan', self.cmd_scan)
545
self.cmd_pcard = user_conf.get('commands', 'pcard', self.cmd_pcard)
546
self.cmd_copy = user_conf.get('commands', 'cpy', self.cmd_copy)
547
self.cmd_fax = user_conf.get('commands', 'fax', self.cmd_fax)
548
self.cmd_fab = user_conf.get('commands', 'fab', self.cmd_fab)
550
self.upgrade_notify= to_bool(user_conf.get('upgrade', 'notify_upgrade', '0'))
551
self.upgrade_last_update_time = int(user_conf.get('upgrade','last_upgraded_time', '0'))
552
self.upgrade_pending_update_time =int(user_conf.get('upgrade', 'pending_upgrade_time', '0'))
553
self.latest_available_version=str(user_conf.get('upgrade', 'latest_available_version',''))
557
log.debug("Print command: %s" % self.cmd_print)
558
log.debug("PCard command: %s" % self.cmd_pcard)
559
log.debug("Fax command: %s" % self.cmd_fax)
560
log.debug("FAB command: %s" % self.cmd_fab)
561
log.debug("Copy command: %s " % self.cmd_copy)
562
log.debug("Scan command: %s" % self.cmd_scan)
563
log.debug("Auto refresh: %s" % self.auto_refresh)
564
log.debug("Auto refresh rate: %s" % self.auto_refresh_rate)
565
log.debug("Auto refresh type: %s" % self.auto_refresh_type)
566
log.debug("Upgrade notification:%d" %self.upgrade_notify)
567
log.debug("Last Installed time:%d" %self.upgrade_last_update_time)
568
log.debug("Next scheduled installation time:%d" % self.upgrade_pending_update_time)
572
log.debug("Saving user settings...")
573
user_conf.set('commands', 'prnt', self.cmd_print)
574
user_conf.set('commands', 'pcard', self.cmd_pcard)
575
user_conf.set('commands', 'fax', self.cmd_fax)
576
user_conf.set('commands', 'scan', self.cmd_scan)
577
user_conf.set('commands', 'cpy', self.cmd_copy)
578
user_conf.set('refresh', 'enable',self.auto_refresh)
579
user_conf.set('refresh', 'rate', self.auto_refresh_rate)
580
user_conf.set('refresh', 'type', self.auto_refresh_type)
581
user_conf.set('upgrade', 'notify_upgrade', self.upgrade_notify)
582
user_conf.set('upgrade','last_upgraded_time', self.upgrade_last_update_time)
583
user_conf.set('upgrade', 'pending_upgrade_time', self.upgrade_pending_update_time)
584
user_conf.set('upgrade', 'latest_available_version', self.latest_available_version)
590
def no_qt_message_gtk():
594
dialog = gtk.MessageDialog(w, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
595
gtk.MESSAGE_WARNING, gtk.BUTTONS_OK,
596
"PyQt not installed. GUI not available. Please check that the PyQt package is installed. Exiting.")
601
log.error("PyQt not installed. GUI not available. Please check that the PyQt package is installed. Exiting.")
604
def canEnterGUIMode(): # qt3
605
if not prop.gui_build:
606
log.warn("GUI mode disabled in build.")
609
elif not os.getenv('DISPLAY'):
610
log.warn("No display found.")
613
elif not checkPyQtImport():
614
log.warn("Qt/PyQt 3 initialization failed.")
620
def canEnterGUIMode4(): # qt4
621
if not prop.gui_build:
622
log.warn("GUI mode disabled in build.")
625
elif not os.getenv('DISPLAY'):
626
log.warn("No display found.")
629
elif not checkPyQtImport4():
630
log.warn("Qt/PyQt 4 initialization failed.")
636
def checkPyQtImport(): # qt3
641
if os.getenv('DISPLAY') and os.getenv('STARTED_FROM_MENU'):
644
log.error("PyQt not installed. GUI not available. Exiting.")
647
# check version of Qt
648
qtMajor = int(qt.qVersion().split('.')[0])
650
if qtMajor < MINIMUM_QT_MAJOR_VER:
652
log.error("Incorrect version of Qt installed. Ver. 3.0.0 or greater required.")
655
#check version of PyQt
657
pyqtVersion = qt.PYQT_VERSION_STR
658
except AttributeError:
659
pyqtVersion = qt.PYQT_VERSION
661
while pyqtVersion.count('.') < 2:
664
(maj_ver, min_ver, pat_ver) = pyqtVersion.split('.')
666
if pyqtVersion.find('snapshot') >= 0:
667
log.warning("A non-stable snapshot version of PyQt is installed.")
670
maj_ver = int(maj_ver)
671
min_ver = int(min_ver)
672
pat_ver = int(pat_ver)
674
maj_ver, min_ver, pat_ver = 0, 0, 0
676
if maj_ver < MINIMUM_PYQT_MAJOR_VER or \
677
(maj_ver == MINIMUM_PYQT_MAJOR_VER and min_ver < MINIMUM_PYQT_MINOR_VER):
678
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))
679
log.error("Incorrect version of pyQt installed. Ver. %d.%d or greater required." % (MINIMUM_PYQT_MAJOR_VER, MINIMUM_PYQT_MINOR_VER))
680
log.error("This program will continue, but you may experience errors, crashes or other problems.")
686
def checkPyQtImport4():
696
from string import Template # will fail in Python <= 2.3
698
# Code from Python 2.4 string.py
702
"""Helper class for combining multiple mappings.
704
Used by .{safe_,}substitute() to combine the mapping and keyword
707
def __init__(self, primary, secondary):
708
self._primary = primary
709
self._secondary = secondary
711
def __getitem__(self, key):
713
return self._primary[key]
715
return self._secondary[key]
718
class _TemplateMetaclass(type):
721
(?P<escaped>%(delim)s) | # Escape sequence of two delimiters
722
(?P<named>%(id)s) | # delimiter and a Python identifier
723
{(?P<braced>%(id)s)} | # delimiter and a braced identifier
724
(?P<invalid>) # Other ill-formed delimiter exprs
728
def __init__(cls, name, bases, dct):
729
super(_TemplateMetaclass, cls).__init__(name, bases, dct)
731
pattern = cls.pattern
733
pattern = _TemplateMetaclass.pattern % {
734
'delim' : re.escape(cls.delimiter),
735
'id' : cls.idpattern,
737
cls.pattern = re.compile(pattern, re.IGNORECASE | re.VERBOSE)
741
"""A string class for supporting $-substitutions."""
742
__metaclass__ = _TemplateMetaclass
745
idpattern = r'[_a-z][_a-z0-9]*'
747
def __init__(self, template):
748
self.template = template
750
# Search for $$, $identifier, ${identifier}, and any bare $'s
751
def _invalid(self, mo):
752
i = mo.start('invalid')
753
lines = self.template[:i].splitlines(True)
758
colno = i - len(''.join(lines[:-1]))
760
raise ValueError('Invalid placeholder in string: line %d, col %d' %
763
def substitute(self, *args, **kws):
765
raise TypeError('Too many positional arguments')
769
mapping = _multimap(kws, args[0])
772
# Helper function for .sub()
774
# Check the most common path first.
775
named = mo.group('named') or mo.group('braced')
776
if named is not None:
778
# We use this idiom instead of str() because the latter will
779
# fail if val is a Unicode containing non-ASCII characters.
781
if mo.group('escaped') is not None:
782
return self.delimiter
783
if mo.group('invalid') is not None:
785
raise ValueError('Unrecognized named group in pattern',
787
return self.pattern.sub(convert, self.template)
790
def safe_substitute(self, *args, **kws):
792
raise TypeError('Too many positional arguments')
796
mapping = _multimap(kws, args[0])
799
# Helper function for .sub()
801
named = mo.group('named')
802
if named is not None:
804
# We use this idiom instead of str() because the latter
805
# will fail if val is a Unicode containing non-ASCII
806
return '%s' % mapping[named]
808
return self.delimiter + named
809
braced = mo.group('braced')
810
if braced is not None:
812
return '%s' % mapping[braced]
814
return self.delimiter + '{' + braced + '}'
815
if mo.group('escaped') is not None:
816
return self.delimiter
817
if mo.group('invalid') is not None:
818
return self.delimiter
819
raise ValueError('Unrecognized named group in pattern',
821
return self.pattern.sub(convert, self.template)
825
#cat = lambda _ : Template(_).substitute(sys._getframe(1).f_globals, **sys._getframe(1).f_locals)
828
globals = sys._getframe(1).f_globals.copy()
829
if 'self' in globals:
832
locals = sys._getframe(1).f_locals.copy()
836
return Template(s).substitute(sys._getframe(1).f_globals, **locals)
839
identity = string.maketrans('','')
840
unprintable = identity.translate(identity, string.printable)
844
return s.translate(identity, unprintable)
847
def any(S,f=lambda x:x):
853
def all(S,f=lambda x:x):
855
if not f(x): return False
859
BROWSERS = ['firefox', 'mozilla', 'konqueror', 'galeon', 'skipstone'] # in preferred order
860
BROWSER_OPTS = {'firefox': '-new-window', 'mozilla' : '', 'konqueror': '', 'galeon': '-w', 'skipstone': ''}
864
if platform_avail and platform.system() == 'Darwin':
874
def openURL(url, use_browser_opts=True):
875
if platform_avail and platform.system() == 'Darwin':
876
cmd = 'open "%s"' % url
883
bb = os.path.join(bb, b)
885
cmd = """%s %s "%s" &""" % (bb, BROWSER_OPTS[b], url)
887
cmd = """%s "%s" &""" % (bb, url)
892
log.warn("Unable to open URL: %s" % url)
895
def uniqueList(input):
897
[temp.append(i) for i in input if not temp.count(i)]
901
def list_move_up(l, m, cmp=None):
903
f = lambda x: l[x] == m
905
f = lambda x: cmp(l[x], m)
907
for i in range(1, len(l)):
909
l[i-1], l[i] = l[i], l[i-1]
912
def list_move_down(l, m, cmp=None):
914
f = lambda x: l[x] == m
916
f = lambda x: cmp(l[x], m)
918
for i in range(len(l)-2, -1, -1):
920
l[i], l[i+1] = l[i+1], l[i]
924
class XMLToDictParser:
930
def startElement(self, name, attrs):
931
#print "START:", name, attrs
932
self.stack.append(unicode(name).lower())
933
self.last_start = unicode(name).lower()
937
self.stack.append(unicode(a).lower())
938
self.addData(attrs[a])
941
def endElement(self, name):
942
if name.lower() == self.last_start:
948
def charData(self, data):
949
data = unicode(data).strip()
951
if data and self.stack:
954
def addData(self, data):
962
stack_str = '-'.join(self.stack)
963
stack_str_0 = '-'.join([stack_str, '0'])
969
self.data[stack_str_0]
971
self.data[stack_str] = data
976
self.data['-'.join([stack_str, unicode(j)])]
978
self.data['-'.join([stack_str, unicode(j)])] = data
983
self.data[stack_str_0] = self.data[stack_str]
984
self.data['-'.join([stack_str, '1'])] = data
985
del self.data[stack_str]
988
def parseXML(self, text):
989
parser = expat.ParserCreate()
990
parser.StartElementHandler = self.startElement
991
parser.EndElementHandler = self.endElement
992
parser.CharacterDataHandler = self.charData
993
parser.Parse(text.encode('utf-8'), True)
998
return ''.join(['"', s, '"'])
1001
# Python 2.2.x compatibility functions (strip() family with char argument added in Python 2.2.3)
1002
if sys.hexversion < 0x020203f0:
1003
def xlstrip(s, chars=' '):
1005
for c, i in zip(s, range(len(s))):
1011
def xrstrip(s, chars=' '):
1012
return xreverse(xlstrip(xreverse(s), chars))
1019
def xstrip(s, chars=' '):
1020
return xreverse(xlstrip(xreverse(xlstrip(s, chars)), chars))
1023
xlstrip = string.lstrip
1024
xrstrip = string.rstrip
1025
xstrip = string.strip
1030
return int(platform.architecture()[0][:-3])
1032
return struct.calcsize("P") << 3
1037
return platform.machine().replace(' ', '_').lower() # i386, i686, power_macintosh, etc.
1039
return "i686" # TODO: Need a fix here
1043
if sys.byteorder == 'big':
1046
return LITTLE_ENDIAN
1050
return getpass.getpass("Enter password: ")
1052
def get_password_ui(pswd_msg=''):
1053
fp = open("/etc/hp/hplip.conf", "r")
1056
if string.find(line, "qt4") is not -1 and string.find(line, "yes") is not -1:
1060
from ui4.setupdialog import showPasswordUI
1062
username, password = showPasswordUI("Your HP Device requires to install HP proprietary plugin\nPlease enter root/superuser password to continue", "root", False)
1064
username, password = showPasswordUI(pswd_msg, "root", False)
1066
from ui.setupform import showPasswordUI
1068
username, password = showPasswordUI("Your HP Device requires to install HP proprietary plugin\nPlease enter root/superuser password to continue", "root", False)
1070
username, password = showPasswordUI(pswd_msg, "root", False)
1073
def run(cmd, log_output=True, password_func=get_password, timeout=1, spinner=True, pswd_msg=''):
1074
output = cStringIO.StringIO()
1077
child = pexpect.spawn(cmd, timeout=timeout)
1078
except pexpect.ExceptionPexpect:
1086
i = child.expect(["[pP]assword:", pexpect.EOF, pexpect.TIMEOUT])
1089
output.write(child.before)
1091
log.debug(child.before)
1093
if i == 0: # Password:
1094
if password_func is not None:
1095
if password_func == "get_password_ui":
1096
child.sendline(get_password_ui(pswd_msg))
1098
child.sendline(password_func())
1100
child.sendline(get_password())
1106
elif i == 2: # TIMEOUT
1110
except Exception, e:
1111
log.error("Exception: %s" % e)
1116
return child.exitstatus, output.getvalue()
1119
def expand_range(ns): # ns -> string repr. of numeric range, e.g. "1-4, 7, 9-12"
1120
"""Credit: Jean Brouwers, comp.lang.python 16-7-2004
1121
Convert a string representation of a set of ranges into a
1123
u"1-4, 7, 9-12" --> [1,2,3,4,7,9,10,11,12]
1126
for n in ns.split(u','):
1129
if len(r) == 2: # expand name with range
1130
h = r[0].rstrip(u'0123456789') # header
1131
r[0] = r[0][len(h):]
1132
# range can't be empty
1133
if not (r[0] and r[1]):
1134
raise ValueError, 'empty range: ' + n
1135
# handle leading zeros
1136
if r[0] == u'0' or r[0][0] != u'0':
1139
w = [len(i) for i in r]
1141
raise ValueError, 'wide range: ' + n
1142
h += u'%%0%dd' % max(w)
1144
r = [int(i, 10) for i in r]
1146
raise ValueError, 'bad range: ' + n
1147
for i in range(r[0], r[1]+1):
1153
fs = dict([(n, i) for i, n in enumerate(fs)]).keys()
1154
# convert to ints and sort
1155
fs = [int(x) for x in fs if x]
1161
def collapse_range(x): # x --> sorted list of ints
1162
""" Convert a list of integers into a string
1163
range representation:
1164
[1,2,3,4,7,9,10,11,12] --> u"1-4,7,9-12"
1169
s, c, r = [str(x[0])], x[0], False
1176
s.append(u'-%s,%s' % (c,i))
1179
s.append(u',%s' % i)
1184
s.append(u'-%s' % i)
1189
def createSequencedFilename(basename, ext, dir=None, digits=3):
1194
for f in walkFiles(dir, recurse=False, abs_paths=False, return_folders=False, pattern='*', path=None):
1195
r, e = os.path.splitext(f)
1197
if r.startswith(basename) and ext == e:
1199
i = int(r[len(basename):])
1205
return os.path.join(dir, "%s%0*d%s" % (basename, digits, m+1, ext))
1208
def validate_language(lang, default='en_US'):
1210
loc, encoder = locale.getdefaultlocale()
1212
lang = lang.lower().strip()
1213
for loc, ll in supported_locales.items():
1218
log.warn("Unknown lang/locale. Using default of %s." % loc)
1223
def gen_random_uuid():
1225
import uuid # requires Python 2.5+
1226
return str(uuid.uuid4())
1229
uuidgen = which("uuidgen")
1231
uuidgen = os.path.join(uuidgen, "uuidgen")
1232
return commands.getoutput(uuidgen) # TODO: Replace with subprocess (commands is deprecated in Python 3.0)
1237
class RestTableFormatter(object):
1238
def __init__(self, header=None):
1239
self.header = header # tuple of strings
1240
self.rows = [] # list of tuples
1242
def add(self, row_data): # tuple of strings
1243
self.rows.append(row_data)
1245
def output(self, w):
1247
num_cols = len(self.rows[0])
1249
if len(r) != num_cols:
1250
log.error("Invalid number of items in row: %s" % r)
1253
if len(self.header) != num_cols:
1254
log.error("Invalid number of items in header.")
1257
for x, c in enumerate(self.header):
1260
max_width = max(max_width, len(r[x]))
1262
col_widths.append(max_width+2)
1265
for c in col_widths:
1266
x = ''.join([x, '-' * (c+2), '+'])
1268
x = ''.join([x, '\n'])
1274
for i, c in enumerate(col_widths):
1275
x = ''.join([x, ' ', self.header[i], ' ' * (c+1-len(self.header[i])), '|'])
1277
x = ''.join([x, '\n'])
1281
for c in col_widths:
1282
x = ''.join([x, '=' * (c+2), '+'])
1284
x = ''.join([x, '\n'])
1288
for j, r in enumerate(self.rows):
1290
for i, c in enumerate(col_widths):
1291
x = ''.join([x, ' ', self.rows[j][i], ' ' * (c+1-len(self.rows[j][i])), '|'])
1293
x = ''.join([x, '\n'])
1297
for c in col_widths:
1298
x = ''.join([x, '-' * (c+2), '+'])
1300
x = ''.join([x, '\n'])
1304
log.error("No data rows")
1310
locals = inspect.stack()[1][0].f_locals
1311
if "__module__" not in locals:
1312
raise TypeError("Must call mixin() from within class def.")
1314
dict = cls.__dict__.copy()
1315
dict.pop("__doc__", None)
1316
dict.pop("__module__", None)
1322
# TODO: Move usage stuff to to base/module/Module class
1325
# ------------------------- Usage Help
1326
USAGE_OPTIONS = ("[OPTIONS]", "", "heading", False)
1327
USAGE_LOGGING1 = ("Set the logging level:", "-l<level> or --logging=<level>", 'option', False)
1328
USAGE_LOGGING2 = ("", "<level>: none, info\*, error, warn, debug (\*default)", "option", False)
1329
USAGE_LOGGING3 = ("Run in debug mode:", "-g (same as option: -ldebug)", "option", False)
1330
USAGE_LOGGING_PLAIN = ("Output plain text only:", "-t", "option", False)
1331
USAGE_ARGS = ("[PRINTER|DEVICE-URI]", "", "heading", False)
1332
USAGE_ARGS2 = ("[PRINTER]", "", "heading", False)
1333
USAGE_DEVICE = ("To specify a device-URI:", "-d<device-uri> or --device=<device-uri>", "option", False)
1334
USAGE_PRINTER = ("To specify a CUPS printer:", "-p<printer> or --printer=<printer>", "option", False)
1335
USAGE_BUS1 = ("Bus to probe (if device not specified):", "-b<bus> or --bus=<bus>", "option", False)
1336
USAGE_BUS2 = ("", "<bus>: cups\*, usb\*, net, bt, fw, par\* (\*defaults) (Note: bt and fw not supported in this release.)", 'option', False)
1337
USAGE_HELP = ("This help information:", "-h or --help", "option", True)
1338
USAGE_SPACE = ("", "", "space", False)
1339
USAGE_EXAMPLES = ("Examples:", "", "heading", False)
1340
USAGE_NOTES = ("Notes:", "", "heading", False)
1341
USAGE_STD_NOTES1 = ("If device or printer is not specified, the local device bus is probed and the program enters interactive mode.", "", "note", False)
1342
USAGE_STD_NOTES2 = ("If -p\* is specified, the default CUPS printer will be used.", "", "note", False)
1343
USAGE_SEEALSO = ("See Also:", "", "heading", False)
1344
USAGE_LANGUAGE = ("Set the language:", "-q <lang> or --lang=<lang>. Use -q? or --lang=? to see a list of available language codes.", "option", False)
1345
USAGE_LANGUAGE2 = ("Set the language:", "--lang=<lang>. Use --lang=? to see a list of available language codes.", "option", False)
1346
USAGE_MODE = ("[MODE]", "", "header", False)
1347
USAGE_NON_INTERACTIVE_MODE = ("Run in non-interactive mode:", "-n or --non-interactive", "option", False)
1348
USAGE_GUI_MODE = ("Run in graphical UI mode:", "-u or --gui (Default)", "option", False)
1349
USAGE_INTERACTIVE_MODE = ("Run in interactive mode:", "-i or --interactive", "option", False)
1351
if sys_conf.get('configure', 'ui-toolkit', 'qt3') == 'qt3':
1352
USAGE_USE_QT3 = ("Use Qt3:", "--qt3 (Default)", "option", False)
1353
USAGE_USE_QT4 = ("Use Qt4:", "--qt4", "option", False)
1355
USAGE_USE_QT3 = ("Use Qt3:", "--qt3", "option", False)
1356
USAGE_USE_QT4 = ("Use Qt4:", "--qt4 (Default)", "option", False)
1361
def ttysize(): # TODO: Move to base/tui
1362
ln1 = commands.getoutput('stty -a').splitlines()[0]
1363
vals = {'rows':None, 'columns':None}
1364
for ph in ln1.split(';'):
1370
rows, cols = int(vals['rows']), int(vals['columns'])
1377
def usage_formatter(override=0): # TODO: Move to base/module/Module class
1378
rows, cols = ttysize()
1382
col2 = cols - col1 - 8
1384
col1 = int(cols / 3) - 8
1385
col2 = cols - col1 - 8
1387
return TextFormatter(({'width': col1, 'margin' : 2},
1388
{'width': col2, 'margin' : 2},))
1391
def format_text(text_list, typ='text', title='', crumb='', version=''): # TODO: Move to base/module/Module class
1393
Format usage text in multiple formats:
1394
text: for --help in the console
1395
rest: for conversion with rst2web for the website
1399
formatter = usage_formatter()
1401
for line in text_list:
1402
text1, text2, format, trailing_space = line
1404
# remove any reST/man escapes
1405
text1 = text1.replace("\\", "")
1406
text2 = text2.replace("\\", "")
1408
if format == 'summary':
1409
log.info(log.bold(text1))
1412
elif format in ('para', 'name', 'seealso'):
1418
elif format in ('heading', 'header'):
1419
log.info(log.bold(text1))
1421
elif format in ('option', 'example'):
1422
log.info(formatter.compose((text1, text2), trailing_space))
1424
elif format == 'note':
1425
if text1.startswith(' '):
1426
log.info('\t' + text1.lstrip())
1430
elif format == 'space':
1437
opt_colwidth1, opt_colwidth2 = 0, 0
1438
exmpl_colwidth1, exmpl_colwidth2 = 0, 0
1439
note_colwidth1, note_colwidth2 = 0, 0
1441
for line in text_list:
1442
text1, text2, format, trailing_space = line
1444
if format == 'option':
1445
opt_colwidth1 = max(len(text1), opt_colwidth1)
1446
opt_colwidth2 = max(len(text2), opt_colwidth2)
1448
elif format == 'example':
1449
exmpl_colwidth1 = max(len(text1), exmpl_colwidth1)
1450
exmpl_colwidth2 = max(len(text2), exmpl_colwidth2)
1452
elif format == 'note':
1453
note_colwidth1 = max(len(text1), note_colwidth1)
1454
note_colwidth2 = max(len(text2), note_colwidth2)
1458
exmpl_colwidth1 += 4
1459
exmpl_colwidth2 += 4
1462
opt_tablewidth = opt_colwidth1 + opt_colwidth2
1463
exmpl_tablewidth = exmpl_colwidth1 + exmpl_colwidth2
1464
note_tablewidth = note_colwidth1 + note_colwidth2
1466
# write the rst2web header
1467
log.info("""restindex
1471
file-extension: html
1473
/restindex\n""" % (title, crumb))
1475
t = "%s: %s (ver. %s)" % (crumb, title, version)
1477
log.info("="*len(t))
1481
needs_header = False
1482
for line in text_list:
1483
text1, text2, format, trailing_space = line
1485
if format == 'seealso':
1487
text1 = "`%s`_" % text1
1489
len1, len2 = len(text1), len(text2)
1491
if format == 'summary':
1492
log.info(''.join(["**", text1, "**"]))
1495
elif format in ('para', 'name'):
1500
elif format in ('heading', 'header'):
1503
log.info("**" + text1 + "**")
1507
elif format == 'option':
1509
log.info(".. class:: borderless")
1511
log.info(''.join(["+", "-"*opt_colwidth1, "+", "-"*opt_colwidth2, "+"]))
1512
needs_header = False
1514
if text1 and '`_' not in text1:
1515
log.info(''.join(["| *", text1, '*', " "*(opt_colwidth1-len1-3), "|", text2, " "*(opt_colwidth2-len2), "|"]))
1517
log.info(''.join(["|", text1, " "*(opt_colwidth1-len1), "|", text2, " "*(opt_colwidth2-len2), "|"]))
1519
log.info(''.join(["|", " "*(opt_colwidth1), "|", text2, " "*(opt_colwidth2-len2), "|"]))
1521
log.info(''.join(["+", "-"*opt_colwidth1, "+", "-"*opt_colwidth2, "+"]))
1523
elif format == 'example':
1525
log.info(".. class:: borderless")
1527
log.info(''.join(["+", "-"*exmpl_colwidth1, "+", "-"*exmpl_colwidth2, "+"]))
1528
needs_header = False
1530
if text1 and '`_' not in text1:
1531
log.info(''.join(["| *", text1, '*', " "*(exmpl_colwidth1-len1-3), "|", text2, " "*(exmpl_colwidth2-len2), "|"]))
1533
log.info(''.join(["|", text1, " "*(exmpl_colwidth1-len1), "|", text2, " "*(exmpl_colwidth2-len2), "|"]))
1535
log.info(''.join(["|", " "*(exmpl_colwidth1), "|", text2, " "*(exmpl_colwidth2-len2), "|"]))
1537
log.info(''.join(["+", "-"*exmpl_colwidth1, "+", "-"*exmpl_colwidth2, "+"]))
1539
elif format == 'seealso':
1540
if text1 and '`_' not in text1:
1544
elif format == 'note':
1546
log.info(".. class:: borderless")
1548
log.info(''.join(["+", "-"*note_colwidth1, "+", "-"*note_colwidth2, "+"]))
1549
needs_header = False
1551
if text1.startswith(' '):
1552
log.info(''.join(["|", " "*(note_tablewidth+1), "|"]))
1554
log.info(''.join(["|", text1, " "*(note_tablewidth-len1+1), "|"]))
1555
log.info(''.join(["+", "-"*note_colwidth1, "+", "-"*note_colwidth2, "+"]))
1557
elif format == 'space':
1561
log.info("\n.. _`%s`: %s.html\n" % (l, l.replace('hp-', '')))
1566
log.info('.TH "%s" 1 "%s" Linux "User Manuals"' % (crumb, version))
1567
log.info(".SH NAME\n%s \- %s" % (crumb, title))
1569
for line in text_list:
1570
text1, text2, format, trailing_space = line
1572
text1 = text1.replace("\\*", "*")
1573
text2 = text2.replace("\\*", "*")
1575
len1, len2 = len(text1), len(text2)
1577
if format == 'summary':
1578
log.info(".SH SYNOPSIS")
1579
log.info(".B %s" % text1.replace('Usage:', ''))
1581
elif format == 'name':
1582
log.info(".SH DESCRIPTION\n%s" % text1)
1584
elif format in ('option', 'example', 'note'):
1586
log.info('.IP "%s"\n%s' % (text1, text2))
1590
elif format in ('header', 'heading'):
1591
log.info(".SH %s" % text1.upper().replace(':', '').replace('[', '').replace(']', ''))
1593
elif format in ('seealso, para'):
1596
log.info(".SH AUTHOR")
1597
log.info("HPLIP (Hewlett-Packard Linux Imaging and Printing) is an")
1598
log.info("HP developed solution for printing, scanning, and faxing with")
1599
log.info("HP inkjet and laser based printers in Linux.")
1601
log.info(".SH REPORTING BUGS")
1602
log.info("The HPLIP Launchpad.net site")
1603
log.info(".B https://launchpad.net/hplip")
1604
log.info("is available to get help, report")
1605
log.info("bugs, make suggestions, discuss the HPLIP project or otherwise")
1606
log.info("contact the HPLIP Team.")
1608
log.info(".SH COPYRIGHT")
1609
log.info("Copyright (c) 2001-14 Hewlett-Packard Development Company, L.P.")
1611
log.info("This software comes with ABSOLUTELY NO WARRANTY.")
1612
log.info("This is free software, and you are welcome to distribute it")
1613
log.info("under certain conditions. See COPYING file for more details.")
1618
def log_title(program_name, version, show_ver=True): # TODO: Move to base/module/Module class
1622
log.info(log.bold("HP Linux Imaging and Printing System (ver. %s)" % prop.version))
1624
log.info(log.bold("HP Linux Imaging and Printing System"))
1626
log.info(log.bold("%s ver. %s" % (program_name, version)))
1628
log.info("Copyright (c) 2001-14 Hewlett-Packard Development Company, LP")
1629
log.info("This software comes with ABSOLUTELY NO WARRANTY.")
1630
log.info("This is free software, and you are welcome to distribute it")
1631
log.info("under certain conditions. See COPYING file for more details.")
1635
def ireplace(old, search, replace):
1636
regex = '(?i)' + re.escape(search)
1637
return re.sub(regex, replace, old)
1644
su_sudo_str = 'kdesu -- %s'
1646
elif utils.which('/usr/lib/kde4/libexec/kdesu'):
1647
su_sudo_str = '/usr/lib/kde4/libexec/kdesu -- %s'
1649
elif utils.which('kdesudo'):
1650
su_sudo_str = 'kdesudo -- %s'
1652
elif which('gnomesu'):
1653
su_sudo_str = 'gnomesu -c "%s"'
1656
su_sudo_str = 'gksu "%s"'
1663
# This function returns the distro name and distro version.
1664
#This is provided to check on Fedora 14 in pkit.py file for Plugin-installation.
1665
#is_su variable is used to provide a check on Fedora 8
1670
if which('lsb_release'):
1671
name = os.popen('lsb_release -i | cut -f 2')
1672
os_name = name.read().strip()
1674
version = os.popen('lsb_release -r | cut -f 2')
1675
os_version=version.read().strip()
1679
name = os.popen('cat /etc/issue | cut -c 1-6 | head -n 1')
1680
os_name = name.read().strip()
1682
version1=os.popen('cat /etc/issue | cut -c 16 | head -n 1')
1683
version2=version1.read().strip()
1685
if (version2 == '1'):
1686
version=os.popen('cat /etc/issue | cut -c 16-17 | head -n 1')
1689
version=os.popen('cat /etc/issue | cut -c 16 | head -n 1')
1691
os_version=version.read().strip()
1694
return os_name,os_version,is_su
1698
# Removes HTML or XML character references and entities from a text string.
1704
if text[:2] == "&#":
1705
# character reference
1707
if text[:3] == "&#x":
1708
#return unichr(int(text[3:-1], 16))
1709
return chr(int(text[3:-1], 16))
1711
#return unichr(int(text[2:-1]))
1712
return chr(int(text[2:-1]))
1718
#text = unichr(htmlentitydefs.name2codepoint[text[1:-1]])
1719
text = chr(htmlentitydefs.name2codepoint[text[1:-1]])
1722
return text # leave as is
1723
return re.sub("&#?\w+;", fixup, text)
1726
# Adds HTML or XML character references and entities from a text string
1729
if not isinstance(s, unicode):
1730
s = unicode(s) # hmmm...
1732
s = s.replace(u"&", u"&")
1734
for c in htmlentitydefs.codepoint2name:
1735
if c != 0x26: # exclude &
1736
s = s.replace(unichr(c), u"&%s;" % htmlentitydefs.codepoint2name[c])
1738
for c in range(0x20) + range(0x7f, 0xa0):
1739
s = s.replace(unichr(c), u"&#%d;" % c)
1743
# checks if given process is running.
1746
# None - if process is not running
1747
# grep output - if process is running
1749
def Is_Process_Running(process_name):
1751
p1 = Popen(["ps", "aux"], stdout=PIPE)
1752
p2 = Popen(["grep", process_name], stdin=p1.stdout, stdout=PIPE)
1753
p3 = Popen(["grep", "-v", "grep"], stdin=p2.stdout, stdout=PIPE)
1754
output = p3.communicate()[0]
1755
log.debug("Is_Process_Running outpu = %s " %output)
1757
if process_name in output:
1762
except Exception, e:
1763
log.error("Execution failed: process Name[%s]" %process_name)
1764
print >>sys.stderr, "Execution failed:", e
1767
#return tye: strings
1769
# None --> on error.
1770
# "terminal name"-->success
1772
terminal_list=['gnome-terminal', 'konsole','x-terminal-emulator', 'xterm', 'gtkterm']
1775
while cnt < len(terminal_list):
1776
if which(terminal_list[cnt]):
1777
terminal_cmd = terminal_list[cnt]+" -e "
1778
log.debug("Available Terminal = %s " %terminal_cmd)
1785
# True --> if it is older version
1786
# False --> if it is same or later version.
1788
def Is_HPLIP_older_version(installed_version, available_version):
1790
if available_version == "" or available_version == None or installed_version == "" or installed_version == None:
1791
log.debug("available_version is ''")
1794
installed_array=installed_version.split('.')
1795
available_array=available_version.split('.')
1797
log.debug("HPLIP Installed_version=%s Available_version=%s"%(installed_version,available_version))
1800
while cnt <len(installed_array) and cnt <len(available_array):
1801
if(int(installed_array[cnt]) < int(available_array[cnt])):
1804
elif(int(installed_array[cnt]) > int(available_array[cnt])):
1805
log.debug("Already new verison is installed")
1809
# To check internal version is installed.
1810
if Is_older is False and len(installed_array) >len(available_array):
1816
def downLoad_status(count, blockSize, totalSize):
1817
percent = int(count*blockSize*100/totalSize)
1819
sys.stdout.write("\b\b\b")
1820
sys.stdout.write("%s" %(log.color("%2d%%"%percent, 'bold')))
1824
def download_from_network(weburl, outputFile = None, useURLLIB=False):
1827
if weburl is "" or weburl is None:
1828
log.error("URL is empty")
1831
if outputFile is None:
1832
fp, outputFile = make_temp_file()
1835
if useURLLIB is False:
1836
wget = which("wget")
1838
wget = os.path.join(wget, "wget")
1839
status, output = run("%s --cache=off --timeout=60 --output-document=%s %s" %(wget, outputFile, weburl))
1841
log.error("Failed to connect to HPLIP site. Error code = %d" %status)
1847
sys.stdout.write("Download in progress...")
1848
urllib.urlretrieve(weburl, outputFile, downLoad_status)
1851
log.error("I/O Error: %s" % e.strerror)
1854
if not os.path.exists(outputFile):
1855
log.error("Failed to get hplip version/ %s file not found."%hplip_version_file)
1858
return True, outputFile
1865
def __init__(self, filename):
1866
self.Lock_filename = filename
1867
self.handler = open(self.Lock_filename, 'w')
1869
# Wait for another process to release resource and acquires the resource.
1871
fcntl.flock(self.handler, fcntl.LOCK_EX)
1874
fcntl.flock(self.handler, fcntl.LOCK_UN)
1877
self.handler.close()