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 which('gnomesu'):
1647 su_sudo_str = 'gnomesu -c "%s"'
1650 su_sudo_str = 'gksu "%s"'
1657 # This function returns the distro name and distro version.
1658 #This is provided to check on Fedora 14 in pkit.py file for Plugin-installation.
1659 #is_su variable is used to provide a check on Fedora 8
1664 if which('lsb_release'):
1665 name = os.popen('lsb_release -i | cut -f 2')
1666 os_name = name.read().strip()
1668 version = os.popen('lsb_release -r | cut -f 2')
1669 os_version=version.read().strip()
1673 name = os.popen('cat /etc/issue | cut -c 1-6 | head -n 1')
1674 os_name = name.read().strip()
1676 version1=os.popen('cat /etc/issue | cut -c 16 | head -n 1')
1677 version2=version1.read().strip()
1679 if (version2 == '1'):
1680 version=os.popen('cat /etc/issue | cut -c 16-17 | head -n 1')
1683 version=os.popen('cat /etc/issue | cut -c 16 | head -n 1')
1685 os_version=version.read().strip()
1688 return os_name,os_version,is_su
1692 # Removes HTML or XML character references and entities from a text string.
1698 if text[:2] == "&#":
1699 # character reference
1701 if text[:3] == "&#x":
1702 #return unichr(int(text[3:-1], 16))
1703 return chr(int(text[3:-1], 16))
1705 #return unichr(int(text[2:-1]))
1706 return chr(int(text[2:-1]))
1712 #text = unichr(htmlentitydefs.name2codepoint[text[1:-1]])
1713 text = chr(htmlentitydefs.name2codepoint[text[1:-1]])
1716 return text # leave as is
1717 return re.sub("&#?\w+;", fixup, text)
1720 # Adds HTML or XML character references and entities from a text string
1723 if not isinstance(s, unicode):
1724 s = unicode(s) # hmmm...
1726 s = s.replace(u"&", u"&")
1728 for c in htmlentitydefs.codepoint2name:
1729 if c != 0x26: # exclude &
1730 s = s.replace(unichr(c), u"&%s;" % htmlentitydefs.codepoint2name[c])
1732 for c in range(0x20) + range(0x7f, 0xa0):
1733 s = s.replace(unichr(c), u"&#%d;" % c)
1737 # checks if given process is running.
1740 # None - if process is not running
1741 # grep output - if process is running
1743 def Is_Process_Running(process_name):
1745 p1 = Popen(["ps", "aux"], stdout=PIPE)
1746 p2 = Popen(["grep", process_name], stdin=p1.stdout, stdout=PIPE)
1747 p3 = Popen(["grep", "-v", "grep"], stdin=p2.stdout, stdout=PIPE)
1748 output = p3.communicate()[0]
1749 log.debug("Is_Process_Running outpu = %s " %output)
1751 if process_name in output:
1756 except Exception, e:
1757 log.error("Execution failed: process Name[%s]" %process_name)
1758 print >>sys.stderr, "Execution failed:", e
1761 #return tye: strings
1763 # None --> on error.
1764 # "terminal name"-->success
1766 terminal_list=['gnome-terminal', 'konsole','x-terminal-emulator', 'xterm', 'gtkterm']
1769 while cnt < len(terminal_list):
1770 if which(terminal_list[cnt]):
1771 terminal_cmd = terminal_list[cnt]+" -e "
1772 log.debug("Available Terminal = %s " %terminal_cmd)
1779 # True --> if it is older version
1780 # False --> if it is same or later version.
1782 def Is_HPLIP_older_version(installed_version, available_version):
1784 if available_version == "" or available_version == None or installed_version == "" or installed_version == None:
1785 log.debug("available_version is ''")
1788 installed_array=installed_version.split('.')
1789 available_array=available_version.split('.')
1791 log.debug("HPLIP Installed_version=%s Available_version=%s"%(installed_version,available_version))
1794 while cnt <len(installed_array) and cnt <len(available_array):
1795 if(int(installed_array[cnt]) < int(available_array[cnt])):
1798 elif(int(installed_array[cnt]) > int(available_array[cnt])):
1799 log.debug("Already new verison is installed")
1803 # To check internal version is installed.
1804 if Is_older is False and len(installed_array) >len(available_array):
1810 def downLoad_status(count, blockSize, totalSize):
1811 percent = int(count*blockSize*100/totalSize)
1813 sys.stdout.write("\b\b\b")
1814 sys.stdout.write("%s" %(log.color("%2d%%"%percent, 'bold')))
1818 def download_from_network(weburl, outputFile = None, useURLLIB=False):
1821 if weburl is "" or weburl is None:
1822 log.error("URL is empty")
1825 if outputFile is None:
1826 fp, outputFile = make_temp_file()
1829 if useURLLIB is False:
1830 wget = which("wget")
1832 wget = os.path.join(wget, "wget")
1833 status, output = run("%s --cache=off --timeout=60 --output-document=%s %s" %(wget, outputFile, weburl))
1835 log.error("Failed to connect to HPLIP site. Error code = %d" %status)
1841 sys.stdout.write("Download in progress...")
1842 urllib.urlretrieve(weburl, outputFile, downLoad_status)
1845 log.error("I/O Error: %s" % e.strerror)
1848 if not os.path.exists(outputFile):
1849 log.error("Failed to get hplip version/ %s file not found."%hplip_version_file)
1852 return True, outputFile
1859 def __init__(self, filename):
1860 self.Lock_filename = filename
1861 self.handler = open(self.Lock_filename, 'w')
1863 # Wait for another process to release resource and acquires the resource.
1865 fcntl.flock(self.handler, fcntl.LOCK_EX)
1868 fcntl.flock(self.handler, fcntl.LOCK_UN)
1871 self.handler.close()