home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2007 September / PCWSEP07.iso / Software / Linux / Linux Mint 3.0 Light / LinuxMint-3.0-Light.iso / casper / filesystem.squashfs / usr / lib / hplip / base / device.py < prev    next >
Encoding:
Python Source  |  2007-04-04  |  73.6 KB  |  2,080 lines

  1. # -*- coding: utf-8 -*-
  2. #
  3. # (c) Copyright 2003-2007 Hewlett-Packard Development Company, L.P.
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  18. #
  19. # Author: Don Welch
  20. #
  21.  
  22. # Std Lib
  23. import socket
  24. import re
  25. import gzip
  26. import os.path
  27. import time
  28. import urllib
  29. import StringIO
  30. import httplib
  31. import gettext
  32.  
  33. # Local
  34. from g import *
  35. from codes import *
  36. from msg import *
  37. import utils, status, pml, service
  38. from prnt import pcl, ldl, cups
  39. import models, mdns, slp
  40. from strings import string_table
  41.  
  42. DEFAULT_PROBE_BUS = 'usb,par,cups'
  43. VALID_BUSES = ('par', 'net', 'cups', 'usb') #, 'bt', 'fw')
  44. VALID_BUSES_WO_CUPS = ('par', 'net', 'usb')
  45. DEFAULT_FILTER = 'none'
  46. VALID_FILTERS = ('none', 'print', 'scan', 'fax', 'pcard', 'copy')
  47.  
  48. pat_deviceuri = re.compile(r"""(.*):/(.*?)/(\S*?)\?(?:serial=(\S*)|device=(\S*)|ip=(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}[^&]*))(?:&port=(\d))?""", re.IGNORECASE)
  49. http_pat_url = re.compile(r"""/(.*?)/(\S*?)\?(?:serial=(\S*)|device=(\S*))&loc=(\S*)""", re.IGNORECASE)
  50. direct_pat = re.compile(r'direct (.*?) "(.*?)" "(.*?)" "(.*?)"', re.IGNORECASE)
  51.  
  52. # Pattern to check for ; at end of CTR fields
  53. # Note: If ; not present, CTR value is invalid
  54. pat_dynamic_ctr = re.compile(r"""CTR:\d*\s.*;""", re.IGNORECASE)
  55.  
  56. MAX_BUFFER = 8192
  57.  
  58. # Cache for model data
  59. model_dat = models.ModelData()
  60.  
  61. def makeURI(hpiod_sock, param, port=1):  
  62.     ip_pat = re.compile(r"""\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b""", re.IGNORECASE)
  63.     dev_pat = re.compile(r"""/dev/.+""", re.IGNORECASE)
  64.     usb_pat = re.compile(r"""(\d+):(\d+)""", re.IGNORECASE)
  65.  
  66.     cups_uri, sane_uri, fax_uri = '', '', ''
  67.     found = False
  68.  
  69.     # see if the param represents a hostname
  70.     try:
  71.         param = socket.gethostbyname(param)
  72.     except socket.gaierror:
  73.         log.debug("Gethostbyname() failed.")
  74.         pass
  75.  
  76.     if dev_pat.search(param) is not None: # parallel
  77.         log.debug("Trying parallel")
  78.         try:
  79.             fields, data, result_code = \
  80.                 xmitMessage(hpiod_sock, "MakeURI", None, 
  81.                                 {'device-file' : param, 'bus' : 'par',})
  82.         except Error:
  83.             result_code = ERROR_INTERNAL
  84.  
  85.         if result_code == ERROR_SUCCESS:
  86.             cups_uri = fields.get( 'device-uri', '' )
  87.             found = True
  88.  
  89.  
  90.     elif usb_pat.search(param) is not None: # USB
  91.         log.debug("Trying USB")
  92.         match_obj = usb_pat.search(param)
  93.         usb_bus_id = match_obj.group(1)
  94.         usb_dev_id = match_obj.group(2)
  95.  
  96.         try:
  97.             fields, data, result_code = \
  98.                 xmitMessage(hpiod_sock, "MakeURI", None, 
  99.                                 {'usb-bus' : usb_bus_id, 
  100.                                  'usb-dev': usb_dev_id, 
  101.                                  'bus' : 'usb',})
  102.         except Error:
  103.             result_code = ERROR_INTERNAL
  104.  
  105.         if result_code == ERROR_SUCCESS:
  106.             cups_uri = fields.get( 'device-uri', '' )
  107.             found = True
  108.  
  109.     elif ip_pat.search(param) is not None: # IPv4 dotted quad
  110.         log.debug("Trying IP address")
  111.         try:
  112.             fields, data, result_code = \
  113.                 xmitMessage(hpiod_sock, "MakeURI", None, 
  114.                                 {'hostname' : param, 
  115.                                  'port': port, 
  116.                                  'bus' : 'net',})
  117.         except Error:
  118.             result_code = ERROR_INTERNAL
  119.  
  120.         if result_code == ERROR_SUCCESS:
  121.             cups_uri = fields.get( 'device-uri', '' )
  122.             found = True
  123.  
  124.     else: # serial
  125.         log.debug("Trying serial number")
  126.         devices = probeDevices(None, bus="usb,par")
  127.  
  128.         for d in devices:
  129.             log.debug(d)
  130.  
  131.             # usb has serial in URI...
  132.             back_end, is_hp, bus, model, serial, dev_file, host, port = \
  133.                 parseDeviceURI(d)
  134.  
  135.             if bus == 'par': # ...parallel does not. Must get Device ID to obtain it...
  136.                 mq = queryModelByURI(d)
  137.  
  138.                 fields, data, result_code = \
  139.                     xmitMessage(hpiod_sock, "DeviceOpen", None,
  140.                                     {'device-uri':    d,
  141.                                       'io-mode' :     mq.get('io-mode', '0'),
  142.                                       'io-mfp-mode' : mq.get('io-mfp-mode', '2'),
  143.                                       'io-control' :  mq.get('io-control', '0'),
  144.                                       'io-scan-port': mq.get('io-scan-port', '0'),
  145.                                     })
  146.  
  147.                 if result_code == ERROR_SUCCESS:
  148.                     device_id = fields['device-id']
  149.  
  150.                     fields, data, result_code = \
  151.                         xmitMessage(hpiod_sock, 'DeviceID', None, {'device-id' : device_id,})
  152.  
  153.                     serial = parseDeviceID(data).get('SN', '')
  154.  
  155.                 fields, data, result_code = \
  156.                     xmitMessage(hpiod_sock, "DeviceClose", None, {'device-id': device_id,})
  157.  
  158.             if serial.lower() == param.lower():
  159.                 found = True
  160.                 cups_uri = d
  161.                 break
  162.  
  163.     if found:
  164.         mq = queryModelByURI(cups_uri)
  165.  
  166.         if mq.get('scan-type', 0):
  167.             sane_uri = cups_uri.replace("hp:", "hpaio:")
  168.  
  169.         if mq.get('fax-type', 0):
  170.             fax_uri = cups_uri.replace("hp:", "hpfax:")
  171.  
  172.     else:
  173.         scan_uri, fax_uri = '', ''
  174.         
  175.     if cups_uri:
  176.         user_cfg.last_used.device_uri = cups_uri
  177.  
  178.     return cups_uri, sane_uri, fax_uri
  179.  
  180.  
  181. def queryModelByModel(model):
  182.     model = normalizeModelName(model).lower()
  183.     return model_dat[model]
  184.  
  185. def queryModelByURI(device_uri):
  186.     try:
  187.         back_end, is_hp, bus, model, \
  188.             serial, dev_file, host, port = \
  189.             parseDeviceURI(device_uri)
  190.     except Error:
  191.         raise ERROR_INVALID_DEVICE_URI
  192.     else:
  193.         return queryModelByModel(model)
  194.  
  195.  
  196. def getInteractiveDeviceURI(bus='cups,usb,par', filter='none', back_end_filter=('hp',)):
  197.     probed_devices = probeDevices(None, bus.lower(), filter=filter)
  198.     cups_printers = cups.getPrinters()
  199.     log.debug(probed_devices)
  200.     log.debug(cups_printers)
  201.     max_deviceid_size, x, devices = 0, 0, {}
  202.  
  203.     for d in probed_devices:
  204.         printers = []
  205.  
  206.         back_end, is_hp, bus, model, serial, dev_file, host, port = \
  207.             parseDeviceURI(d)
  208.  
  209.         if back_end in back_end_filter:
  210.             for p in cups_printers:
  211.                 if p.device_uri == d:
  212.                     printers.append(p.name)
  213.  
  214.             devices[x] = (d, printers)
  215.             x += 1
  216.             max_deviceid_size = max(len(d), max_deviceid_size)
  217.  
  218.     if x == 0:
  219.         log.error("No devices found.")
  220.         raise Error(ERROR_NO_PROBED_DEVICES_FOUND)
  221.  
  222.     elif x == 1:
  223.         log.info(utils.bold("Using device: %s" % devices[0][0]))
  224.         #user_cfg.last_used.device_uri = devices[0][0]
  225.         return devices[0][0]
  226.  
  227.     else:
  228.         last_used_device_uri = user_cfg.last_used.device_uri
  229.         last_used_index = None
  230.         
  231.         rows, cols = utils.ttysize()
  232.         if cols > 100: cols = 100
  233.  
  234.         log.info(utils.bold("\nChoose device from probed devices connected on bus(es): %s:\n" % bus))
  235.         formatter = utils.TextFormatter(
  236.                 (
  237.                     {'width': 4},
  238.                     {'width': max_deviceid_size, 'margin': 2},
  239.                     {'width': cols-max_deviceid_size-8, 'margin': 2},
  240.                 )
  241.             )
  242.         log.info(formatter.compose(("Num.", "Device-URI", "CUPS printer(s)")))
  243.         log.info(formatter.compose(('-'*4, '-'*(max_deviceid_size), '-'*(cols-max_deviceid_size-10))))
  244.  
  245.         for y in range(x):
  246.             log.info(formatter.compose((str(y), devices[y][0], ', '.join(devices[y][1]))))
  247.             
  248.             if last_used_device_uri == devices[y][0]:
  249.                 last_used_index = y
  250.  
  251.         while 1:
  252.             if last_used_index is not None:
  253.                 user_input = raw_input(utils.bold("\nEnter number 0...%d for device (q=quit, enter=last used device: %s) ?" % ((x-1), last_used_device_uri))).strip()
  254.             
  255.             else:
  256.                 user_input = raw_input(utils.bold("\nEnter number 0...%d for device (q=quit) ?" % (x-1))).strip()
  257.  
  258.             if last_used_index is not None and not user_input:
  259.                 user_input = str(last_used_index)
  260.             else:
  261.                 if not user_input:
  262.                     log.warn("Invalid input - enter a numeric value or 'q' to quit.")
  263.                     continue
  264.  
  265.             if user_input.lower() == 'q':
  266.                 return
  267.  
  268.             try:
  269.                 i = int(user_input)
  270.             except ValueError:
  271.                 log.warn("Invalid input - enter a numeric value or 'q' to quit.")
  272.                 continue
  273.  
  274.             if i < 0 or i > (x-1):
  275.                 log.warn("Invalid input - enter a value between 0 and %d or 'q' to quit." % (x-1))
  276.                 continue
  277.  
  278.             break
  279.  
  280.         #user_cfg.last_used.device_uri = devices[i][0]
  281.  
  282.         return devices[i][0]
  283.  
  284.  
  285. def probeDevices(hpiod_sock=None, bus='cups,usb,par', timeout=10,
  286.                  ttl=4, filter='none',  search='', net_search='slp', 
  287.                  back_end_filter=('hp',)):
  288.  
  289.     close_sock, num_devices, ret_devices = False, 0, {}
  290.  
  291.     if search:
  292.         try:
  293.             search_pat = re.compile(search, re.IGNORECASE)
  294.         except:
  295.             log.error("Invalid search pattern. Search uses standard regular expressions. For more info, see: http://www.amk.ca/python/howto/regex/")
  296.             search = ''
  297.  
  298.     buses = bus
  299.     for bus in buses.split(','):
  300.         bus = bus.lower().strip()
  301.  
  302.         if bus not in VALID_BUSES:
  303.             log.error("Invalid bus: %s" % bus)
  304.             continue
  305.  
  306.         if bus == 'net':
  307.             if net_search == 'slp':
  308.                 try:
  309.                     detected_devices = slp.detectNetworkDevices(ttl, timeout)
  310.                 except Error:
  311.                     log.error("An error occured during network probe.")
  312.                     raise ERROR_INTERNAL
  313.             else:
  314.                 try:
  315.                     detected_devices = mdns.detectNetworkDevices(ttl, timeout)
  316.                 except Error:
  317.                     log.error("An error occured during network probe.")
  318.                     raise ERROR_INTERNAL
  319.  
  320.             for ip in detected_devices:
  321.                 update_spinner()
  322.                 hn = detected_devices[ip].get('hn', '?UNKNOWN?')
  323.                 num_devices_on_jd = detected_devices[ip].get('num_devices', 0)
  324.                 num_ports_on_jd = detected_devices[ip].get('num_ports', 1)
  325.  
  326.                 if num_devices_on_jd > 0:
  327.                     for port in range(num_ports_on_jd):
  328.                         dev = detected_devices[ip].get('device%d' % (port+1), '0')
  329.  
  330.                         if dev is not None and dev != '0':
  331.                             device_id = parseDeviceID(dev)
  332.                             model = normalizeModelName(device_id.get('MDL', '?UNKNOWN?'))
  333.  
  334.                             if num_ports_on_jd == 1:
  335.                                 device_uri = 'hp:/net/%s?ip=%s' % (model, ip)
  336.                             else:
  337.                                 device_uri = 'hp:/net/%s?ip=%s&port=%d' % (model, ip, (port+1))
  338.  
  339.                             include = True
  340.                             mq = queryModelByModel(model)
  341.  
  342.                             if not mq:
  343.                                 log.debug("Not found.")
  344.                                 include = False
  345.  
  346.                             elif int(mq.get('support-type', SUPPORT_TYPE_NONE)) == SUPPORT_TYPE_NONE:
  347.                                 log.debug("Not supported.")
  348.                                 include = False
  349.  
  350.                             elif filter not in ('none', 'print'):
  351.                                 for f in filter.split(','):
  352.                                     filter_type = int(mq.get('%s-type' % f.lower().strip(), 0))
  353.  
  354.                                     if filter_type == 0:
  355.                                         log.debug("%s filtered out." % device_uri)
  356.                                         include = False
  357.                                         break
  358.  
  359.                             if include:
  360.                                 ret_devices[device_uri] = (model, model, hn)
  361.  
  362.         elif bus in ('usb', 'par'):
  363.  
  364.             if hpiod_sock is None:
  365.                 close_sock = True
  366.                 hpiod_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  367.  
  368.                 try:
  369.                     prop.hpiod_port = int(file(os.path.join(prop.run_dir, 'hpiod.port'), 'r').read())
  370.                 except:
  371.                     pass
  372.  
  373.                 try:
  374.                     hpiod_sock.connect((prop.hpiod_host, prop.hpiod_port))
  375.                 except socket.error:
  376.                     raise Error(ERROR_UNABLE_TO_CONTACT_SERVICE)
  377.  
  378.             fields, data, result_code = \
  379.                 xmitMessage(hpiod_sock, "ProbeDevices", None, {'bus' : bus,})
  380.  
  381.             if result_code == ERROR_SUCCESS:
  382.                 for x in data.splitlines():
  383.                     m = direct_pat.match(x)
  384.  
  385.                     uri = m.group(1) or ''
  386.                     mdl = m.group(2) or ''
  387.                     desc = m.group(3) or ''
  388.                     devid = m.group(4) or ''
  389.  
  390.                     log.debug(uri)
  391.  
  392.                     try:
  393.                         back_end, is_hp, bus, model, serial, dev_file, host, port = \
  394.                             parseDeviceURI(uri)
  395.                     except Error:
  396.                         continue
  397.  
  398.                     include = True
  399.  
  400.                     if mdl and uri and is_hp:
  401.                         mq = queryModelByModel(model)
  402.  
  403.                         if not mq:
  404.                             log.debug("Not found.")
  405.                             include = False
  406.  
  407.                         elif int(mq.get('support-type', SUPPORT_TYPE_NONE)) == SUPPORT_TYPE_NONE:
  408.                             log.debug("Not supported.")
  409.                             include = False
  410.  
  411.                         elif filter not in ('none', 'print'):
  412.  
  413.                             for f in filter.split(','):
  414.                                 filter_type = int(mq.get('%s-type' % f.lower().strip(), 0))
  415.  
  416.                                 if filter_type == 0:
  417.                                     log.debug("%s filtered out." % uri)
  418.                                     include = False
  419.                                     break
  420.  
  421.                         if include:
  422.                             ret_devices[uri] = (mdl, desc, devid) # model w/ _'s, mdl w/o
  423.  
  424.         elif bus == 'cups':
  425.             cups_printers = cups.getPrinters()
  426.             x = len(cups_printers)
  427.  
  428.             for p in cups_printers:
  429.                 device_uri = p.device_uri
  430.                 log.debug("%s: %s" % (device_uri, p.name))
  431.  
  432.                 if device_uri != '':
  433.                     try:
  434.                         back_end, is_hp, bs, model, serial, dev_file, host, port = \
  435.                             parseDeviceURI(device_uri)
  436.                     except Error:
  437.                         log.warning("Inrecognized URI: %s" % device_uri)
  438.                         continue
  439.  
  440.                     if not is_hp:
  441.                         continue
  442.  
  443.                     include = True
  444.                     mq = queryModelByModel(model)
  445.  
  446.                     if not mq:
  447.                         include = False
  448.                         log.debug("Not found.")
  449.  
  450.                     elif int(mq.get('support-type', SUPPORT_TYPE_NONE)) == SUPPORT_TYPE_NONE:
  451.                         log.debug("Not supported.")
  452.                         include = False
  453.  
  454.                     elif filter not in ('none', 'print'):
  455.                         for f in filter.split(','):
  456.                             filter_type = int(mq.get('%s-type' % f.lower().strip(), 0))
  457.  
  458.                             if filter_type == 0:
  459.                                 log.debug("%s filtered out." % device_uri)
  460.                                 include = False
  461.                                 break
  462.  
  463.                     if include:
  464.                         ret_devices[device_uri] = (model, model, '')
  465.  
  466.     probed_devices = {}
  467.     for uri in ret_devices:
  468.         num_devices += 1
  469.         mdl, model, devid_or_hn = ret_devices[uri]
  470.  
  471.         include = True
  472.         if search:
  473.             match_obj = search_pat.search("%s %s %s %s" % (mdl, model, devid_or_hn, uri))
  474.  
  475.             if match_obj is None:
  476.                 log.debug("%s %s %s %s: Does not match search '%s'." % (mdl, model, devid_or_hn, uri, search))
  477.                 include = False
  478.  
  479.         if include:
  480.             probed_devices[uri] = ret_devices[uri]
  481.  
  482.  
  483.     if close_sock and hpiod_sock is not None:
  484.         hpiod_sock.close()
  485.  
  486.     return probed_devices
  487.  
  488.  
  489. def getSupportedCUPSDevices(back_end_filter=['hp']):
  490.     devices = {}
  491.     printers = cups.getPrinters()
  492.  
  493.     for p in printers:
  494.         try:
  495.             back_end, is_hp, bus, model, serial, dev_file, host, port = \
  496.                 parseDeviceURI(p.device_uri)
  497.  
  498.         except Error:
  499.             continue
  500.  
  501.         if back_end in back_end_filter:
  502.             try:
  503.                 devices[p.device_uri]
  504.             except KeyError:
  505.                 devices[p.device_uri] = [p.name]
  506.             else:
  507.                 devices[p.device_uri].append(p.name)
  508.  
  509.     return devices # { 'device_uri' : [ CUPS printer list ], ... }
  510.  
  511.  
  512. def parseDeviceID(device_id):
  513.     d= {}
  514.     x = [y.strip() for y in device_id.strip().split(';') if y]
  515.  
  516.     for z in x:
  517.         y = z.split(':')
  518.         try:
  519.             d.setdefault(y[0].strip(), y[1])
  520.         except IndexError:
  521.             d.setdefault(y[0].strip(), None)
  522.  
  523.     d.setdefault('MDL', '')
  524.     d.setdefault('SN',  '')
  525.  
  526.     if 'MODEL' in d:
  527.         d['MDL'] = d['MODEL']
  528.         del d['MODEL']
  529.  
  530.     if 'SERIAL' in d:
  531.         d['SN'] = d['SERIAL']
  532.         del d['SERIAL']
  533.  
  534.     elif 'SERN' in d:
  535.         d['SN'] = d['SERN']
  536.         del d['SERN']
  537.  
  538.     if d['SN'].startswith('X'):
  539.         d['SN'] = ''
  540.  
  541.     return d
  542.  
  543.  
  544. def parseDynamicCounter(ctr_field, convert_to_int=True):
  545.     counter, value = ctr_field.split(' ')
  546.     try:
  547.         counter = int(utils.xlstrip(str(counter), '0') or '0')
  548.  
  549.         if convert_to_int:
  550.             value = int(utils.xlstrip(str(value), '0') or '0')
  551.     except ValueError:
  552.         if convert_to_int:
  553.             counter, value = 0, 0
  554.         else:
  555.             counter, value = 0, ''
  556.  
  557.     return counter, value
  558.  
  559.  
  560. def parseDeviceURI(device_uri):
  561.     m = pat_deviceuri.match(device_uri)
  562.  
  563.     if m is None:
  564.         raise Error(ERROR_INVALID_DEVICE_URI)
  565.  
  566.     back_end = m.group(1).lower() or ''
  567.     is_hp = (back_end in ('hp', 'hpfax', 'hpaio'))
  568.     bus = m.group(2).lower() or ''
  569.  
  570.     if bus not in ('usb', 'net', 'bt', 'fw', 'par'):
  571.         raise Error(ERROR_INVALID_DEVICE_URI)
  572.  
  573.     model = m.group(3) or ''
  574.     serial = m.group(4) or ''
  575.     dev_file = m.group(5) or ''
  576.     host = m.group(6) or ''
  577.     port = m.group(7) or 1
  578.  
  579.     if bus == 'net':
  580.         try:
  581.             port = int(port)
  582.         except (ValueError, TypeError):
  583.             port = 1
  584.  
  585.         if port == 0:
  586.             port = 1
  587.  
  588.     return back_end, is_hp, bus, model, serial, dev_file, host, port
  589.  
  590.  
  591. def validateBusList(bus, allow_cups=True):
  592.     for x in bus.split(','):
  593.         bb = x.lower().strip()
  594.         if allow_cups:
  595.             vb = VALID_BUSES
  596.         else:
  597.             vb = VALID_BUSES_WO_CUPS
  598.         if bb not in vb:
  599.             log.error("Invalid bus name: %s" % bb)
  600.             return False
  601.  
  602.     return True
  603.  
  604. def validateFilterList(filter):
  605.     for f in filter.split(','):
  606.         if f not in VALID_FILTERS:
  607.             log.error("Invalid term '%s' in filter list" % f)
  608.             return False
  609.  
  610.     return True
  611.  
  612.  
  613. inter_pat = re.compile(r"""%(.*)%""", re.IGNORECASE)
  614.  
  615. strings_init = False
  616.  
  617. def initStrings():
  618.     global strings_init
  619.     strings_init = True
  620.  
  621.     gettext.install('hplip')
  622.     cycles = 0
  623.  
  624.     while True:
  625.  
  626.         found = False
  627.  
  628.         for s in string_table:
  629.             short_string, long_string = string_table[s]
  630.             short_replace, long_replace = short_string, long_string
  631.  
  632.             try:
  633.                 short_match = inter_pat.match(short_string).group(1)
  634.             except (AttributeError, TypeError):
  635.                 short_match = None
  636.  
  637.             if short_match is not None:
  638.                 found = True
  639.  
  640.                 try:
  641.                     short_replace, dummy = string_table[short_match]
  642.                 except KeyError:
  643.                     log.error("String interpolation error: %s" % short_match)
  644.  
  645.             try:
  646.                 long_match = inter_pat.match(long_string).group(1)
  647.             except (AttributeError, TypeError):
  648.                 long_match = None
  649.  
  650.             if long_match is not None:
  651.                 found = True
  652.  
  653.                 try:
  654.                     dummy, long_replace = string_table[long_match]
  655.                 except KeyError:
  656.                     log.error("String interpolation error: %s" % long_match)
  657.  
  658.             if found:
  659.                 string_table[s] = (short_replace, long_replace)
  660.  
  661.         if not found:
  662.             break
  663.         else:
  664.             cycles +=1
  665.             if cycles > 1000:
  666.                 break    
  667.  
  668.  
  669. def queryString(string_id, typ=0):
  670.     if not strings_init:
  671.         initStrings()
  672.  
  673.     s = string_table.get(str(string_id), ('', ''))[typ]
  674.  
  675.     if type(s) == type(''):
  676.         return s
  677.  
  678.     return s()
  679.  
  680.  
  681. AGENT_types = { AGENT_TYPE_NONE        : 'invalid',
  682.                 AGENT_TYPE_BLACK       : 'black',
  683.                 AGENT_TYPE_CMY         : 'cmy',
  684.                 AGENT_TYPE_KCM         : 'kcm',
  685.                 AGENT_TYPE_CYAN        : 'cyan',
  686.                 AGENT_TYPE_MAGENTA     : 'magenta',
  687.                 AGENT_TYPE_YELLOW      : 'yellow',
  688.                 AGENT_TYPE_CYAN_LOW    : 'photo_cyan',
  689.                 AGENT_TYPE_MAGENTA_LOW : 'photo_magenta',
  690.                 AGENT_TYPE_YELLOW_LOW  : 'photo_yellow',
  691.                 AGENT_TYPE_GGK         : 'photo_gray',
  692.                 AGENT_TYPE_BLUE        : 'photo_blue',
  693.                 AGENT_TYPE_KCMY_CM     : 'kcmy_cm',
  694.                 AGENT_TYPE_LC_LM       : 'photo_cyan_and_photo_magenta',
  695.                 #AGENT_TYPE_Y_M         : 'yellow_and_magenta',
  696.                 #AGENT_TYPE_C_K         : 'cyan_and_black',
  697.                 AGENT_TYPE_LG_PK       : 'light_gray_and_photo_black',
  698.                 AGENT_TYPE_LG          : 'light_gray',
  699.                 AGENT_TYPE_G           : 'medium_gray',
  700.                 AGENT_TYPE_PG          : 'photo_gray',
  701.                 AGENT_TYPE_C_M         : 'cyan_and_magenta',
  702.                 AGENT_TYPE_K_Y         : 'black_and_yellow',
  703.                 AGENT_TYPE_UNSPECIFIED : 'unspecified', # Kind=5,6
  704.             }
  705.  
  706. AGENT_kinds = {AGENT_KIND_NONE            : 'invalid',
  707.                 AGENT_KIND_HEAD            : 'head',
  708.                 AGENT_KIND_SUPPLY          : 'supply',
  709.                 AGENT_KIND_HEAD_AND_SUPPLY : 'cartridge',
  710.                 AGENT_KIND_TONER_CARTRIDGE : 'toner',
  711.                 AGENT_KIND_MAINT_KIT       : 'maint_kit', # fuser
  712.                 AGENT_KIND_ADF_KIT         : 'adf_kit',
  713.                 AGENT_KIND_DRUM_KIT        : 'drum_kit',
  714.                 AGENT_KIND_TRANSFER_KIT    : 'transfer_kit',
  715.                 AGENT_KIND_INT_BATTERY     : 'battery',
  716.                 AGENT_KIND_UNKNOWN         : 'unknown',
  717.               }
  718.  
  719. AGENT_healths = {AGENT_HEALTH_OK           : 'ok',
  720.                   AGENT_HEALTH_MISINSTALLED : 'misinstalled', # supply/cart
  721.                   AGENT_HEALTH_FAIR_MODERATE : '',
  722.                   AGENT_HEALTH_INCORRECT    : 'incorrect',
  723.                   AGENT_HEALTH_FAILED       : 'failed',
  724.                   AGENT_HEALTH_OVERTEMP     : 'overtemp', # battery
  725.                   AGENT_HEALTH_CHARGING     : 'charging', # battery
  726.                   AGENT_HEALTH_DISCHARGING  : 'discharging', # battery
  727.                 }
  728.  
  729.  
  730. AGENT_levels = {AGENT_LEVEL_TRIGGER_MAY_BE_LOW : 'low',
  731.                  AGENT_LEVEL_TRIGGER_PROBABLY_OUT : 'low',
  732.                  AGENT_LEVEL_TRIGGER_ALMOST_DEFINITELY_OUT : 'out',
  733.                }
  734.  
  735.  
  736. MODEL_UI_REPLACEMENTS = {'laserjet'   : 'LaserJet',
  737.                           'psc'        : 'PSC',
  738.                           'officejet'  : 'Officejet',
  739.                           'deskjet'    : 'Deskjet',
  740.                           'hp'         : 'HP',
  741.                           'business'   : 'Business',
  742.                           'inkjet'     : 'Inkjet',
  743.                           'photosmart' : 'Photosmart',
  744.                           'color'      : 'Color',
  745.                           'series'     : 'series',
  746.                           'printer'    : 'Printer',
  747.                           'mfp'        : 'MFP',
  748.                           'mopier'     : 'Mopier',
  749.                           'pro'        : 'Pro',
  750.                         }
  751.  
  752.  
  753. def normalizeModelUIName(model):
  754.     if not model.lower().startswith('hp'):
  755.         z = 'HP ' + model.replace('_', ' ')
  756.     else:
  757.         z = model.replace('_', ' ')
  758.  
  759.     y = []
  760.     for x in z.split():
  761.         xx = x.lower()
  762.         y.append(MODEL_UI_REPLACEMENTS.get(xx, xx))
  763.  
  764.     return ' '.join(y)
  765.  
  766. def normalizeModelName(model):
  767.     return utils.xstrip(model.replace(' ', '_').replace('__', '_').replace('~','').replace('/', '_'), '_')
  768.  
  769.  
  770. def isLocal(bus):
  771.     return bus in ('par', 'usb', 'fw', 'bt')
  772.  
  773.  
  774. # **************************************************************************** #
  775.  
  776. string_cache = {}
  777.  
  778. class Device(object):
  779.     def __init__(self, device_uri, printer_name=None,
  780.                 hpssd_sock=None, hpiod_sock=None,
  781.                 callback=None):
  782.  
  783.         printers = cups.getPrinters()
  784.         
  785.         if device_uri is None:
  786.             for p in printers:
  787.                 if p.name.lower() == printer_name.lower():
  788.                     device_uri = p.device_uri
  789.                     break
  790.             else:
  791.                 raise Error(ERROR_DEVICE_NOT_FOUND)
  792.  
  793.  
  794.         self.device_uri = device_uri
  795.         self.callback = callback
  796.         self.close_socket = False
  797.         self.query_device_thread = None
  798.  
  799.         try:
  800.             self.back_end, self.is_hp, self.bus, self.model, \
  801.                 self.serial, self.dev_file, self.host, self.port = \
  802.                 parseDeviceURI(self.device_uri)
  803.         except Error:
  804.             self.io_state = IO_STATE_NON_HP
  805.             raise Error(ERROR_INVALID_DEVICE_URI)
  806.  
  807.         log.debug("URI: backend=%s, is_hp=%s, bus=%s, model=%s, serial=%s, dev=%s, host=%s, port=%d" % \
  808.             (self.back_end, self.is_hp, self.bus, self.model, self.serial, self.dev_file, self.host, self.port))
  809.  
  810.         self.model_ui = normalizeModelUIName(self.model)
  811.         self.model = normalizeModelName(self.model)
  812.  
  813.         log.debug("Model/UI model: %s/%s" % (self.model, self.model_ui))
  814.  
  815.         if hpiod_sock is None:
  816.             self.hpiod_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  817.             try:
  818.                 self.hpiod_sock.connect((prop.hpiod_host, prop.hpiod_port))
  819.                 self.close_hpiod_socket = True
  820.             except socket.error:
  821.                 raise Error(ERROR_UNABLE_TO_CONTACT_SERVICE)
  822.         else:
  823.             self.hpiod_sock = hpiod_sock
  824.  
  825.         log.debug("hpiod socket: %d" % self.hpiod_sock.fileno())
  826.  
  827.         if hpssd_sock is None:
  828.             self.hpssd_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  829.             try:
  830.                 self.hpssd_sock.connect((prop.hpssd_host, prop.hpssd_port))
  831.                 self.close_hpssd_socket = True
  832.             except socket.error:
  833.                 raise Error(ERROR_UNABLE_TO_CONTACT_SERVICE)
  834.         else:
  835.             self.hpssd_sock = hpssd_sock
  836.  
  837.         log.debug("hpssd socket: %d" % self.hpssd_sock.fileno())
  838.  
  839.         service.setAlertsEx(self.hpssd_sock)
  840.  
  841.         self.mq = {} # Model query
  842.         self.dq = {} # Device query
  843.         self.cups_printers = []
  844.         self.channels = {} # { 'SERVICENAME' : channel_id, ... }
  845.         self.device_id = -1
  846.         self.r_values = None # ( r_value, r_value_str, rg, rr )
  847.         self.deviceID = ''
  848.         self.panel_check = True
  849.         self.io_state = IO_STATE_HP_READY
  850.         self.is_local = isLocal(self.bus)
  851.  
  852.         self.supported = False
  853.  
  854.         self.queryModel()
  855.         if not self.supported:
  856.             log.error("Unsupported model: %s" % self.model)
  857.             self.sendEvent(STATUS_DEVICE_UNSUPPORTED)
  858.         else:
  859.             self.supported = True
  860.  
  861.  
  862.         self.mq.update({'model'    : self.model,
  863.                         'model-ui' : self.model_ui})
  864.  
  865.         self.error_state = ERROR_STATE_ERROR
  866.         self.device_state = DEVICE_STATE_NOT_FOUND
  867.         self.status_code = EVENT_ERROR_DEVICE_NOT_FOUND
  868.  
  869.         #printers = cups.getPrinters()
  870.         for p in printers:
  871.             if self.device_uri == p.device_uri:
  872.                 self.cups_printers.append(p.name)
  873.                 self.state = p.state # ?
  874.  
  875.                 if self.io_state == IO_STATE_NON_HP:
  876.                     self.model = p.makemodel.split(',')[0]
  877.  
  878.         try:
  879.             self.first_cups_printer = self.cups_printers[0]
  880.         except IndexError:
  881.             self.first_cups_printer = ''
  882.  
  883.         if self.mq.get('fax-type', FAX_TYPE_NONE) != FAX_TYPE_NONE:
  884.             self.dq.update({ 'fax-uri' : self.device_uri.replace('hp:/', 'hpfax:/').replace('hpaio:/', 'hpfax:/')})
  885.  
  886.         if self.mq.get('scan-type', SCAN_TYPE_NONE) != SCAN_TYPE_NONE:
  887.             self.dq.update({ 'scan-uri' : self.device_uri.replace('hp:/', 'hpaio:/').replace('hpfax:/', 'hpaio:/')})
  888.  
  889.         self.dq.update({
  890.             'back-end'         : self.back_end,
  891.             'is-hp'            : self.is_hp,
  892.             'serial'           : self.serial,
  893.             'dev-file'         : self.dev_file,
  894.             'host'             : self.host,
  895.             'port'             : self.port,
  896.             'cups-printer'     : ','.join(self.cups_printers),
  897.             'status-code'      : self.status_code,
  898.             'status-desc'      : '',
  899.             'deviceid'         : '',
  900.             'panel'            : 0,
  901.             'panel-line1'      : '',
  902.             'panel-line2'      : '',
  903.             '3bit-status-code' : 0,
  904.             '3bit-status-name' : 'IOTrap',
  905.             'device-state'     : self.device_state,
  906.             'error-state'      : self.error_state,
  907.             'device-uri'       : self.device_uri,
  908.             'cups-uri'         : self.device_uri.replace('hpfax:/', 'hp:/').replace('hpaio:/', 'hp:/'),
  909.             })
  910.  
  911.         self.device_vars = {
  912.             'URI'        : self.device_uri,
  913.             'DEVICE_URI' : self.device_uri,
  914.             'SCAN_URI'   : self.device_uri.replace('hp:', 'hpaio:'),
  915.             'SANE_URI'   : self.device_uri.replace('hp:', 'hpaio:'),
  916.             'FAX_URI'    : self.device_uri.replace('hp:', 'hpfax:'),
  917.             'PRINTER'    : self.first_cups_printer,
  918.             'HOME'       : prop.home_dir,
  919.                            }            
  920.  
  921.  
  922.     def xmitHpiodMessage(self, msg_type, other_fields={},
  923.                          payload=None, timeout=prop.read_timeout):
  924.  
  925.         return xmitMessage(self.hpiod_sock, msg_type,
  926.                                 payload, other_fields, timeout)
  927.  
  928.     def xmitHpssdMessage(self, msg_type, other_fields={},
  929.                          payload=None, timeout=prop.read_timeout):
  930.  
  931.         return xmitMessage(self.hpssd_sock, msg_type,
  932.                                 payload, other_fields, timeout)
  933.  
  934.     def quit(self):
  935.         if self.close_hpiod_socket:
  936.             self.hpiod_sock.close()                
  937.  
  938.         if self.close_hpssd_socket:
  939.             self.hpssd_sock.close()                
  940.  
  941.     def queryModel(self):
  942.         if not self.mq:
  943.             self.mq = queryModelByURI(self.device_uri)
  944.  
  945.         self.supported = bool(self.mq)
  946.  
  947.         if self.supported:
  948.             for m in self.mq:
  949.                 self.__dict__[m.replace('-','_')] = self.mq[m]
  950.  
  951.     def queryString(self, string_id):
  952.         return queryString(string_id)
  953.  
  954.     def open(self, network_timeout=3):
  955.         if self.supported and self.io_state in (IO_STATE_HP_READY, IO_STATE_HP_NOT_AVAIL):
  956.             log.debug("Opening device: %s" % self.device_uri)
  957.             prev_device_state = self.device_state
  958.             self.io_state = IO_STATE_HP_NOT_AVAIL
  959.             self.device_state = DEVICE_STATE_NOT_FOUND
  960.             self.error_state = ERROR_STATE_ERROR
  961.             self.status_code = EVENT_ERROR_DEVICE_NOT_FOUND
  962.             self.device_id = -1
  963.  
  964.             fields, data, result_code = \
  965.                 self.xmitHpiodMessage("DeviceOpen",
  966.                                         {'device-uri':   self.device_uri,
  967.                                           'io-mode' :     self.mq.get('io-mode', '0'),
  968.                                           'io-mfp-mode' : self.mq.get('io-mfp-mode', '2'),
  969.                                           'io-control' :  self.mq.get('io-control', '0'),
  970.                                           'io-scan-port': self.mq.get('io-scan-port', '0'),
  971.                                         }
  972.                                       )
  973.  
  974.             if result_code != ERROR_SUCCESS:
  975.                 self.sendEvent(EVENT_ERROR_DEVICE_NOT_FOUND, typ='error')
  976.                 log.error("Unable to communicate with device: %s" % self.device_uri)
  977.                 raise Error(ERROR_DEVICE_NOT_FOUND)
  978.             else:
  979.                 self.device_id = fields['device-id']
  980.                 log.debug("device-id=%d" % self.device_id)
  981.                 self.io_state = IO_STATE_HP_OPEN
  982.                 self.error_state = ERROR_STATE_CLEAR
  983.                 log.debug("Opened device: %s (backend=%s,is_hp=%s,bus=%s,model=%s,dev=%s,serial=%s,host=%s,port=%d)" %
  984.                     (self.back_end, self.device_uri, self.is_hp, self.bus, self.model, self.dev_file, self.serial, self.host, self.port))
  985.  
  986.                 if prev_device_state == DEVICE_STATE_NOT_FOUND:
  987.                     self.device_state = DEVICE_STATE_JUST_FOUND
  988.                 else:
  989.                     self.device_state = DEVICE_STATE_FOUND
  990.  
  991.                 self.getDeviceID()
  992.                 self.getSerialNumber()
  993.                 return self.device_id
  994.  
  995.  
  996.  
  997.     def close(self):
  998.         if self.io_state == IO_STATE_HP_OPEN:
  999.             log.debug("Closing device...")
  1000.  
  1001.             if len(self.channels) > 0:
  1002.  
  1003.                 for c in self.channels.keys():
  1004.                     self.__closeChannel(c)
  1005.  
  1006.             fields, data, result_code = \
  1007.                 self.xmitHpiodMessage("DeviceClose", {'device-id': self.device_id,})
  1008.  
  1009.             self.channels.clear()
  1010.             self.io_state = IO_STATE_HP_READY
  1011.  
  1012.  
  1013.     def __openChannel(self, service_name):
  1014.         self.open()
  1015.  
  1016.         #if not self.mq['io-mode'] == IO_MODE_UNI:
  1017.         if 1:
  1018.             service_name = service_name.upper()
  1019.  
  1020.             if service_name not in self.channels:
  1021.                 log.debug("Opening %s channel..." % service_name)
  1022.  
  1023.                 fields, data, result_code = \
  1024.                     self.xmitHpiodMessage("ChannelOpen",
  1025.                                           {'device-id':  self.device_id,
  1026.                                             'service-name' : service_name,
  1027.                                           }
  1028.                                         )
  1029.                 try:
  1030.                     channel_id = fields['channel-id']
  1031.                 except KeyError:
  1032.                     raise Error(ERROR_INTERNAL)
  1033.  
  1034.                 self.channels[service_name] = channel_id
  1035.                 log.debug("channel-id=%d" % channel_id)
  1036.                 return channel_id
  1037.             else:
  1038.                 return self.channels[service_name]
  1039.         else:
  1040.             return -1
  1041.  
  1042.  
  1043.     def openChannel(self, service_name):
  1044.         return self.__openChannel(service_name)
  1045.  
  1046.     def openPrint(self):
  1047.         return self.__openChannel('PRINT')
  1048.  
  1049.     def openFax(self):
  1050.         return self.__openChannel('HP-FAX-SEND')
  1051.  
  1052.     def openPCard(self):
  1053.         return self.__openChannel('HP-CARD-ACCESS')
  1054.  
  1055.     def openFax(self):
  1056.         return self.__openChannel('HP-FAX-SEND')
  1057.  
  1058.     def openEWS(self):
  1059.         return self.__openChannel('HP-EWS')
  1060.  
  1061.     def closePrint(self):
  1062.         return self.__closeChannel('PRINT')
  1063.  
  1064.     def closePCard(self):
  1065.         return self.__closeChannel('HP-CARD-ACCESS')
  1066.  
  1067.     def closeFax(self):
  1068.         return self.__closeChannel('HP-FAX')
  1069.  
  1070.     def openPML(self):
  1071.         return self.__openChannel('HP-MESSAGE')
  1072.  
  1073.     def closePML(self):
  1074.         return self.__closeChannel('HP-MESSAGE')
  1075.  
  1076.     def closeEWS(self):
  1077.         return self.__closeChannel('HP-EWS')
  1078.  
  1079.     def openCfgUpload(self):
  1080.         return self.__openChannel('HP-CONFIGURATION-UPLOAD')
  1081.  
  1082.     def closeCfgUpload(self):
  1083.         return self.__closeChannel('HP-CONFIGURATION-UPLOAD')
  1084.  
  1085.     def openCfgDownload(self):
  1086.         return self.__openChannel('HP-CONFIGURATION-DOWNLOAD')
  1087.  
  1088.     def closeCfgDownload(self):
  1089.         return self.__closeChannel('HP-CONFIGURATION-DOWNLOAD')
  1090.  
  1091.  
  1092.     def __closeChannel(self, service_name):
  1093.         if not self.mq['io-mode'] == IO_MODE_UNI and \
  1094.             self.io_state == IO_STATE_HP_OPEN:
  1095.  
  1096.             service_name = service_name.upper()
  1097.  
  1098.             if service_name in self.channels:
  1099.                 log.debug("Closing %s channel..." % service_name)
  1100.  
  1101.                 fields, data, result_code = \
  1102.                     self.xmitHpiodMessage('ChannelClose',
  1103.                                           {
  1104.                                             'device-id': self.device_id,
  1105.                                             'channel-id' : self.channels[service_name],
  1106.                                           }
  1107.                                         )
  1108.  
  1109.                 del self.channels[service_name]
  1110.  
  1111.  
  1112.     def closeChannel(self, service_name):
  1113.         return self.__closeChannel(service_name)
  1114.  
  1115.  
  1116.     def getDeviceID(self):
  1117.         fields, data, result_code = \
  1118.             self.xmitHpiodMessage('DeviceID', {'device-id' : self.device_id,})
  1119.  
  1120.         if result_code != ERROR_SUCCESS:
  1121.             self.raw_deviceID = ''
  1122.             self.deviceID = {}
  1123.         else:
  1124.             self.raw_deviceID = data
  1125.             self.deviceID = parseDeviceID(data)
  1126.  
  1127.         return self.deviceID
  1128.  
  1129.  
  1130.     def getSerialNumber(self):
  1131.         if self.serial:
  1132.             return
  1133.  
  1134.         try:
  1135.             self.serial = self.deviceID['SN']
  1136.         except KeyError:
  1137.             pass
  1138.         else:
  1139.             if self.serial:
  1140.                 return
  1141.  
  1142.         if self.mq.get('status-type', STATUS_TYPE_NONE) != STATUS_TYPE_NONE and \
  1143.             not self.mq.get('io-mode', IO_MODE_UNI) == IO_MODE_UNI:
  1144.  
  1145.             try:
  1146.                 try:
  1147.                     error_code, self.serial = self.getPML(pml.OID_SERIAL_NUMBER)
  1148.                 except Error:
  1149.                     self.serial = ''
  1150.             finally:
  1151.                 self.closePML()
  1152.  
  1153.         if self.serial is None:
  1154.             self.serial = ''
  1155.  
  1156.  
  1157.     def getThreeBitStatus(self):
  1158.         fields, data, result_code = \
  1159.             self.xmitHpiodMessage('DeviceStatus', {'device-id' : self.device_id,})
  1160.  
  1161.         if result_code != ERROR_SUCCESS:
  1162.             self.three_bit_status_code = 0
  1163.             self.three_bit_status_name = 'IOTrap'
  1164.         else:
  1165.             self.three_bit_status_code = fields['status-code']
  1166.             self.three_bit_status_name = fields['status-name']
  1167.  
  1168.  
  1169.     def getStatusFromDeviceID(self):
  1170.         self.getDeviceID()
  1171.         return status.parseStatus(parseDeviceID(self.raw_deviceID))
  1172.  
  1173.  
  1174.     def __parseRValues(self, r_value):
  1175.         r_value_str = str(r_value)
  1176.         r_value_str = ''.join(['0'*(9 - len(r_value_str)), r_value_str])
  1177.         rg, rr = r_value_str[:3], r_value_str[3:]
  1178.         r_value = int(rr)
  1179.         self.r_values = r_value, r_value_str, rg, rr
  1180.         return r_value, r_value_str, rg, rr
  1181.  
  1182.  
  1183.     def getRValues(self, r_type, status_type, dynamic_counters):
  1184.         r_value, r_value_str, rg, rr = 0, '000000000', '000', '000000'
  1185.  
  1186.         if r_type > 0 and \
  1187.             dynamic_counters != STATUS_DYNAMIC_COUNTERS_NONE:
  1188.  
  1189.             if self.r_values is None:
  1190.                 fields, data, result_code = \
  1191.                     self.xmitHpssdMessage('GetValue', {'device-uri': self.device_uri, 'key': 'r_value'})
  1192.  
  1193.                 if result_code == ERROR_SUCCESS and data:
  1194.                     try:
  1195.                         r_value = int(data.strip())
  1196.                     except:
  1197.                         pass
  1198.                     else:
  1199.                         log.debug("r_value=%d" % r_value)
  1200.                         r_value, r_value_str, rg, rr = self.__parseRValues(r_value)
  1201.  
  1202.                         return r_value, r_value_str, rg, rr
  1203.  
  1204.             if self.r_values is None:   
  1205.  
  1206.                 if status_type ==  STATUS_TYPE_S and \
  1207.                     self.is_local and \
  1208.                     dynamic_counters != STATUS_DYNAMIC_COUNTERS_PML_SNMP:
  1209.  
  1210.                     try:    
  1211.                         try:
  1212.                             r_value = self.getDynamicCounter(140)
  1213.  
  1214.                             if r_value is not None:
  1215.                                 log.debug("r_value=%d" % r_value)
  1216.                                 r_value, r_value_str, rg, rr = self.__parseRValues(r_value)
  1217.  
  1218.                                 fields, data, result_code = \
  1219.                                     self.xmitHpssdMessage('SetValue', {'device-uri': self.device_uri, 'key': 'r_value', 'value': r_value})
  1220.  
  1221.                             else:
  1222.                                 log.error("Error attempting to read r-value (2).")
  1223.                                 r_value = 0
  1224.                         except Error:
  1225.                             log.error("Error attempting to read r-value (1).")
  1226.                             r_value = 0
  1227.                     finally:
  1228.                         self.closePrint()
  1229.  
  1230.  
  1231.                 elif (status_type ==  STATUS_TYPE_S and 
  1232.                       dynamic_counters == STATUS_DYNAMIC_COUNTERS_PCL and 
  1233.                       not self.is_local) or \
  1234.                       dynamic_counters == STATUS_DYNAMIC_COUNTERS_PML_SNMP:
  1235.  
  1236.                     try:
  1237.                         result_code, r_value = self.getPML(pml.OID_R_SETTING)
  1238.  
  1239.                         if r_value is not None:
  1240.                             log.debug("r_value=%d" % r_value)
  1241.                             r_value, r_value_str, rg, rr = self.__parseRValues(r_value)
  1242.  
  1243.                             fields, data, result_code = \
  1244.                                 self.xmitHpssdMessage('SetValue', {'device-uri': self.device_uri, 'key': 'r_value', 'value': r_value})
  1245.                         else:
  1246.                             r_value = 0
  1247.  
  1248.                     finally:
  1249.                         self.closePML()
  1250.  
  1251.             else:
  1252.                 r_value, r_value_str, rg, rr = self.r_values
  1253.  
  1254.         return r_value, r_value_str, rg, rr
  1255.  
  1256.  
  1257.     def queryDevice(self, quick=False, no_fwd=False, reread_cups_printers=False):
  1258.         if not self.supported:
  1259.             self.dq = {}
  1260.             return
  1261.  
  1262.         r_type = self.mq.get('r-type', 0)
  1263.         tech_type = self.mq.get('tech-type', TECH_TYPE_NONE)
  1264.         status_type = self.mq.get('status-type', STATUS_TYPE_NONE)
  1265.         battery_check = self.mq.get('status-battery-check', STATUS_BATTERY_CHECK_NONE)
  1266.         dynamic_counters = self.mq.get('status-dynamic-counters', STATUS_DYNAMIC_COUNTERS_NONE)
  1267.         io_mode = self.mq.get('io-mode', IO_MODE_UNI)
  1268.  
  1269.         # Turn off status if local connection and bi-di not avail.
  1270.         if io_mode  == IO_MODE_UNI and self.back_end != 'net':
  1271.             status_type = STATUS_TYPE_NONE
  1272.  
  1273.         agents = []
  1274.  
  1275.         if self.device_state != DEVICE_STATE_NOT_FOUND:
  1276.             try:
  1277.                 self.getThreeBitStatus()
  1278.             except Error, e:
  1279.                 log.error("Error getting 3-bit status.")
  1280.                 raise Error(ERROR_DEVICE_IO_ERROR)
  1281.  
  1282.             if self.tech_type in (TECH_TYPE_MONO_INK, TECH_TYPE_COLOR_INK):
  1283.                 try:
  1284.                     self.getDeviceID()
  1285.                 except Error, e:
  1286.                     log.error("Error getting device ID.")
  1287.                     raise Error(ERROR_DEVICE_IO_ERROR)
  1288.  
  1289.             status_desc = self.queryString(self.status_code)
  1290.  
  1291.             self.dq.update({
  1292.                 'serial'           : self.serial,
  1293.                 'cups-printer'     : ','.join(self.cups_printers),
  1294.                 'status-code'      : self.status_code,
  1295.                 'status-desc'      : status_desc,
  1296.                 'deviceid'         : self.raw_deviceID,
  1297.                 'panel'            : 0,
  1298.                 'panel-line1'      : '',
  1299.                 'panel-line2'      : '',
  1300.                 '3bit-status-code' : self.three_bit_status_code,
  1301.                 '3bit-status-name' : self.three_bit_status_name,
  1302.                 'device-state'     : self.device_state,
  1303.                 'error-state'      : self.error_state,
  1304.                 })
  1305.  
  1306.             status_block = {}
  1307.  
  1308.             if status_type == STATUS_TYPE_NONE:
  1309.                 log.warn("No status available for device.")
  1310.                 status_block = {'status-code' : STATUS_UNKNOWN}
  1311.  
  1312.             elif status_type in (STATUS_TYPE_VSTATUS, STATUS_TYPE_S):
  1313.                 log.debug("Type 1/2 (S: or VSTATUS:) status")
  1314.                 status_block = status.parseStatus(self.deviceID)
  1315.  
  1316.             elif status_type == STATUS_TYPE_LJ:
  1317.                 log.debug("Type 3 LaserJet status")
  1318.                 status_block = status.StatusType3(self, self.deviceID)
  1319.  
  1320.             elif status_type == STATUS_TYPE_LJ_XML:
  1321.                 log.debug("Type 6: LJ XML")
  1322.                 status_block = status.StatusType6(self)
  1323.  
  1324.             else:
  1325.                 log.error("Unimplemented status type: %d" % status_type)
  1326.  
  1327.             if battery_check:
  1328.                 log.debug("Battery check...")
  1329.                 status.BatteryCheck(self, status_block)
  1330.  
  1331.             if status_block:
  1332.                 log.debug(status_block)
  1333.                 self.dq.update(status_block)
  1334.                 try:
  1335.                     status_block['agents']
  1336.                 except KeyError:
  1337.                     pass
  1338.                 else:
  1339.                     agents = status_block['agents']
  1340.                     del self.dq['agents']
  1341.  
  1342.  
  1343.             status_code = self.dq.get('status-code', STATUS_UNKNOWN)
  1344.  
  1345.             if not quick and \
  1346.                 self.mq.get('fax-type', FAX_TYPE_NONE) and \
  1347.                 status_code == STATUS_PRINTER_IDLE:
  1348.  
  1349.                 log.debug("Fax activity check...")
  1350.  
  1351.                 tx_active, rx_active = status.getFaxStatus(self)
  1352.  
  1353.                 if tx_active:
  1354.                     status_code = STATUS_FAX_TX_ACTIVE
  1355.                 elif rx_active:
  1356.                     status_code = STATUS_FAX_RX_ACTIVE
  1357.  
  1358.  
  1359.             typ = 'event'
  1360.             self.error_state = STATUS_TO_ERROR_STATE_MAP.get(status_code, ERROR_STATE_CLEAR)
  1361.             if self.error_state == ERROR_STATE_ERROR:
  1362.                 typ = 'error'
  1363.  
  1364.             self.sendEvent(status_code, typ=typ, no_fwd=no_fwd)
  1365.  
  1366.             try:
  1367.                 self.dq.update({'status-desc' : self.queryString(status_code),
  1368.                                 'error-state' : self.error_state,
  1369.                                 })
  1370.  
  1371.             except (KeyError, Error):
  1372.                 self.dq.update({'status-desc' : '',
  1373.                                 'error-state' : ERROR_STATE_CLEAR,
  1374.                                 })
  1375.  
  1376.             r_value = 0
  1377.  
  1378.             if not quick and status_type != STATUS_TYPE_NONE:
  1379.                 if self.panel_check:
  1380.                     self.panel_check = bool(self.mq.get('panel-check-type', 0))
  1381.  
  1382.                 if self.panel_check and status_type in (STATUS_TYPE_LJ, STATUS_TYPE_S, STATUS_TYPE_VSTATUS):
  1383.                     log.debug("Panel check...")
  1384.                     try:
  1385.                         self.panel_check, line1, line2 = status.PanelCheck(self)
  1386.                     finally:
  1387.                         self.closePML()
  1388.  
  1389.                     self.dq.update({'panel': int(self.panel_check),
  1390.                                       'panel-line1': line1,
  1391.                                       'panel-line2': line2,})
  1392.  
  1393.  
  1394.                 if dynamic_counters != STATUS_DYNAMIC_COUNTERS_NONE:
  1395.                     r_value, r_value_str, rg, rr = self.getRValues(r_type, status_type, dynamic_counters)
  1396.                 else:
  1397.                     r_value, r_value_str, rg, rr = 0, '000000000', '000', '000000'
  1398.  
  1399.                 self.dq.update({'r'  : r_value,
  1400.                                 'rs' : r_value_str,
  1401.                                 'rg' : rg,
  1402.                                 'rr' : rr,
  1403.                               })
  1404.  
  1405.             if not quick and reread_cups_printers:
  1406.                 self.cups_printers = []
  1407.                 log.debug("Re-reading CUPS printer queue information.")
  1408.                 printers = cups.getPrinters()
  1409.                 for p in printers:
  1410.                     if self.device_uri == p.device_uri:
  1411.                         self.cups_printers.append(p.name)
  1412.                         self.state = p.state # ?
  1413.  
  1414.                         if self.io_state == IO_STATE_NON_HP:
  1415.                             self.model = p.makemodel.split(',')[0]
  1416.  
  1417.                 self.dq.update({'cups-printer' : ','.join(self.cups_printers)})
  1418.  
  1419.                 try:
  1420.                     self.first_cups_printer = self.cups_printers[0]
  1421.                 except IndexError:
  1422.                     self.first_cups_printer = ''
  1423.  
  1424.             if not quick:
  1425.                 # Make sure there is some valid agent data for this r_value
  1426.                 # If not, fall back to r_value == 0
  1427.                 if r_value > 0 and self.mq.get('r%d-agent1-kind' % r_value, 0) == 0:
  1428.                     r_value = 0
  1429.                     self.dq.update({'r'  : r_value,
  1430.                                     'rs' : r_value_str,
  1431.                                     'rg' : rg,
  1432.                                     'rr' : rr,
  1433.                                   })
  1434.  
  1435.                 a = 1
  1436.                 while True:
  1437.                     mq_agent_kind = self.mq.get('r%d-agent%d-kind' % (r_value, a), 0)
  1438.  
  1439.                     if mq_agent_kind == 0:
  1440.                         break
  1441.  
  1442.                     mq_agent_type = self.mq.get('r%d-agent%d-type' % (r_value, a), 0)
  1443.                     mq_agent_sku = self.mq.get('r%d-agent%d-sku' % (r_value, a), '')
  1444.  
  1445.                     found = False
  1446.  
  1447.                     for agent in agents:
  1448.                         agent_kind = agent['kind']
  1449.                         agent_type = agent['type']
  1450.  
  1451.                         if agent_kind == mq_agent_kind and \
  1452.                            agent_type == mq_agent_type:
  1453.                            found = True
  1454.                            break
  1455.  
  1456.                     if found:
  1457.                          log.debug("r%d-kind%d-type%d" % (r_value, agent_kind, agent_type))
  1458.                         log.debug("found")
  1459.                         agent_health = agent.get('health', AGENT_HEALTH_OK)
  1460.                         agent_level_trigger = agent.get('level-trigger',
  1461.                             AGENT_LEVEL_TRIGGER_SUFFICIENT_0)
  1462.  
  1463.                         query = 'agent_%s_%s' % (AGENT_types.get(agent_type, 'unknown'), 
  1464.                                                  AGENT_kinds.get(agent_kind, 'unknown'))
  1465.  
  1466.                         agent_desc = self.queryString(query)
  1467.                         query = 'agent_health_ok'
  1468.  
  1469.                         # If printer is not in an error state, and
  1470.                         # if agent health is OK, check for low supplies. If low, use
  1471.                         # the agent level trigger description for the agent description.
  1472.                         # Otherwise, report the agent health.
  1473.                         if status_code == STATUS_PRINTER_IDLE and \
  1474.                             (agent_health == AGENT_HEALTH_OK or 
  1475.                              (agent_health == AGENT_HEALTH_FAIR_MODERATE and agent_kind == AGENT_KIND_HEAD)) and \
  1476.                             agent_level_trigger >= AGENT_LEVEL_TRIGGER_MAY_BE_LOW:
  1477.  
  1478.                             # Low
  1479.                             query = 'agent_level_%s' % AGENT_levels.get(agent_level_trigger, 'unknown')
  1480.  
  1481.                             if tech_type in (TECH_TYPE_MONO_INK, TECH_TYPE_COLOR_INK):
  1482.                                 code = agent_type + STATUS_PRINTER_LOW_INK_BASE
  1483.                             else:
  1484.                                 code = agent_type + STATUS_PRINTER_LOW_TONER_BASE
  1485.  
  1486.                             self.dq['status-code'] = code
  1487.                             self.dq['status-desc'] = self.queryString(code)
  1488.  
  1489.                             self.dq['error-state'] = STATUS_TO_ERROR_STATE_MAP.get(code, ERROR_STATE_LOW_SUPPLIES)
  1490.                             self.sendEvent(code)
  1491.  
  1492.                             if agent_level_trigger in (AGENT_LEVEL_TRIGGER_PROBABLY_OUT, AGENT_LEVEL_TRIGGER_ALMOST_DEFINITELY_OUT):
  1493.                                 query = 'agent_level_out'
  1494.                             else:
  1495.                                 query = 'agent_level_low'
  1496.  
  1497.                         agent_health_desc = self.queryString(query)
  1498.  
  1499.                         self.dq.update(
  1500.                         {
  1501.                             'agent%d-kind' % a :          agent_kind,
  1502.                             'agent%d-type' % a :          agent_type,
  1503.                             'agent%d-known' % a :         agent.get('known', False),
  1504.                             'agent%d-sku' % a :           mq_agent_sku,
  1505.                             'agent%d-level' % a :         agent.get('level', 0),
  1506.                             'agent%d-level-trigger' % a : agent_level_trigger,
  1507.                             'agent%d-ack' % a :           agent.get('ack', False),
  1508.                             'agent%d-hp-ink' % a :        agent.get('hp-ink', False),
  1509.                             'agent%d-health' % a :        agent_health,
  1510.                             'agent%d-dvc' % a :           agent.get('dvc', 0),
  1511.                             'agent%d-virgin' % a :        agent.get('virgin', False),
  1512.                             'agent%d-desc' % a :          agent_desc,
  1513.                             'agent%d-id' % a :            agent.get('id', 0),
  1514.                             'agent%d-health-desc' % a :   agent_health_desc,
  1515.                         })
  1516.  
  1517.                     else:
  1518.                         agent_health = AGENT_HEALTH_MISINSTALLED
  1519.                         agent_level_trigger = AGENT_LEVEL_TRIGGER_ALMOST_DEFINITELY_OUT
  1520.  
  1521.                         query = 'agent_%s_%s' % (AGENT_types.get(mq_agent_type, 'unknown'),
  1522.                                                  AGENT_kinds.get(mq_agent_kind, 'unknown'))
  1523.  
  1524.                         agent_desc = self.queryString(query)
  1525.                         agent_health_desc = self.queryString("agent_health_misinstalled")
  1526.  
  1527.                         self.dq.update(
  1528.                         {
  1529.                             'agent%d-kind' % a :          mq_agent_kind,
  1530.                             'agent%d-type' % a :          mq_agent_type,
  1531.                             'agent%d-known' % a :         False,
  1532.                             'agent%d-sku' % a :           mq_agent_sku,
  1533.                             'agent%d-level' % a :         0,
  1534.                             'agent%d-level-trigger' % a : agent_level_trigger,
  1535.                             'agent%d-ack' % a :           False,
  1536.                             'agent%d-hp-ink' % a :        False,
  1537.                             'agent%d-health' % a :        agent_health,
  1538.                             'agent%d-dvc' % a :           0,
  1539.                             'agent%d-virgin' % a :        False,
  1540.                             'agent%d-desc' % a :          agent_desc,
  1541.                             'agent%d-id' % a :            0,
  1542.                             'agent%d-health-desc' % a :   agent_health_desc,
  1543.                         })
  1544.  
  1545.                     a += 1
  1546.  
  1547.                 else: # Create agent keys for not-found devices
  1548.  
  1549.                     r_value = 0
  1550.                     if r_type > 0 and self.r_values is not None:
  1551.                         r_value = self.r_values[0]
  1552.  
  1553.                     # Make sure there is some valid agent data for this r_value
  1554.                     # If not, fall back to r_value == 0
  1555.                     if r_value > 0 and self.mq.get('r%d-agent1-kind', 0) == 0:
  1556.                         r_value = 0
  1557.  
  1558.                     a = 1
  1559.                     while True:
  1560.                         mq_agent_kind = self.mq.get('r%d-agent%d-kind' % (r_value, a), 0)
  1561.  
  1562.                         if mq_agent_kind == 0:
  1563.                             break
  1564.  
  1565.                         mq_agent_type = self.mq.get('r%d-agent%d-type' % (r_value, a), 0)
  1566.                         mq_agent_sku = self.mq.get('r%d-agent%d-sku' % (r_value, a), '')
  1567.                         query = 'agent_%s_%s' % (AGENT_types.get(mq_agent_type, 'unknown'),
  1568.                                                  AGENT_kinds.get(mq_agent_kind, 'unknown'))
  1569.  
  1570.                         agent_desc = self.queryString(query)
  1571.  
  1572.                         self.dq.update(
  1573.                         {
  1574.                             'agent%d-kind' % a :          mq_agent_kind,
  1575.                             'agent%d-type' % a :          mq_agent_type,
  1576.                             'agent%d-known' % a :         False,
  1577.                             'agent%d-sku' % a :           mq_agent_sku,
  1578.                             'agent%d-level' % a :         0,
  1579.                             'agent%d-level-trigger' % a : AGENT_LEVEL_TRIGGER_ALMOST_DEFINITELY_OUT,
  1580.                             'agent%d-ack' % a :           False,
  1581.                             'agent%d-hp-ink' % a :        False,
  1582.                             'agent%d-health' % a :        AGENT_HEALTH_MISINSTALLED,
  1583.                             'agent%d-dvc' % a :           0,
  1584.                             'agent%d-virgin' % a :        False,
  1585.                             'agent%d-health-desc' % a :   self.queryString('agent_health_unknown'),
  1586.                             'agent%d-desc' % a :          agent_desc,
  1587.                             'agent%d-id' % a :            0,
  1588.                         })
  1589.  
  1590.                         a += 1
  1591.  
  1592.         for d in self.dq:
  1593.             self.__dict__[d.replace('-','_')] = self.dq[d]
  1594.  
  1595.         log.debug(self.dq)
  1596.  
  1597.  
  1598.     def isBusyOrInErrorState(self):
  1599.         self.queryDevice(quick=True)
  1600.         return self.error_state > ERROR_STATE_MAX_OK
  1601.  
  1602.     def isIdleAndNoError(self):
  1603.         self.queryDevice(quick=True)
  1604.         return self.error_state <= ERROR_STATE_MAX_OK
  1605.  
  1606.     def getPML(self, oid, desired_int_size=pml.INT_SIZE_INT): # oid => ( 'dotted oid value', pml type )
  1607.         channel_id = self.openPML()
  1608.  
  1609.         fields, data, result_code = \
  1610.             self.xmitHpiodMessage("GetPML",
  1611.                                   {
  1612.                                     'device-id' :  self.device_id,
  1613.                                     'channel-id' : channel_id,
  1614.                                     'oid' :        pml.PMLToSNMP(oid[0]),
  1615.                                     'type' :       oid[1],
  1616.                                    }
  1617.                                 )
  1618.  
  1619.         pml_result_code = fields.get('pml-result-code', pml.ERROR_OK)
  1620.  
  1621.         log.debug("Result code = 0x%x" % pml_result_code)
  1622.  
  1623.         if pml_result_code >= pml.ERROR_UNKNOWN_REQUEST:
  1624.             return pml_result_code, None
  1625.  
  1626.         return pml_result_code, pml.ConvertFromPMLDataFormat(data, oid[1], desired_int_size)
  1627.  
  1628.  
  1629.     def setPML(self, oid, value): # oid => ( 'dotted oid value', pml type )
  1630.         channel_id = self.openPML()
  1631.  
  1632.         value = pml.ConvertToPMLDataFormat(value, oid[1])
  1633.  
  1634.         fields, data, result_code = \
  1635.             self.xmitHpiodMessage("SetPML",
  1636.                                   {
  1637.                                     'device-id' :  self.device_id,
  1638.                                     'channel-id' : channel_id,
  1639.                                     'oid' :        pml.PMLToSNMP(oid[0]),
  1640.                                     'type' :      oid[1],
  1641.                                   },
  1642.                                  value,
  1643.                                 )
  1644.  
  1645.         pml_result_code = fields.get('pml-result-code', pml.ERROR_OK)
  1646.         log.debug("Result code = 0x%x" % pml_result_code)
  1647.         return pml_result_code
  1648.  
  1649.  
  1650.     def getDynamicCounter(self, counter, convert_to_int=True):
  1651.         dynamic_counters = self.mq.get('status-dynamic-counters', STATUS_DYNAMIC_COUNTERS_NONE)
  1652.         if dynamic_counters != STATUS_DYNAMIC_COUNTERS_NONE:
  1653.  
  1654.             if dynamic_counters == STATUS_DYNAMIC_COUNTERS_LIDIL_0_5_4:
  1655.                 self.printData(ldl.buildResetPacket(), direct=True) 
  1656.                 self.printData(ldl.buildDynamicCountersPacket(counter), direct=True)
  1657.             else:
  1658.                 self.printData(pcl.buildDynamicCounter(counter), direct=True)
  1659.  
  1660.             value, tries, times_seen, sleepy_time, max_tries = 0, 0, 0, 0.1, 5
  1661.             time.sleep(0.1)
  1662.  
  1663.             while True:
  1664.  
  1665.                 if self.callback:
  1666.                     self.callback()
  1667.  
  1668.                 sleepy_time += 0.1
  1669.                 tries += 1
  1670.  
  1671.                 time.sleep(sleepy_time)
  1672.  
  1673.                 self.getDeviceID()
  1674.  
  1675.                 if 'CTR' in self.deviceID and \
  1676.                     pat_dynamic_ctr.search(self.raw_deviceID) is not None:
  1677.                     dev_counter, value = parseDynamicCounter(self.deviceID['CTR'], convert_to_int)
  1678.  
  1679.                     if counter == dev_counter:
  1680.                         self.printData(pcl.buildDynamicCounter(0), direct=True)
  1681.                         # protect the value as a string during msg handling
  1682.                         if not convert_to_int:
  1683.                             value = '#' + value
  1684.                         return value
  1685.  
  1686.                 if tries > max_tries:
  1687.                     if dynamic_counters == STATUS_DYNAMIC_COUNTERS_LIDIL_0_5_4:
  1688.                         self.printData(ldl.buildResetPacket())
  1689.                         self.printData(ldl.buildDynamicCountersPacket(counter), direct=True)
  1690.                     else:
  1691.                         self.printData(pcl.buildDynamicCounter(0), direct=True)
  1692.  
  1693.                     return None
  1694.  
  1695.                 if dynamic_counters == STATUS_DYNAMIC_COUNTERS_LIDIL_0_5_4:
  1696.                     self.printData(ldl.buildResetPacket())
  1697.                     self.printData(ldl.buildDynamicCountersPacket(counter), direct=True)
  1698.                 else:
  1699.                     self.printData(pcl.buildDynamicCounter(counter), direct=True)
  1700.  
  1701.         else:
  1702.             raise Error(ERROR_DEVICE_DOES_NOT_SUPPORT_OPERATION)
  1703.  
  1704.  
  1705.     def readPrint(self, bytes_to_read, stream=None, timeout=prop.read_timeout):
  1706.         return self.__readChannel(self.openPrint, bytes_to_read, stream, timeout)
  1707.  
  1708.     def readPCard(self, bytes_to_read, stream=None, timeout=prop.read_timeout):
  1709.         return self.__readChannel(self.openPCard, bytes_to_read, stream, timeout)
  1710.  
  1711.     def readFax(self, bytes_to_read, stream=None, timeout=prop.read_timeout):
  1712.         return self.__readChannel(self.openFax, bytes_to_read, stream, timeout)
  1713.  
  1714.     def readCfgUpload(self, bytes_to_read, stream=None, timeout=prop.read_timeout):
  1715.         return self.__readChannel(self.openCfgUpload, bytes_to_read, stream, timeout)
  1716.  
  1717.     def readEWS(self, bytes_to_read, stream=None, timeout=prop.read_timeout):
  1718.         return self.__readChannel(self.openEWS, bytes_to_read, stream, timeout, True)
  1719.  
  1720.  
  1721.     def __readChannel(self, opener, bytes_to_read, stream=None, 
  1722.                       timeout=prop.read_timeout, allow_short_read=False):
  1723.  
  1724.         channel_id = opener()
  1725.  
  1726.         log.debug("Reading channel %d..." % channel_id)
  1727.  
  1728.         num_bytes = 0
  1729.  
  1730.         if stream is None:
  1731.             buffer = ''
  1732.  
  1733.         while True:
  1734.             fields, data, result_code = \
  1735.                 self.xmitHpiodMessage('ChannelDataIn',
  1736.                                         {'device-id': self.device_id,
  1737.                                           'channel-id' : channel_id,
  1738.                                           'bytes-to-read' : bytes_to_read,
  1739.                                           'timeout' : timeout,
  1740.                                         }, 
  1741.                                       )
  1742.  
  1743.             l = len(data)
  1744.  
  1745.             if result_code != ERROR_SUCCESS:
  1746.                 log.error("Channel read error")
  1747.                 raise Error(ERROR_DEVICE_IO_ERROR)
  1748.  
  1749.             if not l:
  1750.                 log.debug("End of data")
  1751.                 break
  1752.  
  1753.             if stream is None:
  1754.                 buffer = ''.join([buffer, data])
  1755.             else:
  1756.                 stream.write(data)
  1757.  
  1758.             num_bytes += l
  1759.  
  1760.             if self.callback is not None:
  1761.                 self.callback()
  1762.  
  1763.             if num_bytes == bytes_to_read or allow_short_read:
  1764.                 log.debug("Read complete")
  1765.                 break
  1766.  
  1767.         if stream is None:
  1768.             log.debug("Returned %d total bytes in buffer." % num_bytes)
  1769.             return buffer
  1770.         else:
  1771.             log.debug("Wrote %d total bytes to stream." % num_bytes)
  1772.             return num_bytes
  1773.  
  1774.  
  1775.     def writePrint(self, data):
  1776.         return self.__writeChannel(self.openPrint, data)
  1777.  
  1778.     def writePCard(self, data):
  1779.         return self.__writeChannel(self.openPCard, data)
  1780.  
  1781.     def writeFax(self, data):
  1782.         return self.__writeChannel(self.openFax, data)
  1783.  
  1784.     def writeEWS(self, data):
  1785.         return self.__writeChannel(self.openEWS, data)
  1786.  
  1787.     def writeCfgDownload(self, data):
  1788.         return self.__writeChannel(self.openCfgDownload, data)
  1789.  
  1790.     def __writeChannel(self, opener, data):
  1791.         channel_id = opener()
  1792.  
  1793.         log.debug("Writing channel %d..." % channel_id)
  1794.         buffer, bytes_out, total_bytes_to_write = data, 0, len(data)
  1795.  
  1796.         while len(buffer) > 0:
  1797.             fields, data, result_code =\
  1798.                 self.xmitHpiodMessage('ChannelDataOut',
  1799.                                         {
  1800.                                             'device-id': self.device_id,
  1801.                                             'channel-id' : channel_id,
  1802.                                         },
  1803.                                         buffer[:prop.max_message_len],
  1804.                                       )
  1805.  
  1806.             if result_code != ERROR_SUCCESS:
  1807.                 log.error("Channel write error")
  1808.                 raise Error(ERROR_DEVICE_IO_ERROR)
  1809.  
  1810.             buffer = buffer[prop.max_message_len:]
  1811.             bytes_out += fields['bytes-written']
  1812.  
  1813.             if self.callback is not None:
  1814.                 self.callback()
  1815.  
  1816.         if total_bytes_to_write != bytes_out:
  1817.             raise Error(ERROR_DEVICE_IO_ERROR)
  1818.  
  1819.         return bytes_out
  1820.  
  1821.  
  1822.     def writeEmbeddedPML(self, oid, value, style=1, direct=True):
  1823.         if style == 1:
  1824.             func = pcl.buildEmbeddedPML2
  1825.         else:
  1826.             func = pcl.buildEmbeddedPML
  1827.  
  1828.         data = func(pcl.buildPCLCmd('&', 'b', 'W',
  1829.                      pml.buildEmbeddedPMLSetPacket(oid[0],
  1830.                                                     value,
  1831.                                                     oid[1])))
  1832.  
  1833.         self.printData(data, direct=direct, raw=True)
  1834.  
  1835.  
  1836.     def printGzipFile(self, file_name, printer_name=None, direct=False, raw=True, remove=False):
  1837.         return self.printFile(file_name, printer_name, direct, raw, remove)
  1838.  
  1839.     def printParsedGzipPostscript(self, print_file, printer_name=None):
  1840.         # always: direct=False, raw=False, remove=True
  1841.         try:
  1842.             os.stat(print_file)
  1843.         except OSError:
  1844.             log.error("File not found: %s" % print_file)
  1845.             return
  1846.  
  1847.         temp_file_fd, temp_file_name = utils.make_temp_file()
  1848.         f = gzip.open(print_file, 'r')
  1849.  
  1850.         x = f.readline()
  1851.         while not x.startswith('%PY_BEGIN'):
  1852.             os.write(temp_file_fd, x)
  1853.             x = f.readline()
  1854.  
  1855.         sub_lines = []
  1856.         x = f.readline()
  1857.         while not x.startswith('%PY_END'):
  1858.             sub_lines.append(x)
  1859.             x = f.readline()
  1860.  
  1861.         SUBS = {'VERSION' : prop.version,
  1862.                  'MODEL'   : self.model_ui,
  1863.                  'URI'     : self.device_uri,
  1864.                  'BUS'     : self.bus,
  1865.                  'SERIAL'  : self.serial,
  1866.                  'IP'      : self.host,
  1867.                  'PORT'    : self.port,
  1868.                  'DEVNODE' : self.dev_file,
  1869.                  }
  1870.  
  1871.         if self.bus == 'net':
  1872.             SUBS['DEVNODE'] = 'n/a'
  1873.         else:
  1874.             SUBS['IP'] = 'n/a'
  1875.             SUBS['PORT'] = 'n/a'
  1876.  
  1877.         for s in sub_lines:
  1878.             os.write(temp_file_fd, s % SUBS)
  1879.  
  1880.         os.write(temp_file_fd, f.read())
  1881.         f.close()
  1882.         os.close(temp_file_fd)
  1883.  
  1884.         self.printFile(temp_file_name, printer_name, direct=False, raw=False, remove=True)
  1885.  
  1886.     def printFile(self, file_name, printer_name=None, direct=False, raw=True, remove=False):
  1887.         is_gzip = os.path.splitext(file_name)[-1].lower() == '.gz'
  1888.  
  1889.         if printer_name is None:
  1890.             try:
  1891.                 printer_name = self.cups_printers[0]
  1892.             except IndexError:
  1893.                 raise Error(ERROR_NO_CUPS_QUEUE_FOUND_FOR_DEVICE)
  1894.  
  1895.         log.debug("Printing file '%s' to queue '%s' (gzip=%s, direct=%s, raw=%s, remove=%s)" %
  1896.                    (file_name, printer_name, is_gzip, direct, raw, remove))
  1897.  
  1898.         if direct: # implies raw==True
  1899.             if is_gzip:
  1900.                 self.writePrint(gzip.open(file_name, 'r').read())
  1901.             else:
  1902.                 self.writePrint(file(file_name, 'r').read())
  1903.  
  1904.         else:
  1905.             if not utils.which('lpr'):
  1906.                 lp_opt = ''
  1907.  
  1908.                 if raw:
  1909.                     lp_opt = '-oraw'
  1910.  
  1911.                 if is_gzip:
  1912.                     c = 'gunzip -c %s | lp -c -d%s %s' % (file_name, printer_name, lp_opt)
  1913.                 else:
  1914.                     c = 'lp -c -d%s %s %s' % (printer_name, lp_opt, file_name)
  1915.  
  1916.                 log.debug(c)
  1917.                 exit_code = os.system(c)
  1918.  
  1919.                 if exit_code != 0:
  1920.                     log.error("Print command failed with exit code %d!" % exit_code)
  1921.  
  1922.                 if remove:
  1923.                     os.remove(file_name)
  1924.  
  1925.             else:
  1926.                 raw_str, rem_str = '', ''
  1927.                 if raw: raw_str = '-l'
  1928.                 if remove: rem_str = '-r'
  1929.  
  1930.                 if is_gzip:
  1931.                     c = 'gunzip -c %s | lpr %s %s -P%s' % (file_name, raw_str, rem_str, printer_name)
  1932.                 else:
  1933.                     c = 'lpr -P%s %s %s %s' % (printer_name, raw_str, rem_str, file_name)
  1934.  
  1935.                 log.debug(c)
  1936.                 exit_code = os.system(c)
  1937.  
  1938.                 if exit_code != 0:
  1939.                     log.error("Print command failed with exit code %d!" % exit_code)
  1940.  
  1941.  
  1942.     def printTestPage(self, printer_name=None):
  1943.         return self.printParsedGzipPostscript(os.path.join( prop.home_dir, 'data',
  1944.                                               'ps', 'testpage.ps.gz' ), printer_name)
  1945.  
  1946.  
  1947.     def printData(self, data, printer_name=None, direct=True, raw=True):
  1948.         if direct:
  1949.             self.writePrint(data)
  1950.         else:
  1951.             temp_file_fd, temp_file_name = utils.make_temp_file()
  1952.             os.write(temp_file_fd, data)
  1953.             os.close(temp_file_fd)
  1954.  
  1955.             self.printFile(temp_file_name, printer_name, False, raw, remove=True)
  1956.  
  1957.  
  1958.     def cancelJob(self, jobid):
  1959.         cups.cancelJob(jobid)
  1960.         self.sendEvent(STATUS_PRINTER_CANCELING, jobid)
  1961.  
  1962.     def sendEvent(self, event, jobid=0, typ='event', no_fwd=False): 
  1963.         sendEvent(self.hpssd_sock, 'Event', None,
  1964.                       {
  1965.                           'job-id'        : jobid,
  1966.                           'event-type'    : typ,
  1967.                           'event-code'    : event,
  1968.                           'username'      : prop.username,
  1969.                           'device-uri'    : self.device_uri,
  1970.                           'retry-timeout' : 0,
  1971.                           'no-fwd'        : no_fwd,
  1972.                       }
  1973.                      )
  1974.  
  1975.  
  1976.  
  1977.     def queryHistory(self):
  1978.         fields, data, result_code = \
  1979.             self.xmitHpssdMessage("QueryHistory", {'device-uri' : self.device_uri,})
  1980.  
  1981.         result = []
  1982.         lines = data.strip().splitlines()
  1983.         lines.reverse()
  1984.  
  1985.         for x in lines:
  1986.             yr, mt, dy, hr, mi, sec, wd, yd, dst, job, user, ec = x.strip().split(',', 13)
  1987.             ec = int(ec)
  1988.             ess = queryString(ec, 0)
  1989.             esl = queryString(ec, 1)
  1990.             result.append((int(yr), int(mt), int(dy), int(hr), int(mi), int(sec), int(wd),
  1991.                              int(yd), int(dst), int(job), user, ec, ess, esl))
  1992.  
  1993.     self.hist = result
  1994.         return result
  1995.  
  1996.  
  1997.     def getEWSUrl(self, url, stream):
  1998.         try:
  1999.             if self.is_local:
  2000.                 url2 = "%s&loc=%s" % (self.device_uri, url)
  2001.                 data = self
  2002.             else:
  2003.                 url2 = "http://%s%s" % (self.host, url)
  2004.                 data = None
  2005.  
  2006.             log.debug("Opening: %s" % url2)
  2007.             opener = LocalOpener({})
  2008.             try:
  2009.                 f = opener.open(url2, data)
  2010.             except Error:
  2011.                 log.error("Status read failed: %s" % url2)
  2012.                 stream.seek(0)
  2013.                 stream.truncate()
  2014.             else:
  2015.                 try:
  2016.                     stream.write(f.read())
  2017.                 finally:
  2018.                     f.close()
  2019.  
  2020.         finally:
  2021.             self.closeEWS()
  2022.  
  2023.  
  2024.     def downloadFirmware(self):
  2025.         ok = False
  2026.         filename = os.path.join(prop.data_dir, "firmware", self.model + '.fw.gz')
  2027.         log.debug(filename)
  2028.  
  2029.         if os.path.exists(filename):
  2030.             try:
  2031.                 log.debug("Downloading firmware file '%s'..." % filename)
  2032.                 self.openPrint()
  2033.                 bytes_written = self.writePrint(gzip.open(filename).read())
  2034.                 log.debug("%s bytes downloaded." % utils.commafy(bytes_written))
  2035.                 self.closePrint()
  2036.                 ok = True
  2037.             except:
  2038.                 log.error("An error occured.")
  2039.         else:
  2040.             log.error("Firmware file '%s' not found." % filename)
  2041.  
  2042.         return ok
  2043.  
  2044.  
  2045. # ********************************** Support classes/functions
  2046.  
  2047.  
  2048. class xStringIO(StringIO.StringIO):
  2049.     def makefile(self, x, y):
  2050.         return self
  2051.  
  2052. # URLs: hp:/usb/HP_LaserJet_3050?serial=00XXXXXXXXXX&loc=/hp/device/info_device_status.xml
  2053. class LocalOpener(urllib.URLopener):
  2054.     def open_hp(self, url, dev):
  2055.         log.debug("open_hp(%s)" % url)
  2056.  
  2057.         match_obj = http_pat_url.search(url)
  2058.         bus = match_obj.group(1) or ''
  2059.         model = match_obj.group(2) or ''
  2060.         serial = match_obj.group(3) or ''
  2061.         device = match_obj.group(4) or ''
  2062.         loc = match_obj.group(5) or ''
  2063.  
  2064.         dev.openEWS()
  2065.         dev.writeEWS("""GET %s HTTP/1.0\nContent-Length:0\nHost:localhost\nUser-Agent:hplip\n\n""" % loc)
  2066.  
  2067.         reply = xStringIO()
  2068.         dev.readEWS(MAX_BUFFER, reply)
  2069.  
  2070.         reply.seek(0)
  2071.  
  2072.         response = httplib.HTTPResponse(reply)
  2073.         response.begin()
  2074.  
  2075.         if response.status != httplib.OK:
  2076.             raise Error(ERROR_DEVICE_STATUS_NOT_AVAILABLE)
  2077.         else:
  2078.             return response.fp
  2079.  
  2080.