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('simple-scan')
485
self.cmd_scan = 'simple-scan %SANE_URI%'
487
path = which('xsane')
489
self.cmd_scan = 'xsane -V %SANE_URI%'
491
path = which('kooka')
493
self.cmd_scan = 'kooka'
495
path = which('xscanimage')
497
self.cmd_scan = 'xscanimage'
500
path = which('hp-unload')
503
self.cmd_pcard = 'hp-unload -d %DEVICE_URI%'
505
self.cmd_pcard = 'python %HOME%/unload.py -d %DEVICE_URI%'
508
path = which('hp-makecopies')
511
self.cmd_copy = 'hp-makecopies -d %DEVICE_URI%'
513
self.cmd_copy = 'python %HOME%/makecopies.py -d %DEVICE_URI%'
516
path = which('hp-sendfax')
519
self.cmd_fax = 'hp-sendfax -d %FAX_URI%'
521
self.cmd_fax = 'python %HOME%/sendfax.py -d %FAX_URI%'
524
path = which('hp-fab')
527
self.cmd_fab = 'hp-fab'
529
self.cmd_fab = 'python %HOME%/fab.py'
533
log.debug("Loading user settings...")
534
self.auto_refresh = to_bool(user_conf.get('refresh', 'enable', '0'))
537
self.auto_refresh_rate = int(user_conf.get('refresh', 'rate', '30'))
539
self.auto_refresh_rate = 30 # (secs)
542
self.auto_refresh_type = int(user_conf.get('refresh', 'type', '0'))
544
self.auto_refresh_type = 0 # refresh 1 (1=refresh all)
546
self.cmd_print = user_conf.get('commands', 'prnt', self.cmd_print)
547
self.cmd_scan = user_conf.get('commands', 'scan', self.cmd_scan)
548
self.cmd_pcard = user_conf.get('commands', 'pcard', self.cmd_pcard)
549
self.cmd_copy = user_conf.get('commands', 'cpy', self.cmd_copy)
550
self.cmd_fax = user_conf.get('commands', 'fax', self.cmd_fax)
551
self.cmd_fab = user_conf.get('commands', 'fab', self.cmd_fab)
553
self.upgrade_notify= to_bool(user_conf.get('upgrade', 'notify_upgrade', '0'))
554
self.upgrade_last_update_time = int(user_conf.get('upgrade','last_upgraded_time', '0'))
555
self.upgrade_pending_update_time =int(user_conf.get('upgrade', 'pending_upgrade_time', '0'))
556
self.latest_available_version=str(user_conf.get('upgrade', 'latest_available_version',''))
560
log.debug("Print command: %s" % self.cmd_print)
561
log.debug("PCard command: %s" % self.cmd_pcard)
562
log.debug("Fax command: %s" % self.cmd_fax)
563
log.debug("FAB command: %s" % self.cmd_fab)
564
log.debug("Copy command: %s " % self.cmd_copy)
565
log.debug("Scan command: %s" % self.cmd_scan)
566
log.debug("Auto refresh: %s" % self.auto_refresh)
567
log.debug("Auto refresh rate: %s" % self.auto_refresh_rate)
568
log.debug("Auto refresh type: %s" % self.auto_refresh_type)
569
log.debug("Upgrade notification:%d" %self.upgrade_notify)
570
log.debug("Last Installed time:%d" %self.upgrade_last_update_time)
571
log.debug("Next scheduled installation time:%d" % self.upgrade_pending_update_time)
575
log.debug("Saving user settings...")
576
user_conf.set('commands', 'prnt', self.cmd_print)
577
user_conf.set('commands', 'pcard', self.cmd_pcard)
578
user_conf.set('commands', 'fax', self.cmd_fax)
579
user_conf.set('commands', 'scan', self.cmd_scan)
580
user_conf.set('commands', 'cpy', self.cmd_copy)
581
user_conf.set('refresh', 'enable',self.auto_refresh)
582
user_conf.set('refresh', 'rate', self.auto_refresh_rate)
583
user_conf.set('refresh', 'type', self.auto_refresh_type)
584
user_conf.set('upgrade', 'notify_upgrade', self.upgrade_notify)
585
user_conf.set('upgrade','last_upgraded_time', self.upgrade_last_update_time)
586
user_conf.set('upgrade', 'pending_upgrade_time', self.upgrade_pending_update_time)
587
user_conf.set('upgrade', 'latest_available_version', self.latest_available_version)
593
def no_qt_message_gtk():
597
dialog = gtk.MessageDialog(w, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
598
gtk.MESSAGE_WARNING, gtk.BUTTONS_OK,
599
"PyQt not installed. GUI not available. Please check that the PyQt package is installed. Exiting.")
604
log.error("PyQt not installed. GUI not available. Please check that the PyQt package is installed. Exiting.")
607
def canEnterGUIMode(): # qt3
608
if not prop.gui_build:
609
log.warn("GUI mode disabled in build.")
612
elif not os.getenv('DISPLAY'):
613
log.warn("No display found.")
616
elif not checkPyQtImport():
617
log.warn("Qt/PyQt 3 initialization failed.")
623
def canEnterGUIMode4(): # qt4
624
if not prop.gui_build:
625
log.warn("GUI mode disabled in build.")
628
elif not os.getenv('DISPLAY'):
629
log.warn("No display found.")
632
elif not checkPyQtImport4():
633
log.warn("Qt/PyQt 4 initialization failed.")
639
def checkPyQtImport(): # qt3
644
if os.getenv('DISPLAY') and os.getenv('STARTED_FROM_MENU'):
647
log.error("PyQt not installed. GUI not available. Exiting.")
650
# check version of Qt
651
qtMajor = int(qt.qVersion().split('.')[0])
653
if qtMajor < MINIMUM_QT_MAJOR_VER:
655
log.error("Incorrect version of Qt installed. Ver. 3.0.0 or greater required.")
658
#check version of PyQt
660
pyqtVersion = qt.PYQT_VERSION_STR
661
except AttributeError:
662
pyqtVersion = qt.PYQT_VERSION
664
while pyqtVersion.count('.') < 2:
667
(maj_ver, min_ver, pat_ver) = pyqtVersion.split('.')
669
if pyqtVersion.find('snapshot') >= 0:
670
log.warning("A non-stable snapshot version of PyQt is installed.")
673
maj_ver = int(maj_ver)
674
min_ver = int(min_ver)
675
pat_ver = int(pat_ver)
677
maj_ver, min_ver, pat_ver = 0, 0, 0
679
if maj_ver < MINIMUM_PYQT_MAJOR_VER or \
680
(maj_ver == MINIMUM_PYQT_MAJOR_VER and min_ver < MINIMUM_PYQT_MINOR_VER):
681
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))
682
log.error("Incorrect version of pyQt installed. Ver. %d.%d or greater required." % (MINIMUM_PYQT_MAJOR_VER, MINIMUM_PYQT_MINOR_VER))
683
log.error("This program will continue, but you may experience errors, crashes or other problems.")
689
def checkPyQtImport4():
699
from string import Template # will fail in Python <= 2.3
701
# Code from Python 2.4 string.py
705
"""Helper class for combining multiple mappings.
707
Used by .{safe_,}substitute() to combine the mapping and keyword
710
def __init__(self, primary, secondary):
711
self._primary = primary
712
self._secondary = secondary
714
def __getitem__(self, key):
716
return self._primary[key]
718
return self._secondary[key]
721
class _TemplateMetaclass(type):
724
(?P<escaped>%(delim)s) | # Escape sequence of two delimiters
725
(?P<named>%(id)s) | # delimiter and a Python identifier
726
{(?P<braced>%(id)s)} | # delimiter and a braced identifier
727
(?P<invalid>) # Other ill-formed delimiter exprs
731
def __init__(cls, name, bases, dct):
732
super(_TemplateMetaclass, cls).__init__(name, bases, dct)
734
pattern = cls.pattern
736
pattern = _TemplateMetaclass.pattern % {
737
'delim' : re.escape(cls.delimiter),
738
'id' : cls.idpattern,
740
cls.pattern = re.compile(pattern, re.IGNORECASE | re.VERBOSE)
744
"""A string class for supporting $-substitutions."""
745
__metaclass__ = _TemplateMetaclass
748
idpattern = r'[_a-z][_a-z0-9]*'
750
def __init__(self, template):
751
self.template = template
753
# Search for $$, $identifier, ${identifier}, and any bare $'s
754
def _invalid(self, mo):
755
i = mo.start('invalid')
756
lines = self.template[:i].splitlines(True)
761
colno = i - len(''.join(lines[:-1]))
763
raise ValueError('Invalid placeholder in string: line %d, col %d' %
766
def substitute(self, *args, **kws):
768
raise TypeError('Too many positional arguments')
772
mapping = _multimap(kws, args[0])
775
# Helper function for .sub()
777
# Check the most common path first.
778
named = mo.group('named') or mo.group('braced')
779
if named is not None:
781
# We use this idiom instead of str() because the latter will
782
# fail if val is a Unicode containing non-ASCII characters.
784
if mo.group('escaped') is not None:
785
return self.delimiter
786
if mo.group('invalid') is not None:
788
raise ValueError('Unrecognized named group in pattern',
790
return self.pattern.sub(convert, self.template)
793
def safe_substitute(self, *args, **kws):
795
raise TypeError('Too many positional arguments')
799
mapping = _multimap(kws, args[0])
802
# Helper function for .sub()
804
named = mo.group('named')
805
if named is not None:
807
# We use this idiom instead of str() because the latter
808
# will fail if val is a Unicode containing non-ASCII
809
return '%s' % mapping[named]
811
return self.delimiter + named
812
braced = mo.group('braced')
813
if braced is not None:
815
return '%s' % mapping[braced]
817
return self.delimiter + '{' + braced + '}'
818
if mo.group('escaped') is not None:
819
return self.delimiter
820
if mo.group('invalid') is not None:
821
return self.delimiter
822
raise ValueError('Unrecognized named group in pattern',
824
return self.pattern.sub(convert, self.template)
828
#cat = lambda _ : Template(_).substitute(sys._getframe(1).f_globals, **sys._getframe(1).f_locals)
831
globals = sys._getframe(1).f_globals.copy()
832
if 'self' in globals:
835
locals = sys._getframe(1).f_locals.copy()
839
return Template(s).substitute(sys._getframe(1).f_globals, **locals)
842
identity = string.maketrans('','')
843
unprintable = identity.translate(identity, string.printable)
847
return s.translate(identity, unprintable)
850
def any(S,f=lambda x:x):
856
def all(S,f=lambda x:x):
858
if not f(x): return False
862
BROWSERS = ['firefox', 'mozilla', 'konqueror', 'galeon', 'skipstone'] # in preferred order
863
BROWSER_OPTS = {'firefox': '-new-window', 'mozilla' : '', 'konqueror': '', 'galeon': '-w', 'skipstone': ''}
867
if platform_avail and platform.system() == 'Darwin':
877
def openURL(url, use_browser_opts=True):
878
if platform_avail and platform.system() == 'Darwin':
879
cmd = 'open "%s"' % url
886
bb = os.path.join(bb, b)
888
cmd = """%s %s "%s" &""" % (bb, BROWSER_OPTS[b], url)
890
cmd = """%s "%s" &""" % (bb, url)
895
log.warn("Unable to open URL: %s" % url)
898
def uniqueList(input):
900
[temp.append(i) for i in input if not temp.count(i)]
904
def list_move_up(l, m, cmp=None):
906
f = lambda x: l[x] == m
908
f = lambda x: cmp(l[x], m)
910
for i in range(1, len(l)):
912
l[i-1], l[i] = l[i], l[i-1]
915
def list_move_down(l, m, cmp=None):
917
f = lambda x: l[x] == m
919
f = lambda x: cmp(l[x], m)
921
for i in range(len(l)-2, -1, -1):
923
l[i], l[i+1] = l[i+1], l[i]
927
class XMLToDictParser:
933
def startElement(self, name, attrs):
934
#print "START:", name, attrs
935
self.stack.append(unicode(name).lower())
936
self.last_start = unicode(name).lower()
940
self.stack.append(unicode(a).lower())
941
self.addData(attrs[a])
944
def endElement(self, name):
945
if name.lower() == self.last_start:
951
def charData(self, data):
952
data = unicode(data).strip()
954
if data and self.stack:
957
def addData(self, data):
965
stack_str = '-'.join(self.stack)
966
stack_str_0 = '-'.join([stack_str, '0'])
972
self.data[stack_str_0]
974
self.data[stack_str] = data
979
self.data['-'.join([stack_str, unicode(j)])]
981
self.data['-'.join([stack_str, unicode(j)])] = data
986
self.data[stack_str_0] = self.data[stack_str]
987
self.data['-'.join([stack_str, '1'])] = data
988
del self.data[stack_str]
991
def parseXML(self, text):
992
parser = expat.ParserCreate()
993
parser.StartElementHandler = self.startElement
994
parser.EndElementHandler = self.endElement
995
parser.CharacterDataHandler = self.charData
996
parser.Parse(text.encode('utf-8'), True)
1001
return ''.join(['"', s, '"'])
1004
# Python 2.2.x compatibility functions (strip() family with char argument added in Python 2.2.3)
1005
if sys.hexversion < 0x020203f0:
1006
def xlstrip(s, chars=' '):
1008
for c, i in zip(s, range(len(s))):
1014
def xrstrip(s, chars=' '):
1015
return xreverse(xlstrip(xreverse(s), chars))
1022
def xstrip(s, chars=' '):
1023
return xreverse(xlstrip(xreverse(xlstrip(s, chars)), chars))
1026
xlstrip = string.lstrip
1027
xrstrip = string.rstrip
1028
xstrip = string.strip
1033
return int(platform.architecture()[0][:-3])
1035
return struct.calcsize("P") << 3
1040
return platform.machine().replace(' ', '_').lower() # i386, i686, power_macintosh, etc.
1042
return "i686" # TODO: Need a fix here
1046
if sys.byteorder == 'big':
1049
return LITTLE_ENDIAN
1053
return getpass.getpass("Enter password: ")
1055
def get_password_ui(pswd_msg=''):
1056
fp = open("/etc/hp/hplip.conf", "r")
1059
if string.find(line, "qt4") is not -1 and string.find(line, "yes") is not -1:
1063
from ui4.setupdialog import showPasswordUI
1065
username, password = showPasswordUI("Your HP Device requires to install HP proprietary plugin\nPlease enter root/superuser password to continue", "root", False)
1067
username, password = showPasswordUI(pswd_msg, "root", False)
1069
from ui.setupform import showPasswordUI
1071
username, password = showPasswordUI("Your HP Device requires to install HP proprietary plugin\nPlease enter root/superuser password to continue", "root", False)
1073
username, password = showPasswordUI(pswd_msg, "root", False)
1076
def run(cmd, log_output=True, password_func=get_password, timeout=1, spinner=True, pswd_msg=''):
1077
output = cStringIO.StringIO()
1080
child = pexpect.spawn(cmd, timeout=timeout)
1081
except pexpect.ExceptionPexpect:
1089
i = child.expect(["[pP]assword:", pexpect.EOF, pexpect.TIMEOUT])
1092
output.write(child.before)
1094
log.debug(child.before)
1096
if i == 0: # Password:
1097
if password_func is not None:
1098
if password_func == "get_password_ui":
1099
child.sendline(get_password_ui(pswd_msg))
1101
child.sendline(password_func())
1103
child.sendline(get_password())
1109
elif i == 2: # TIMEOUT
1113
except Exception, e:
1114
log.error("Exception: %s" % e)
1119
return child.exitstatus, output.getvalue()
1122
def expand_range(ns): # ns -> string repr. of numeric range, e.g. "1-4, 7, 9-12"
1123
"""Credit: Jean Brouwers, comp.lang.python 16-7-2004
1124
Convert a string representation of a set of ranges into a
1126
u"1-4, 7, 9-12" --> [1,2,3,4,7,9,10,11,12]
1129
for n in ns.split(u','):
1132
if len(r) == 2: # expand name with range
1133
h = r[0].rstrip(u'0123456789') # header
1134
r[0] = r[0][len(h):]
1135
# range can't be empty
1136
if not (r[0] and r[1]):
1137
raise ValueError, 'empty range: ' + n
1138
# handle leading zeros
1139
if r[0] == u'0' or r[0][0] != u'0':
1142
w = [len(i) for i in r]
1144
raise ValueError, 'wide range: ' + n
1145
h += u'%%0%dd' % max(w)
1147
r = [int(i, 10) for i in r]
1149
raise ValueError, 'bad range: ' + n
1150
for i in range(r[0], r[1]+1):
1156
fs = dict([(n, i) for i, n in enumerate(fs)]).keys()
1157
# convert to ints and sort
1158
fs = [int(x) for x in fs if x]
1164
def collapse_range(x): # x --> sorted list of ints
1165
""" Convert a list of integers into a string
1166
range representation:
1167
[1,2,3,4,7,9,10,11,12] --> u"1-4,7,9-12"
1172
s, c, r = [str(x[0])], x[0], False
1179
s.append(u'-%s,%s' % (c,i))
1182
s.append(u',%s' % i)
1187
s.append(u'-%s' % i)
1192
def createSequencedFilename(basename, ext, dir=None, digits=3):
1197
for f in walkFiles(dir, recurse=False, abs_paths=False, return_folders=False, pattern='*', path=None):
1198
r, e = os.path.splitext(f)
1200
if r.startswith(basename) and ext == e:
1202
i = int(r[len(basename):])
1208
return os.path.join(dir, "%s%0*d%s" % (basename, digits, m+1, ext))
1211
def validate_language(lang, default='en_US'):
1213
loc, encoder = locale.getdefaultlocale()
1215
lang = lang.lower().strip()
1216
for loc, ll in supported_locales.items():
1221
log.warn("Unknown lang/locale. Using default of %s." % loc)
1226
def gen_random_uuid():
1228
import uuid # requires Python 2.5+
1229
return str(uuid.uuid4())
1232
uuidgen = which("uuidgen")
1234
uuidgen = os.path.join(uuidgen, "uuidgen")
1235
return commands.getoutput(uuidgen) # TODO: Replace with subprocess (commands is deprecated in Python 3.0)
1240
class RestTableFormatter(object):
1241
def __init__(self, header=None):
1242
self.header = header # tuple of strings
1243
self.rows = [] # list of tuples
1245
def add(self, row_data): # tuple of strings
1246
self.rows.append(row_data)
1248
def output(self, w):
1250
num_cols = len(self.rows[0])
1252
if len(r) != num_cols:
1253
log.error("Invalid number of items in row: %s" % r)
1256
if len(self.header) != num_cols:
1257
log.error("Invalid number of items in header.")
1260
for x, c in enumerate(self.header):
1263
max_width = max(max_width, len(r[x]))
1265
col_widths.append(max_width+2)
1268
for c in col_widths:
1269
x = ''.join([x, '-' * (c+2), '+'])
1271
x = ''.join([x, '\n'])
1277
for i, c in enumerate(col_widths):
1278
x = ''.join([x, ' ', self.header[i], ' ' * (c+1-len(self.header[i])), '|'])
1280
x = ''.join([x, '\n'])
1284
for c in col_widths:
1285
x = ''.join([x, '=' * (c+2), '+'])
1287
x = ''.join([x, '\n'])
1291
for j, r in enumerate(self.rows):
1293
for i, c in enumerate(col_widths):
1294
x = ''.join([x, ' ', self.rows[j][i], ' ' * (c+1-len(self.rows[j][i])), '|'])
1296
x = ''.join([x, '\n'])
1300
for c in col_widths:
1301
x = ''.join([x, '-' * (c+2), '+'])
1303
x = ''.join([x, '\n'])
1307
log.error("No data rows")
1313
locals = inspect.stack()[1][0].f_locals
1314
if "__module__" not in locals:
1315
raise TypeError("Must call mixin() from within class def.")
1317
dict = cls.__dict__.copy()
1318
dict.pop("__doc__", None)
1319
dict.pop("__module__", None)
1325
# TODO: Move usage stuff to to base/module/Module class
1328
# ------------------------- Usage Help
1329
USAGE_OPTIONS = ("[OPTIONS]", "", "heading", False)
1330
USAGE_LOGGING1 = ("Set the logging level:", "-l<level> or --logging=<level>", 'option', False)
1331
USAGE_LOGGING2 = ("", "<level>: none, info\*, error, warn, debug (\*default)", "option", False)
1332
USAGE_LOGGING3 = ("Run in debug mode:", "-g (same as option: -ldebug)", "option", False)
1333
USAGE_LOGGING_PLAIN = ("Output plain text only:", "-t", "option", False)
1334
USAGE_ARGS = ("[PRINTER|DEVICE-URI]", "", "heading", False)
1335
USAGE_ARGS2 = ("[PRINTER]", "", "heading", False)
1336
USAGE_DEVICE = ("To specify a device-URI:", "-d<device-uri> or --device=<device-uri>", "option", False)
1337
USAGE_PRINTER = ("To specify a CUPS printer:", "-p<printer> or --printer=<printer>", "option", False)
1338
USAGE_BUS1 = ("Bus to probe (if device not specified):", "-b<bus> or --bus=<bus>", "option", False)
1339
USAGE_BUS2 = ("", "<bus>: cups\*, usb\*, net, bt, fw, par\* (\*defaults) (Note: bt and fw not supported in this release.)", 'option', False)
1340
USAGE_HELP = ("This help information:", "-h or --help", "option", True)
1341
USAGE_SPACE = ("", "", "space", False)
1342
USAGE_EXAMPLES = ("Examples:", "", "heading", False)
1343
USAGE_NOTES = ("Notes:", "", "heading", False)
1344
USAGE_STD_NOTES1 = ("If device or printer is not specified, the local device bus is probed and the program enters interactive mode.", "", "note", False)
1345
USAGE_STD_NOTES2 = ("If -p\* is specified, the default CUPS printer will be used.", "", "note", False)
1346
USAGE_SEEALSO = ("See Also:", "", "heading", False)
1347
USAGE_LANGUAGE = ("Set the language:", "-q <lang> or --lang=<lang>. Use -q? or --lang=? to see a list of available language codes.", "option", False)
1348
USAGE_LANGUAGE2 = ("Set the language:", "--lang=<lang>. Use --lang=? to see a list of available language codes.", "option", False)
1349
USAGE_MODE = ("[MODE]", "", "header", False)
1350
USAGE_NON_INTERACTIVE_MODE = ("Run in non-interactive mode:", "-n or --non-interactive", "option", False)
1351
USAGE_GUI_MODE = ("Run in graphical UI mode:", "-u or --gui (Default)", "option", False)
1352
USAGE_INTERACTIVE_MODE = ("Run in interactive mode:", "-i or --interactive", "option", False)
1354
if sys_conf.get('configure', 'ui-toolkit', 'qt3') == 'qt3':
1355
USAGE_USE_QT3 = ("Use Qt3:", "--qt3 (Default)", "option", False)
1356
USAGE_USE_QT4 = ("Use Qt4:", "--qt4", "option", False)
1358
USAGE_USE_QT3 = ("Use Qt3:", "--qt3", "option", False)
1359
USAGE_USE_QT4 = ("Use Qt4:", "--qt4 (Default)", "option", False)
1364
def ttysize(): # TODO: Move to base/tui
1365
ln1 = commands.getoutput('stty -a').splitlines()[0]
1366
vals = {'rows':None, 'columns':None}
1367
for ph in ln1.split(';'):
1373
rows, cols = int(vals['rows']), int(vals['columns'])
1380
def usage_formatter(override=0): # TODO: Move to base/module/Module class
1381
rows, cols = ttysize()
1385
col2 = cols - col1 - 8
1387
col1 = int(cols / 3) - 8
1388
col2 = cols - col1 - 8
1390
return TextFormatter(({'width': col1, 'margin' : 2},
1391
{'width': col2, 'margin' : 2},))
1394
def format_text(text_list, typ='text', title='', crumb='', version=''): # TODO: Move to base/module/Module class
1396
Format usage text in multiple formats:
1397
text: for --help in the console
1398
rest: for conversion with rst2web for the website
1402
formatter = usage_formatter()
1404
for line in text_list:
1405
text1, text2, format, trailing_space = line
1407
# remove any reST/man escapes
1408
text1 = text1.replace("\\", "")
1409
text2 = text2.replace("\\", "")
1411
if format == 'summary':
1412
log.info(log.bold(text1))
1415
elif format in ('para', 'name', 'seealso'):
1421
elif format in ('heading', 'header'):
1422
log.info(log.bold(text1))
1424
elif format in ('option', 'example'):
1425
log.info(formatter.compose((text1, text2), trailing_space))
1427
elif format == 'note':
1428
if text1.startswith(' '):
1429
log.info('\t' + text1.lstrip())
1433
elif format == 'space':
1440
opt_colwidth1, opt_colwidth2 = 0, 0
1441
exmpl_colwidth1, exmpl_colwidth2 = 0, 0
1442
note_colwidth1, note_colwidth2 = 0, 0
1444
for line in text_list:
1445
text1, text2, format, trailing_space = line
1447
if format == 'option':
1448
opt_colwidth1 = max(len(text1), opt_colwidth1)
1449
opt_colwidth2 = max(len(text2), opt_colwidth2)
1451
elif format == 'example':
1452
exmpl_colwidth1 = max(len(text1), exmpl_colwidth1)
1453
exmpl_colwidth2 = max(len(text2), exmpl_colwidth2)
1455
elif format == 'note':
1456
note_colwidth1 = max(len(text1), note_colwidth1)
1457
note_colwidth2 = max(len(text2), note_colwidth2)
1461
exmpl_colwidth1 += 4
1462
exmpl_colwidth2 += 4
1465
opt_tablewidth = opt_colwidth1 + opt_colwidth2
1466
exmpl_tablewidth = exmpl_colwidth1 + exmpl_colwidth2
1467
note_tablewidth = note_colwidth1 + note_colwidth2
1469
# write the rst2web header
1470
log.info("""restindex
1474
file-extension: html
1476
/restindex\n""" % (title, crumb))
1478
t = "%s: %s (ver. %s)" % (crumb, title, version)
1480
log.info("="*len(t))
1484
needs_header = False
1485
for line in text_list:
1486
text1, text2, format, trailing_space = line
1488
if format == 'seealso':
1490
text1 = "`%s`_" % text1
1492
len1, len2 = len(text1), len(text2)
1494
if format == 'summary':
1495
log.info(''.join(["**", text1, "**"]))
1498
elif format in ('para', 'name'):
1503
elif format in ('heading', 'header'):
1506
log.info("**" + text1 + "**")
1510
elif format == 'option':
1512
log.info(".. class:: borderless")
1514
log.info(''.join(["+", "-"*opt_colwidth1, "+", "-"*opt_colwidth2, "+"]))
1515
needs_header = False
1517
if text1 and '`_' not in text1:
1518
log.info(''.join(["| *", text1, '*', " "*(opt_colwidth1-len1-3), "|", text2, " "*(opt_colwidth2-len2), "|"]))
1520
log.info(''.join(["|", text1, " "*(opt_colwidth1-len1), "|", text2, " "*(opt_colwidth2-len2), "|"]))
1522
log.info(''.join(["|", " "*(opt_colwidth1), "|", text2, " "*(opt_colwidth2-len2), "|"]))
1524
log.info(''.join(["+", "-"*opt_colwidth1, "+", "-"*opt_colwidth2, "+"]))
1526
elif format == 'example':
1528
log.info(".. class:: borderless")
1530
log.info(''.join(["+", "-"*exmpl_colwidth1, "+", "-"*exmpl_colwidth2, "+"]))
1531
needs_header = False
1533
if text1 and '`_' not in text1:
1534
log.info(''.join(["| *", text1, '*', " "*(exmpl_colwidth1-len1-3), "|", text2, " "*(exmpl_colwidth2-len2), "|"]))
1536
log.info(''.join(["|", text1, " "*(exmpl_colwidth1-len1), "|", text2, " "*(exmpl_colwidth2-len2), "|"]))
1538
log.info(''.join(["|", " "*(exmpl_colwidth1), "|", text2, " "*(exmpl_colwidth2-len2), "|"]))
1540
log.info(''.join(["+", "-"*exmpl_colwidth1, "+", "-"*exmpl_colwidth2, "+"]))
1542
elif format == 'seealso':
1543
if text1 and '`_' not in text1:
1547
elif format == 'note':
1549
log.info(".. class:: borderless")
1551
log.info(''.join(["+", "-"*note_colwidth1, "+", "-"*note_colwidth2, "+"]))
1552
needs_header = False
1554
if text1.startswith(' '):
1555
log.info(''.join(["|", " "*(note_tablewidth+1), "|"]))
1557
log.info(''.join(["|", text1, " "*(note_tablewidth-len1+1), "|"]))
1558
log.info(''.join(["+", "-"*note_colwidth1, "+", "-"*note_colwidth2, "+"]))
1560
elif format == 'space':
1564
log.info("\n.. _`%s`: %s.html\n" % (l, l.replace('hp-', '')))
1569
log.info('.TH "%s" 1 "%s" Linux "User Manuals"' % (crumb, version))
1570
log.info(".SH NAME\n%s \- %s" % (crumb, title))
1572
for line in text_list:
1573
text1, text2, format, trailing_space = line
1575
text1 = text1.replace("\\*", "*")
1576
text2 = text2.replace("\\*", "*")
1578
len1, len2 = len(text1), len(text2)
1580
if format == 'summary':
1581
log.info(".SH SYNOPSIS")
1582
log.info(".B %s" % text1.replace('Usage:', ''))
1584
elif format == 'name':
1585
log.info(".SH DESCRIPTION\n%s" % text1)
1587
elif format in ('option', 'example', 'note'):
1589
log.info('.IP "%s"\n%s' % (text1, text2))
1593
elif format in ('header', 'heading'):
1594
log.info(".SH %s" % text1.upper().replace(':', '').replace('[', '').replace(']', ''))
1596
elif format in ('seealso, para'):
1599
log.info(".SH AUTHOR")
1600
log.info("HPLIP (Hewlett-Packard Linux Imaging and Printing) is an")
1601
log.info("HP developed solution for printing, scanning, and faxing with")
1602
log.info("HP inkjet and laser based printers in Linux.")
1604
log.info(".SH REPORTING BUGS")
1605
log.info("The HPLIP Launchpad.net site")
1606
log.info(".B https://launchpad.net/hplip")
1607
log.info("is available to get help, report")
1608
log.info("bugs, make suggestions, discuss the HPLIP project or otherwise")
1609
log.info("contact the HPLIP Team.")
1611
log.info(".SH COPYRIGHT")
1612
log.info("Copyright (c) 2001-14 Hewlett-Packard Development Company, L.P.")
1614
log.info("This software comes with ABSOLUTELY NO WARRANTY.")
1615
log.info("This is free software, and you are welcome to distribute it")
1616
log.info("under certain conditions. See COPYING file for more details.")
1621
def log_title(program_name, version, show_ver=True): # TODO: Move to base/module/Module class
1625
log.info(log.bold("HP Linux Imaging and Printing System (ver. %s)" % prop.version))
1627
log.info(log.bold("HP Linux Imaging and Printing System"))
1629
log.info(log.bold("%s ver. %s" % (program_name, version)))
1631
log.info("Copyright (c) 2001-14 Hewlett-Packard Development Company, LP")
1632
log.info("This software comes with ABSOLUTELY NO WARRANTY.")
1633
log.info("This is free software, and you are welcome to distribute it")
1634
log.info("under certain conditions. See COPYING file for more details.")
1638
def ireplace(old, search, replace):
1639
regex = '(?i)' + re.escape(search)
1640
return re.sub(regex, replace, old)
1647
su_sudo_str = 'kdesu -- %s'
1649
elif utils.which('/usr/lib/kde4/libexec/kdesu'):
1650
su_sudo_str = '/usr/lib/kde4/libexec/kdesu -- %s'
1652
elif utils.which('kdesudo'):
1653
su_sudo_str = 'kdesudo -- %s'
1655
elif which('gnomesu'):
1656
su_sudo_str = 'gnomesu -c "%s"'
1659
su_sudo_str = 'gksu "%s"'
1666
# This function returns the distro name and distro version.
1667
#This is provided to check on Fedora 14 in pkit.py file for Plugin-installation.
1668
#is_su variable is used to provide a check on Fedora 8
1673
if which('lsb_release'):
1674
name = os.popen('lsb_release -i | cut -f 2')
1675
os_name = name.read().strip()
1677
version = os.popen('lsb_release -r | cut -f 2')
1678
os_version=version.read().strip()
1682
name = os.popen('cat /etc/issue | cut -c 1-6 | head -n 1')
1683
os_name = name.read().strip()
1685
version1=os.popen('cat /etc/issue | cut -c 16 | head -n 1')
1686
version2=version1.read().strip()
1688
if (version2 == '1'):
1689
version=os.popen('cat /etc/issue | cut -c 16-17 | head -n 1')
1692
version=os.popen('cat /etc/issue | cut -c 16 | head -n 1')
1694
os_version=version.read().strip()
1697
return os_name,os_version,is_su
1701
# Removes HTML or XML character references and entities from a text string.
1707
if text[:2] == "&#":
1708
# character reference
1710
if text[:3] == "&#x":
1711
#return unichr(int(text[3:-1], 16))
1712
return chr(int(text[3:-1], 16))
1714
#return unichr(int(text[2:-1]))
1715
return chr(int(text[2:-1]))
1721
#text = unichr(htmlentitydefs.name2codepoint[text[1:-1]])
1722
text = chr(htmlentitydefs.name2codepoint[text[1:-1]])
1725
return text # leave as is
1726
return re.sub("&#?\w+;", fixup, text)
1729
# Adds HTML or XML character references and entities from a text string
1732
if not isinstance(s, unicode):
1733
s = unicode(s) # hmmm...
1735
s = s.replace(u"&", u"&")
1737
for c in htmlentitydefs.codepoint2name:
1738
if c != 0x26: # exclude &
1739
s = s.replace(unichr(c), u"&%s;" % htmlentitydefs.codepoint2name[c])
1741
for c in range(0x20) + range(0x7f, 0xa0):
1742
s = s.replace(unichr(c), u"&#%d;" % c)
1746
# checks if given process is running.
1749
# None - if process is not running
1750
# grep output - if process is running
1752
def Is_Process_Running(process_name):
1754
p1 = Popen(["ps", "aux"], stdout=PIPE)
1755
p2 = Popen(["grep", process_name], stdin=p1.stdout, stdout=PIPE)
1756
p3 = Popen(["grep", "-v", "grep"], stdin=p2.stdout, stdout=PIPE)
1757
output = p3.communicate()[0]
1758
log.debug("Is_Process_Running outpu = %s " %output)
1760
if process_name in output:
1765
except Exception, e:
1766
log.error("Execution failed: process Name[%s]" %process_name)
1767
print >>sys.stderr, "Execution failed:", e
1770
#return tye: strings
1772
# None --> on error.
1773
# "terminal name"-->success
1775
terminal_list=['gnome-terminal', 'konsole','x-terminal-emulator', 'xterm', 'gtkterm']
1778
while cnt < len(terminal_list):
1779
if which(terminal_list[cnt]):
1780
terminal_cmd = terminal_list[cnt]+" -e "
1781
log.debug("Available Terminal = %s " %terminal_cmd)
1788
# True --> if it is older version
1789
# False --> if it is same or later version.
1791
def Is_HPLIP_older_version(installed_version, available_version):
1793
if available_version == "" or available_version == None or installed_version == "" or installed_version == None:
1794
log.debug("available_version is ''")
1797
installed_array=installed_version.split('.')
1798
available_array=available_version.split('.')
1800
log.debug("HPLIP Installed_version=%s Available_version=%s"%(installed_version,available_version))
1803
while cnt <len(installed_array) and cnt <len(available_array):
1804
if(int(installed_array[cnt]) < int(available_array[cnt])):
1807
elif(int(installed_array[cnt]) > int(available_array[cnt])):
1808
log.debug("Already new verison is installed")
1812
# To check internal version is installed.
1813
if Is_older is False and len(installed_array) >len(available_array):
1819
def downLoad_status(count, blockSize, totalSize):
1820
percent = int(count*blockSize*100/totalSize)
1822
sys.stdout.write("\b\b\b")
1823
sys.stdout.write("%s" %(log.color("%2d%%"%percent, 'bold')))
1827
def download_from_network(weburl, outputFile = None, useURLLIB=False):
1830
if weburl is "" or weburl is None:
1831
log.error("URL is empty")
1834
if outputFile is None:
1835
fp, outputFile = make_temp_file()
1838
if useURLLIB is False:
1839
wget = which("wget")
1841
wget = os.path.join(wget, "wget")
1842
status, output = run("%s --cache=off --timeout=60 --output-document=%s %s" %(wget, outputFile, weburl))
1844
log.error("Failed to connect to HPLIP site. Error code = %d" %status)
1850
sys.stdout.write("Download in progress...")
1851
urllib.urlretrieve(weburl, outputFile, downLoad_status)
1854
log.error("I/O Error: %s" % e.strerror)
1857
if not os.path.exists(outputFile):
1858
log.error("Failed to get hplip version/ %s file not found."%hplip_version_file)
1861
return True, outputFile
1868
def __init__(self, filename):
1869
self.Lock_filename = filename
1870
self.handler = open(self.Lock_filename, 'w')
1872
# Wait for another process to release resource and acquires the resource.
1874
fcntl.flock(self.handler, fcntl.LOCK_EX)
1877
fcntl.flock(self.handler, fcntl.LOCK_UN)
1880
self.handler.close()