home *** CD-ROM | disk | FTP | other *** search
/ PC Welt 2006 November (DVD) / PCWELT_11_2006.ISO / casper / filesystem.squashfs / usr / lib / hplip / base / device.py < prev    next >
Encoding:
Python Source  |  2006-08-30  |  62.1 KB  |  1,725 lines

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