[ Avaa Bypassed ]




Upload:

Command:

www-data@3.21.104.216: ~ $
# -*- coding: utf-8 -*-
#
# (c) Copyright 2003-2015 HP Development Company, L.P.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
#
# Author: Don Welch, Naga Samrat Chowdary Narla
#

# Std Lib
import socket
import re
import gzip
import os.path
import time
from .sixext.moves import urllib_request, urllib_parse, urllib_error
import io
from io import BytesIO
from .sixext.moves import http_client
import struct
import string
import time
# Local
from .g import *
from .codes import *
from . import utils
from . import services
from . import os_utils
from . import status
from . import pml
from . import status
from prnt import pcl, ldl, cups
from . import models, mdns, slp, avahi
from .strings import *
from .sixext import PY3, to_bytes_utf8, to_unicode, to_string_latin, to_string_utf8, xStringIO

http_result_pat = re.compile("""HTTP/\d.\d\s(\d+)""", re.I)

HTTP_OK = 200
HTTP_ERROR = 500

try:
    import hpmudext
except ImportError:
    if not os.getenv("HPLIP_BUILD"):
        log.error("HPMUDEXT could not be loaded. Please check HPLIP installation.")
        sys.exit(1)
else:
    # Workaround for build machine
    try:
        MAX_BUFFER = hpmudext.HPMUD_BUFFER_SIZE
    except AttributeError:
        MAX_BUFFER = 8192

dbus_avail = False
dbus_disabled = False
try:
    import dbus
    from dbus import lowlevel, SessionBus
    dbus_avail = True
except ImportError:
    log.warn("python-dbus not installed.")

import warnings
# Ignore: .../dbus/connection.py:242: DeprecationWarning: object.__init__() takes no parameters
# (occurring on Python 2.6/dBus 0.83/Ubuntu 9.04)
warnings.simplefilter("ignore", DeprecationWarning)


DEFAULT_PROBE_BUS = ['usb', 'par', 'cups']
VALID_BUSES = ('par', 'net', 'cups', 'usb') #, 'bt', 'fw')
VALID_BUSES_WO_CUPS = ('par', 'net', 'usb')
DEFAULT_FILTER = None
VALID_FILTERS = ('print', 'scan', 'fax', 'pcard', 'copy')
DEFAULT_BE_FILTER = ('hp',)

pat_deviceuri = re.compile(r"""(.*):/(.*?)/(\S*?)\?(?:serial=(\S*)|device=(\S*)|ip=(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}[^&]*)|zc=(\S+)|hostname=(\S+))(?:&port=(\d))?""", re.IGNORECASE)
http_pat_url = re.compile(r"""/(.*?)/(\S*?)\?(?:serial=(\S*)|device=(\S*))&loc=(\S*)""", re.IGNORECASE)
direct_pat = re.compile(r'direct (.*?) "(.*?)" "(.*?)" "(.*?)"', re.IGNORECASE)

# Pattern to check for ; at end of CTR fields
# Note: If ; not present, CTR value is invalid
pat_dynamic_ctr = re.compile(r"""CTR:\d*\s.*;""", re.IGNORECASE)

# Cache for model data
model_dat = models.ModelData()

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)
dev_pat = re.compile(r"""/dev/.+""", re.IGNORECASE)
usb_pat = re.compile(r"""(\d+):(\d+)""", re.IGNORECASE)


class Event(object):
    def __init__(self, device_uri, printer_name, event_code,
                 username=prop.username, job_id=0, title='',
                 timedate=0):

        self.device_uri = to_unicode(device_uri)
        self.printer_name = to_unicode(printer_name)
        self.event_code = int(event_code)
        self.username = to_unicode(username)
        self.job_id = int(job_id)
        self.title = to_unicode(title)

        if timedate:
            self.timedate = float(timedate)
        else:
            self.timedate = time.time()

        self.pipe_fmt = "80s80sI32sI80sf"
        self.dbus_fmt = "ssisisd"


    def debug(self):
        log.debug("    device_uri=%s" % self.device_uri)
        log.debug("    printer_name=%s" % self.printer_name)
        log.debug("    event_code=%d" % self.event_code)
        log.debug("    username=%s" % self.username)
        log.debug("    job_id=%d" % self.job_id)
        log.debug("    title=%s" % self.title)
        log.debug("    timedate=%s" % self.timedate)


    def pack_for_pipe(self):
        return struct.pack(self.pipe_fmt, self.device_uri.encode('utf-8'), self.printer_name.encode('utf-8'),
                self.event_code, self.username.encode('utf-8'), self.job_id, self.title.encode('utf-8'),
                self.timedate)


    def send_via_pipe(self, fd, recipient='hpssd'):
        if fd is not None:
            log.debug("Sending event %d to %s (via pipe %d)..." % (self.event_code, recipient, fd))
            try:
                os.write(fd, self.pack_for_pipe())
                return True
            except OSError:
                log.debug("Failed.")
                return False


    def send_via_dbus(self, session_bus, interface='com.hplip.StatusService'):
        if session_bus is not None and dbus_avail:
            log.debug("Sending event %d to %s (via dbus)..." % (self.event_code, interface))
            msg = lowlevel.SignalMessage('/', interface, 'Event')
            msg.append(signature=self.dbus_fmt, *self.as_tuple())
            session_bus.send_message(msg)


    def copy(self):
        return Event(*self.as_tuple())


    def __str__(self):
        return "<Event('%s', '%s', %d, '%s', %d, '%s', %f)>" % self.as_tuple()


    def as_tuple(self):
        return (self.device_uri, self.printer_name, self.event_code,
             self.username, self.job_id, self.title, self.timedate)


class FaxEvent(Event):
    def __init__(self, temp_file, event):
        Event.__init__(self, *event.as_tuple())
        self.temp_file = temp_file
        self.pipe_fmt = "80s80sI32sI80sfs"
        self.dbus_fmt = "ssisisfs"


    def debug(self):
        log.debug("FAX:")
        Event.debug(self)
        log.debug("    temp_file=%s" % self.temp_file)


    def __str__(self):
        return "<FaxEvent('%s', '%s', %d, '%s', %d, '%s', %f, '%s')>" % self.as_tuple()


    def as_tuple(self):
        return (self.device_uri, self.printer_name, self.event_code,
             self.username, self.job_id, self.title, self.timedate,
             self.temp_file)



class DeviceIOEvent(Event):
    def __init__(self, bytes_written, event):
        Event.__init__(self, *event.as_tuple())
        self.bytes_written = bytes_written
        self.pipe_fmt = "80s80sI32sI80sfI"
        self.dbus_fmt = "ssisisfi"


    def debug(self):
        log.debug("DEVIO:")
        Event.debug(self)
        log.debug("    bytes_written=%d" % self.bytes_written)


    def __str__(self):
        return "<DeviceIOEvent('%s', '%s', %d, '%s', %d, '%s', %f, '%d')>" % self.as_tuple()


    def as_tuple(self):
        return (self.device_uri, self.printer_name, self.event_code,
             self.username, self.job_id, self.title, self.timedate,
             self.bytes_written)


#
# DBus Support
#

def init_dbus(dbus_loop=None):
    global dbus_avail
    service = None
    session_bus = None

    if not prop.gui_build:
        dbus_avail = False
        return dbus_avail, None,  None

    if dbus_avail and not dbus_disabled:
        if os.getuid() == 0:
            log.debug("Not starting dbus: running as root.")
            dbus_avail = False
            return dbus_avail, None,  None

        try:
            if dbus_loop is None:
                session_bus = dbus.SessionBus()
            else:
                session_bus = dbus.SessionBus(dbus_loop)
        except dbus.exceptions.DBusException as e:
            if os.getuid() != 0:
                log.error("Unable to connect to dbus session bus. %s "%e)
            else:
                log.debug("Unable to connect to dbus session bus (running as root?). %s "%e)

            dbus_avail = False
            return dbus_avail, None,  None

        try:
            log.debug("Connecting to com.hplip.StatusService (try #1)...")
            service = session_bus.get_object('com.hplip.StatusService', "/com/hplip/StatusService")
            dbus_avail = True
        except dbus.exceptions.DBusException as e:
            try:
                os.waitpid(-1, os.WNOHANG)
            except OSError:
                pass

            path = utils.which('hp-systray')
            if path:
                path = os.path.join(path, 'hp-systray')
            else:
                path = os.path.join(prop.home_dir, 'systray.py')
                if not os.path.exists(path):
                    log.warn("Unable to start hp-systray")
                    return False, None,  None

            log.debug("Running hp-systray: %s --force-startup" % path)

            os.spawnlp(os.P_NOWAIT, path, 'hp-systray', '--force-startup')

            log.debug("Waiting for hp-systray to start...")
            time.sleep(1)

            t = 2
            while True:
                try:
                    log.debug("Connecting to com.hplip.StatusService (try #%d)..." % t)
                    service = session_bus.get_object('com.hplip.StatusService', "/com/hplip/StatusService")

                except dbus.exceptions.DBusException as e:
                    log.debug("Unable to connect to dbus. Is hp-systray running?")
                    t += 1

                    if t > 5:
                        log.warn("Unable to connect to dbus. Is hp-systray running?")
                        return False, None,  None

                    time.sleep(1)

                else:
                    log.debug("Connected.")
                    dbus_avail = True
                    break

    return dbus_avail, service,  session_bus


#
# Make URI from parameter (bus ID, IP address, etc)
#

def makeURI(param, port=1):
    cups_uri, sane_uri, fax_uri = '', '', ''
    found = False

    if dev_pat.search(param) is not None: # parallel
        log.debug("Trying parallel with %s" % param)

        result_code, uri = hpmudext.make_par_uri(param)

        if result_code == hpmudext.HPMUD_R_OK and uri:
            uri = to_string_utf8(uri)
            log.debug("Found: %s" % uri)
            found = True
            cups_uri = uri
        else:
            log.debug("Not found.")

    elif usb_pat.search(param) is not None: # USB
        match_obj = usb_pat.search(param)
        usb_bus_id = match_obj.group(1)
        usb_dev_id = match_obj.group(2)

        log.debug("Trying USB with bus=%s dev=%s..." % (usb_bus_id, usb_dev_id))
        result_code, uri = hpmudext.make_usb_uri(usb_bus_id, usb_dev_id)

        if result_code == ERROR_SUCCESS and uri:
            uri = to_string_utf8(uri)
            log.debug("Found: %s" % uri)
            found = True
            cups_uri = uri
        else:
            log.debug("Not found.")

    elif ip_pat.search(param) is not None: # IPv4 dotted quad
        log.debug("Trying IP address %s" % param)

        result_code, uri = hpmudext.make_net_uri(param, port)

        if result_code == hpmudext.HPMUD_R_OK and uri:
            uri = to_string_utf8(uri)
            log.debug("Found: %s" % uri)
            found = True
            cups_uri = uri
        else:
            log.debug("Not found.")

    else: # Try Zeroconf hostname
        log.debug("Trying ZC hostname %s" % param)

        result_code, uri = hpmudext.make_zc_uri(param, port)

        if result_code == hpmudext.HPMUD_R_OK and uri:
            uri = to_string_utf8(uri)
            log.debug("Found: %s" % uri)
            found = True
            cups_uri = uri

        else: # Try DNS hostname
            log.debug("Device not found using mDNS hostname. Trying with DNS hostname %s" % param)

            result_code, uri = hpmudext.make_net_uri(param, port)

            if result_code == hpmudext.HPMUD_R_OK and uri:
                uri = to_string_utf8(uri)
                uri = uri.replace("ip=","hostname=")
                log.debug("Found: %s" % uri)
                found = True
                cups_uri = uri
            else:
                log.debug("Not found.")

    if not found:
        log.debug("Trying serial number %s" % param)
        devices = probeDevices(bus=['usb', 'par'])

        for d in devices:
            log.debug(d)

            # usb has serial in URI...
            try:
                back_end, is_hp, bus, model, serial, dev_file, host, zc, port = \
                    parseDeviceURI(d)
            except Error:
                continue

            if bus == 'par': # ...parallel does not. Must get Device ID to obtain it...
                mq = queryModelByURI(d)

                result_code, device_id = \
                    hpmudext.open_device(d, mq.get('io-mode', hpmudext.HPMUD_UNI_MODE))

                if result_code == hpmudext.HPMUD_R_OK:
                    result_code, data = hpmudext.get_device_id(device_id)
                    serial = parseDeviceID(data).get('SN', '')
                    hpmudext.close_device(device_id)

            if serial.lower() == param.lower():
                log.debug("Found: %s" % d)
                found = True
                cups_uri = d
                break
            else:
                log.debug("Not found.")

    if found:
        try:
            mq = queryModelByURI(cups_uri)
        except Error as e:
            log.error("Error: %s" % e.msg)
            cups_uri, sane_uri, fax_uri = '', '', ''
        else:
            if mq.get('support-type', SUPPORT_TYPE_NONE) > SUPPORT_TYPE_NONE:
                if mq.get('scan-type', 0):
                    sane_uri = cups_uri.replace("hp:", "hpaio:")

                if mq.get('fax-type', 0):
                    fax_uri = cups_uri.replace("hp:", "hpfax:")

            else:
                cups_uri, sane_uri, fax_uri = '', '', ''

    else:
        scan_uri, fax_uri = '', ''

    if cups_uri:
        user_conf.set('last_used', 'device_uri', cups_uri)

    return cups_uri, sane_uri, fax_uri


#
# Model Queries
#

def queryModelByModel(model):
    model = models.normalizeModelName(model).lower()
    return model_dat[model]


def queryModelByURI(device_uri):
    try:
        back_end, is_hp, bus, model, \
            serial, dev_file, host, zc, port = \
            parseDeviceURI(device_uri)
    except Error:
        raise Error(ERROR_INVALID_DEVICE_URI)
    else:
        return queryModelByModel(model)


#
# Device Discovery
#

def probeDevices(bus=DEFAULT_PROBE_BUS, timeout=10,
                 ttl=4, filter=DEFAULT_FILTER,  search='', net_search='slp',
                 back_end_filter=('hp',)):

    num_devices, ret_devices = 0, {}

    if search:
        try:
            search_pat = re.compile(search, re.IGNORECASE)
        except:
            log.error("Invalid search pattern. Search uses standard regular expressions. For more info, see: http://www.amk.ca/python/howto/regex/")
            search = ''

    for b in bus:
        log.debug("Probing bus: %s" % b)
        if b not in VALID_BUSES:
            log.error("Invalid bus: %s" % b)
            continue

        if b == 'net':
            if net_search == 'slp':
                try:
                    detected_devices = slp.detectNetworkDevices(ttl, timeout)
                except Error as socket_error:
                    socket.error = socket_error
                    log.error("An error occured during network probe.[%s]"%socket_error)
                    raise ERROR_INTERNAL
            elif net_search == 'avahi':
                try:
                    detected_devices = avahi.detectNetworkDevices(ttl, timeout)
                except Error as socket_error:
                    socket.error = socket_error
                    log.error("An error occured during network probe.[%s]"%socket_error)
                    raise ERROR_INTERNAL
            else :#if net_search = 'mdns'
                try:
                    detected_devices = mdns.detectNetworkDevices(ttl, timeout)
                except Error as socket_error:
                    socket.error = socket_error
                    log.error("An error occured during network probe.[%s]"%socket_error)
                    raise ERROR_INTERNAL

            for ip in detected_devices:
                update_spinner()
                hn = detected_devices[ip].get('hn', '?UNKNOWN?')
                num_devices_on_jd = detected_devices[ip].get('num_devices', 0)
                num_ports_on_jd = detected_devices[ip].get('num_ports', 1)

                if num_devices_on_jd > 0:
                    for port in range(num_ports_on_jd):
                        dev = detected_devices[ip].get('device%d' % (port+1), '0')

                        if dev is not None and dev != '0':
                            device_id = parseDeviceID(dev)
                            model = models.normalizeModelName(device_id.get('MDL', '?UNKNOWN?'))

                            if num_ports_on_jd == 1:
                                if net_search == 'slp':
                                    device_uri = 'hp:/net/%s?ip=%s' % (model, ip)
                                else:
                                    device_uri = 'hp:/net/%s?zc=%s' % (model, hn)
                            else:
                                if net_search == 'slp':
                                    device_uri = 'hp:/net/%s?ip=%s&port=%d' % (model, ip, (port + 1))
                                else:
                                    device_uri = 'hp:/net/%s?zc=%s&port=%d' % (model, hn, (port + 1))

                            include = True
                            mq = queryModelByModel(model)

                            if not mq:
                                log.debug("Not found.")
                                include = False

                            elif int(mq.get('support-type', SUPPORT_TYPE_NONE)) == SUPPORT_TYPE_NONE:
                                log.debug("Not supported.")
                                include = False

                            elif filter not in (None, 'print', 'print-type'):
                                include = __checkFilter(filter, mq)

                            if include:
                                ret_devices[device_uri] = (model, model, hn)

        elif b in ('usb', 'par'):
            if b == 'par':
                bn = hpmudext.HPMUD_BUS_PARALLEL
            else:
                bn = hpmudext.HPMUD_BUS_USB

            result_code, data = hpmudext.probe_devices(bn)
            if result_code == hpmudext.HPMUD_R_OK:
                for x in data.splitlines():
                    m = direct_pat.match(x)

                    uri = m.group(1) or ''
                    mdl = m.group(2) or ''
                    desc = m.group(3) or ''
                    devid = m.group(4) or ''

                    log.debug(uri)
                    #if("scanjet" in  mdl.lower()):
                    #    continue # Do not include HP Scanjets

                    try:
                        back_end, is_hp, bb, model, serial, dev_file, host, zc, port = \
                            parseDeviceURI(uri)
                    except Error:
                        continue

                    include = True

                    if mdl and uri and is_hp:
                        mq = queryModelByModel(model)

                        if not mq:
                            log.debug("Not found.")
                            include = False

                        elif int(mq.get('support-type', SUPPORT_TYPE_NONE)) == SUPPORT_TYPE_NONE:
                            log.debug("Not supported.")
                            include = False

                        elif filter not in (None, 'print', 'print-type'):
                            include = __checkFilter(filter, mq)

                        if include:
                            ret_devices[uri] = (mdl, desc, devid) # model w/ _'s, mdl w/o

        elif b == 'cups':
            cups_printers = cups.getPrinters()
            x = len(cups_printers)

            for p in cups_printers:
                device_uri = p.device_uri
                log.debug("%s: %s" % (device_uri, p.name))

                if device_uri != '':
                    try:
                        back_end, is_hp, bs, model, serial, dev_file, host, zc, port = \
                            parseDeviceURI(device_uri)
                    except Error:
                        log.debug("Unrecognized URI: %s" % device_uri)
                        continue

                    if not is_hp:
                        continue

                    include = True
                    mq = queryModelByModel(model)

                    if not mq:
                        include = False
                        log.debug("Not found.")

                    elif int(mq.get('support-type', SUPPORT_TYPE_NONE)) == SUPPORT_TYPE_NONE:
                        log.debug("Not supported.")
                        include = False

                    elif filter not in (None, 'print', 'print-type'):
                        include = __checkFilter(filter, mq)

                    if include:
                        ret_devices[device_uri] = (model, model, '')

    probed_devices = {}
    for uri in ret_devices:
        num_devices += 1
        mdl, model, devid_or_hn = ret_devices[uri]

        include = True
        if search:
            match_obj = search_pat.search("%s %s %s %s" % (mdl, model, devid_or_hn, uri))

            if match_obj is None:
                log.debug("%s %s %s %s: Does not match search '%s'." % (mdl, model, devid_or_hn, uri, search))
                include = False

        if include:
            probed_devices[uri] = ret_devices[uri]

    cleanup_spinner()
    return probed_devices

#
# CUPS Devices
#

def getSupportedCUPSDevices(back_end_filter=['hp'], filter=DEFAULT_FILTER):
    devices = {}
    printers = cups.getPrinters()
    log.debug(printers)

    for p in printers:
        try:
            back_end, is_hp, bus, model, serial, dev_file, host, zc, port = \
                parseDeviceURI(p.device_uri)

        except Error:
            continue

        if (back_end_filter == '*' or back_end in back_end_filter or \
            ('hpaio' in back_end_filter and back_end == 'hp')) and \
            model and is_hp:

            include = True
            mq = queryModelByModel(model)

            if not mq:
                log.debug("Not found.")
                include = False

            elif int(mq.get('support-type', SUPPORT_TYPE_NONE)) == SUPPORT_TYPE_NONE:
                log.debug("Not supported.")
                include = False

            elif filter not in (None, 'print', 'print-type'):
                include = __checkFilter(filter, mq)

            if include:
                if 'hpaio' in back_end_filter:
                    d = p.device_uri.replace('hp:', 'hpaio:')
                else:
                    d = p.device_uri

                try:
                    devices[d]
                except KeyError:
                    devices[d] = [p.name]
                else:
                    devices[d].append(p.name)

    return devices # { 'device_uri' : [ CUPS printer list ], ... }


def getSupportedCUPSPrinters(back_end_filter=['hp'], filter=DEFAULT_FILTER):
    printer_list = []
    printers = cups.getPrinters()

    for p in printers:
        try:
            back_end, is_hp, bus, model, serial, dev_file, host, zc, port = \
                parseDeviceURI(p.device_uri)

        except Error:
            continue

        if (back_end_filter == '*' or back_end in back_end_filter) and model and is_hp:
            include = True
            mq = queryModelByModel(model)

            if not mq:
                log.debug("Not found.")
                include = False

            elif int(mq.get('support-type', SUPPORT_TYPE_NONE)) == SUPPORT_TYPE_NONE:
                log.debug("Not supported.")
                include = False

            elif filter not in (None, 'print', 'print-type'):
                include = __checkFilter(filter, mq)

            if include:
                printer_list.append(p)


    return printer_list # [ cupsext.Printer, ... ]


def getSupportedCUPSPrinterNames(back_end_filter=['hp'], filter=DEFAULT_FILTER):
    printers = getSupportedCUPSPrinters(back_end_filter, filter)
    return [p.name for p in printers]


def getDeviceURIByPrinterName(printer_name, scan_uri_flag=False):
    if printer_name is None:
        return None

    device_uri = None
    printers = cups.getPrinters()

    for p in printers:
        try:
            back_end, is_hp, bus, model, serial, dev_file, host, zc, port = \
                parseDeviceURI(p.device_uri)

        except Error:
            continue

        if is_hp and p.name == printer_name:
            if scan_uri_flag:
                device_uri = p.device_uri.replace('hp:', 'hpaio:')
            else:
                device_uri = p.device_uri
            break

    return device_uri

#
# IEEE-1284 Device ID parsing
#

def parseDeviceID(device_id):
    d= {}
    x = [y.strip() for y in device_id.strip().split(';') if y]

    for z in x:
        y = z.split(':')
        try:
            d.setdefault(y[0].strip(), y[1])
        except IndexError:
            d.setdefault(y[0].strip(), None)

    d.setdefault('MDL', '')
    d.setdefault('SN',  '')

    if 'MODEL' in d:
        d['MDL'] = d['MODEL']
        del d['MODEL']

    if 'SERIAL' in d:
        d['SN'] = d['SERIAL']
        del d['SERIAL']

    elif 'SERN' in d:
        d['SN'] = d['SERN']
        del d['SERN']

    if d['SN'].startswith('X'):
        d['SN'] = ''

    return d

#
# IEEE-1284 Device ID Dynamic Counter Parsing
#

def parseDynamicCounter(ctr_field, convert_to_int=True):
    counter, value = ctr_field.split(' ')
    try:
        counter = int(utils.xlstrip(str(counter), '0') or '0')

        if convert_to_int:
            value = int(utils.xlstrip(str(value), '0') or '0')
    except ValueError:
        if convert_to_int:
            counter, value = 0, 0
        else:
            counter, value = 0, ''

    return counter, value


#
# Parse Device URI Strings
#

def parseDeviceURI(device_uri):
    m = pat_deviceuri.match(device_uri)
    if m is None:
        log.debug("Device URI %s is invalid/unknown" % device_uri)
        raise Error(ERROR_INVALID_DEVICE_URI)

    back_end = m.group(1).lower() or ''
    is_hp = (back_end in ('hp', 'hpfax', 'hpaio'))
    bus = m.group(2).lower() or ''

    if bus not in ('usb', 'net', 'bt', 'fw', 'par'):
        log.debug("Device URI %s is invalid/unknown" % device_uri)
        raise Error(ERROR_INVALID_DEVICE_URI)

    model = m.group(3) or ''
    serial = m.group(4) or ''
    dev_file = m.group(5) or ''
    host = m.group(6) or ''
    zc = m.group(7) or ''
    hostname = m.group(8) or ''

    if hostname:
        host = hostname
    elif zc:
        host = zc

    port = m.group(8) or 1

    if bus == 'net':
        try:
            port = int(port)
        except (ValueError, TypeError):
            port = 1

        if port == 0:
            port = 1

    log.debug("%s: back_end:%s is_hp:%s bus:%s model:%s serial:%s dev_file:%s host:%s zc:%s port:%s" %
        (device_uri, back_end, is_hp, bus, model, serial, dev_file, host, zc, port))

    return back_end, is_hp, bus, model, serial, dev_file, host, zc, port


def isLocal(bus):
    return bus in ('par', 'usb', 'fw', 'bt')


def isNetwork(bus):
    return bus in ('net',)


#
# Misc
#

def __checkFilter(filter, mq):
    for f, p in list(filter.items()):
        if f is not None:
            op, val = p
            if not op(mq[f], val):
                return False

    return True


def validateBusList(bus, allow_cups=True):
    for b in bus:
        if allow_cups:
            vb = VALID_BUSES
        else:
            vb = VALID_BUSES_WO_CUPS

        if b not in vb:
            log.error("Invalid bus name: %s" %b)
            return False

    return True


def validateFilterList(filter):
    if filter is None:
        return True

    for f in filter:
        if f not in VALID_FILTERS:
            log.error("Invalid term '%s' in filter list" % f)
            return False

    return True


AGENT_types = { AGENT_TYPE_NONE        : 'invalid',
                AGENT_TYPE_BLACK       : 'black',
                AGENT_TYPE_BLACK_B8800 : 'black',
                AGENT_TYPE_CMY         : 'cmy',
                AGENT_TYPE_KCM         : 'kcm',
                AGENT_TYPE_CYAN        : 'cyan',
                AGENT_TYPE_MAGENTA     : 'magenta',
                AGENT_TYPE_YELLOW      : 'yellow',
                AGENT_TYPE_CYAN_LOW    : 'photo_cyan',
                AGENT_TYPE_MAGENTA_LOW : 'photo_magenta',
                AGENT_TYPE_YELLOW_LOW  : 'photo_yellow',
                AGENT_TYPE_GGK         : 'photo_gray',
                AGENT_TYPE_BLUE        : 'photo_blue',
                AGENT_TYPE_KCMY_CM     : 'kcmy_cm',
                AGENT_TYPE_LC_LM       : 'photo_cyan_and_photo_magenta',
                #AGENT_TYPE_Y_M         : 'yellow_and_magenta',
                #AGENT_TYPE_C_K         : 'cyan_and_black',
                AGENT_TYPE_LG_PK       : 'light_gray_and_photo_black',
                AGENT_TYPE_LG          : 'light_gray',
                AGENT_TYPE_G           : 'medium_gray',
                AGENT_TYPE_PG          : 'photo_gray',
                AGENT_TYPE_C_M         : 'cyan_and_magenta',
                AGENT_TYPE_K_Y         : 'black_and_yellow',
                AGENT_TYPE_PHOTO_BLACK : 'photo_black',
                AGENT_TYPE_MATTE_BLACK : 'matte_black',
                AGENT_TYPE_UNSPECIFIED : 'unspecified', # Kind=5,6
            }

AGENT_kinds = {AGENT_KIND_NONE            : 'invalid',
                AGENT_KIND_HEAD            : 'head',
                AGENT_KIND_SUPPLY          : 'supply',
                AGENT_KIND_HEAD_AND_SUPPLY : 'cartridge',
                AGENT_KIND_TONER_CARTRIDGE : 'toner',
                AGENT_KIND_MAINT_KIT       : 'maint_kit', # fuser
                AGENT_KIND_ADF_KIT         : 'adf_kit',
                AGENT_KIND_DRUM_KIT        : 'drum_kit',
                AGENT_KIND_TRANSFER_KIT    : 'transfer_kit',
                AGENT_KIND_INT_BATTERY     : 'battery',
                AGENT_KIND_UNKNOWN         : 'unknown',
              }

AGENT_healths = {AGENT_HEALTH_OK           : 'ok',
                  AGENT_HEALTH_MISINSTALLED : 'misinstalled', # supply/cart
                  #AGENT_HEALTH_FAIR_MODERATE : '',
                  AGENT_HEALTH_INCORRECT    : 'incorrect',
                  AGENT_HEALTH_FAILED       : 'failed',
                  AGENT_HEALTH_OVERTEMP     : 'overtemp', # battery
                  AGENT_HEALTH_CHARGING     : 'charging', # battery
                  AGENT_HEALTH_DISCHARGING  : 'discharging', # battery
                  AGENT_HEALTH_UNKNOWN      : 'unknown',
                }


AGENT_levels = {AGENT_LEVEL_TRIGGER_MAY_BE_LOW : 'low',
                 AGENT_LEVEL_TRIGGER_PROBABLY_OUT : 'low',
                 AGENT_LEVEL_TRIGGER_ALMOST_DEFINITELY_OUT : 'out',
               }




# **************************************************************************** #

string_cache = {}

class Device(object):
    def __init__(self, device_uri, printer_name=None,
                 service=None, callback=None, disable_dbus=False):

        log.debug("Device URI: %s" % device_uri)
        log.debug("Printer: %s" % printer_name)

        global dbus_disabled
        dbus_disabled = disable_dbus

        if not disable_dbus:
            if service is None:
                self.dbus_avail, self.service,  session_bus = init_dbus()
            else:
                self.dbus_avail = True
                self.service = service
        else:
            self.dbus_avail = False
            self.service = None

        self.last_event = None # Used in devmgr if dbus is disabled

        printers = cups.getPrinters()

        if device_uri is None and printer_name is not None:
            for p in printers:
                if p.name.lower() == printer_name.lower():
                    device_uri = p.device_uri
                    log.debug("Device URI: %s" % device_uri)
                    break
            else:
                raise Error(ERROR_DEVICE_NOT_FOUND)

        self.device_uri = device_uri
        self.callback = callback
        self.device_type = DEVICE_TYPE_UNKNOWN

        if self.device_uri is None:
            raise Error(ERROR_DEVICE_NOT_FOUND)

        if self.device_uri.startswith('hp:'):
            self.device_type = DEVICE_TYPE_PRINTER

        elif self.device_uri.startswith('hpaio:'):
            self.device_type = DEVICE_TYPE_SCANNER

        elif self.device_uri.startswith('hpfax:'):
            self.device_type = DEVICE_TYPE_FAX

        try:
            self.back_end, self.is_hp, self.bus, self.model, \
                self.serial, self.dev_file, self.host, self.zc, self.port = \
                parseDeviceURI(self.device_uri)
        except Error:
            self.io_state = IO_STATE_NON_HP
            raise Error(ERROR_INVALID_DEVICE_URI)

        log.debug("URI: backend=%s, is_hp=%s, bus=%s, model=%s, serial=%s, dev=%s, host=%s, port=%d" % \
            (self.back_end, self.is_hp, self.bus, self.model, self.serial, self.dev_file, self.host, self.port))

        self.model_ui = models.normalizeModelUIName(self.model)
        self.model = models.normalizeModelName(self.model)

        log.debug("Model/UI model: %s/%s" % (self.model, self.model_ui))

        if self.bus == 'net':
            self.http_host = self.host
        else:
            self.http_host = 'localhost'  

        # TODO:
        #service.setAlertsEx(self.hpssd_sock)

        self.mq = {} # Model query
        self.dq = {} # Device query
        self.icon = "default_printer"
        self.cups_printers = []
        self.channels = {} # { 'SERVICENAME' : channel_id, ... }
        self.device_id = -1
        self.r_values = None # ( r_value, r_value_str, rg, rr )
        self.deviceID = ''
        self.panel_check = True
        self.io_state = IO_STATE_HP_READY
        self.is_local = isLocal(self.bus)
        self.hist = []

        self.supported = False

        self.queryModel()
        if not self.supported:
            log.error("Unsupported model: %s" % self.model)
            self.error_code = STATUS_DEVICE_UNSUPPORTED
            self.sendEvent(self.error_code)
        else:
            self.supported = True


        self.mq.update({'model'    : self.model,
                        'model-ui' : self.model_ui})

        self.error_state = ERROR_STATE_ERROR
        self.device_state = DEVICE_STATE_NOT_FOUND
        self.status_code = EVENT_ERROR_DEVICE_NOT_FOUND

        self.updateCUPSPrinters()

        if self.mq.get('fax-type', FAX_TYPE_NONE) != FAX_TYPE_NONE:
            self.dq.update({ 'fax-uri' : self.device_uri.replace('hp:/', 'hpfax:/').replace('hpaio:/', 'hpfax:/')})

        if self.mq.get('scan-type', SCAN_TYPE_NONE) != SCAN_TYPE_NONE:
            self.dq.update({ 'scan-uri' : self.device_uri.replace('hp:/', 'hpaio:/').replace('hpfax:/', 'hpaio:/')})

        self.dq.update({
            'back-end'         : self.back_end,
            'is-hp'            : self.is_hp,
            'serial'           : self.serial,
            'dev-file'         : self.dev_file,
            'host'             : self.host,
            'port'             : self.port,
            'cups-printers'    : self.cups_printers,
            'status-code'      : self.status_code,
            'status-desc'      : '',
            'deviceid'         : '',
            'panel'            : 0,
            'panel-line1'      : '',
            'panel-line2'      : '',
            'device-state'     : self.device_state,
            'error-state'      : self.error_state,
            'device-uri'       : self.device_uri,
            'cups-uri'         : self.device_uri.replace('hpfax:/', 'hp:/').replace('hpaio:/', 'hp:/'),
            })

        self.device_vars = {
            'URI'        : self.device_uri,
            'DEVICE_URI' : self.device_uri,
            'SCAN_URI'   : self.device_uri.replace('hp:', 'hpaio:'),
            'SANE_URI'   : self.device_uri.replace('hp:', 'hpaio:'),
            'FAX_URI'    : self.device_uri.replace('hp:', 'hpfax:'),
            'PRINTER'    : self.first_cups_printer,
            'HOME'       : prop.home_dir,
                           }




    def sendEvent(self, event_code, printer_name='', job_id=0, title=''):
        if self.dbus_avail and self.service is not None:
            try:
                log.debug("Sending event %d to hpssd..." % event_code)
                self.service.SendEvent(self.device_uri, printer_name, event_code, prop.username, job_id, title)
            except dbus.exceptions.DBusException as e:
                log.debug("dbus call to SendEvent() failed.")


    def quit(self):
        pass


    def queryModel(self):
        if not self.mq:
            self.mq = queryModelByURI(self.device_uri)

        self.supported = bool(self.mq)

        if self.supported:
            for m in self.mq:
                self.__dict__[m.replace('-','_')] = self.mq[m]


    def queryString(self, string_id):
        return queryString(string_id)


    def open(self, open_for_printing=False):
        if self.supported and self.io_state in (IO_STATE_HP_READY, IO_STATE_HP_NOT_AVAIL):
            prev_device_state = self.device_state
            self.io_state = IO_STATE_HP_NOT_AVAIL
            self.device_state = DEVICE_STATE_NOT_FOUND
            self.error_state = ERROR_STATE_ERROR
            self.status_code = EVENT_ERROR_DEVICE_NOT_FOUND
            self.device_id = -1
            self.open_for_printing = open_for_printing

            if open_for_printing:
                log.debug("Opening device: %s (for printing)" % self.device_uri)
                self.io_mode = self.mq.get('io-mode', hpmudext.HPMUD_UNI_MODE)
            else:
                log.debug("Opening device: %s (not for printing)" % self.device_uri)
                self.io_mode = self.mq.get('io-mfp-mode', hpmudext.HPMUD_UNI_MODE)

            log.debug("I/O mode=%d" % self.io_mode)
            result_code, self.device_id = \
                hpmudext.open_device(self.device_uri, self.io_mode)

            if result_code != hpmudext.HPMUD_R_OK:
                self.error_state = ERROR_STATE_ERROR
                self.error_code = result_code+ERROR_CODE_BASE
                self.sendEvent(self.error_code)

                if result_code == hpmudext.HPMUD_R_DEVICE_BUSY:
                    log.error("Device busy: %s" % self.device_uri)
                else:
                    log.error("Unable to communicate with device (code=%d): %s" % (result_code, self.device_uri))

                self.last_event = Event(self.device_uri, '', EVENT_ERROR_DEVICE_NOT_FOUND,
                        prop.username, 0, '', time.time())

                raise Error(ERROR_DEVICE_NOT_FOUND)

            else:
                log.debug("device-id=%d" % self.device_id)
                self.io_state = IO_STATE_HP_OPEN
                self.error_state = ERROR_STATE_CLEAR
                log.debug("Opened device: %s (backend=%s, is_hp=%s, bus=%s, model=%s, dev=%s, serial=%s, host=%s, port=%d)" %
                    (self.back_end, self.device_uri, self.is_hp, self.bus, self.model,
                     self.dev_file, self.serial, self.host, self.port))

                if prev_device_state == DEVICE_STATE_NOT_FOUND:
                    self.device_state = DEVICE_STATE_JUST_FOUND
                else:
                    self.device_state = DEVICE_STATE_FOUND

                self.getDeviceID()
                self.getSerialNumber()
                return self.device_id


    def close(self):
        if self.io_state == IO_STATE_HP_OPEN:
            log.debug("Closing device...")

            if len(self.channels) > 0:

                for c in list(self.channels.keys()):
                    self.__closeChannel(c)

            result_code = hpmudext.close_device(self.device_id)
            log.debug("Result-code = %d" % result_code)

            self.channels.clear()
            self.io_state = IO_STATE_HP_READY


    def __openChannel(self, service_name):
        try:
            if self.io_state == IO_STATE_HP_OPEN:
                if service_name == hpmudext.HPMUD_S_PRINT_CHANNEL and not self.open_for_printing:
                    self.close()
                    self.open(True)
                elif service_name != hpmudext.HPMUD_S_PRINT_CHANNEL and self.open_for_printing:
                    self.close()
                    self.open(False)
            else:
                self.open(service_name == hpmudext.HPMUD_S_PRINT_CHANNEL)
        except:
            log.error("unable to open channel")
            return -1

        #if not self.mq['io-mode'] == IO_MODE_UNI:
        if 1:
            service_name = service_name.upper()

            if service_name not in self.channels:
                log.debug("Opening %s channel..." % service_name)

                result_code, channel_id = hpmudext.open_channel(self.device_id, service_name)

                self.channels[service_name] = channel_id
                log.debug("channel-id=%d" % channel_id)
                return channel_id
            else:
                return self.channels[service_name]
        else:
            return -1


    def openChannel(self, service_name):
        return self.__openChannel(service_name)

    def openPrint(self):
        return self.__openChannel(hpmudext.HPMUD_S_PRINT_CHANNEL)

    def openFax(self):
        return self.__openChannel(hpmudext.HPMUD_S_FAX_SEND_CHANNEL)

    def openPCard(self):
        return self.__openChannel(hpmudext.HPMUD_S_MEMORY_CARD_CHANNEL)

    def openEWS(self):
        return self.__openChannel(hpmudext.HPMUD_S_EWS_CHANNEL)

    def openEWS_LEDM(self):
        return self.__openChannel(hpmudext.HPMUD_S_EWS_LEDM_CHANNEL)

    def openLEDM(self):
        return self.__openChannel(hpmudext.HPMUD_S_LEDM_SCAN)

    def openMarvell_EWS(self):
        return self.__openChannel(hpmudext.HPMUD_S_MARVELL_EWS_CHANNEL)

    def closePrint(self):
        return self.__closeChannel(hpmudext.HPMUD_S_PRINT_CHANNEL)

    def closePCard(self):
        return self.__closeChannel(hpmudext.HPMUD_S_MEMORY_CARD_CHANNEL)

    def closeFax(self):
        return self.__closeChannel(hpmudext.HPMUD_S_FAX_SEND_CHANNEL)

    def openPML(self):
        return self.__openChannel(hpmudext.HPMUD_S_PML_CHANNEL)

    def openWifiConfig(self):
        return self.__openChannel(hpmudext.HPMUD_S_WIFI_CHANNEL)

    def closePML(self):
        return self.__closeChannel(hpmudext.HPMUD_S_PML_CHANNEL)

    def closeEWS(self):
        return self.__closeChannel(hpmudext.HPMUD_S_EWS_CHANNEL)

    def closeEWS_LEDM(self):
        return self.__closeChannel(hpmudext.HPMUD_S_EWS_LEDM_CHANNEL)

    def closeLEDM(self):
        return self.__closeChannel(hpmudext.HPMUD_S_LEDM_SCAN)

    def closeMarvell_EWS(self):
        return self.__closeChannel(hpmudext.HPMUD_S_MARVELL_EWS_CHANNEL)

    def openCfgUpload(self):
        return self.__openChannel(hpmudext.HPMUD_S_CONFIG_UPLOAD_CHANNEL)

    def closeCfgUpload(self):
        return self.__closeChannel(hpmudext.HPMUD_S_CONFIG_UPLOAD_CHANNEL)

    def openCfgDownload(self):
        return self.__openChannel(hpmudext.HPMUD_S_CONFIG_DOWNLOAD_CHANNEL)

    def closeCfgDownload(self):
        return self.__closeChannel(hpmudext.HPMUD_S_CONFIG_DOWNLOAD_CHANNEL)

    def openSoapFax(self):
        return self.__openChannel(hpmudext.HPMUD_S_SOAP_FAX)

    def openMarvellFax(self):
        return self.__openChannel(hpmudext.HPMUD_S_MARVELL_FAX_CHANNEL)

    def closeSoapFax(self):
        return self.__closeChannel(hpmudext.HPMUD_S_SOAP_FAX)

    def closeMarvellFax(self):
        return self.__closeChannel(hpmudext.HPMUD_S_MARVELL_FAX_CHANNEL)

    def closeWifiConfig(self):
        return self.__closeChannel(hpmudext.HPMUD_S_WIFI_CHANNEL)

    def __closeChannel(self, service_name):
        #if not self.mq['io-mode'] == IO_MODE_UNI and \
        if self.io_state == IO_STATE_HP_OPEN:

            service_name = service_name.upper()

            if service_name in self.channels:
                log.debug("Closing %s channel..." % service_name)

                result_code = hpmudext.close_channel(self.device_id,
                    self.channels[service_name])

                del self.channels[service_name]


    def closeChannel(self, service_name):
        return self.__closeChannel(service_name)


    def getDeviceID(self):
        needs_close = False
        self.raw_deviceID = ''
        self.deviceID = {}

        if self.io_state != IO_STATE_HP_OPEN:
           try:
               self.open()
           except:
               return -1
           needs_close = True

        result_code, data = hpmudext.get_device_id(self.device_id)

        if result_code == hpmudext.HPMUD_R_OK:
            self.raw_deviceID = data
            self.deviceID = parseDeviceID(data)

        if needs_close:
            self.close()

        return self.deviceID


    def getSerialNumber(self):
        if self.serial:
            return

        try:
            self.serial = self.deviceID['SN']
        except KeyError:
            pass
        else:
            if self.serial:
                return

        if self.mq.get('status-type', STATUS_TYPE_NONE) != STATUS_TYPE_NONE: # and \
            #not self.mq.get('io-mode', IO_MODE_UNI) == IO_MODE_UNI:

            try:
                try:
                    error_code, self.serial = self.getPML(pml.OID_SERIAL_NUMBER)
                except Error:
                    self.serial = ''
            finally:
                self.closePML()

        if self.serial is None:
            self.serial = ''


    def getThreeBitStatus(self):
        pass


    def getStatusFromDeviceID(self):
        self.getDeviceID()
        return status.parseStatus(parseDeviceID(self.raw_deviceID))


    def __parseRValues(self, r_value):
        r_value_str = str(r_value)
        r_value_str = ''.join(['0'*(9 - len(r_value_str)), r_value_str])
        rg, rr = r_value_str[:3], r_value_str[3:]
        r_value = int(rr)
        self.r_values = r_value, r_value_str, rg, rr
        return r_value, r_value_str, rg, rr


    def getRValues(self, r_type, status_type, dynamic_counters):
        r_value, r_value_str, rg, rr = 0, '000000000', '000', '000000'

        if r_type > 0 and \
            dynamic_counters != STATUS_DYNAMIC_COUNTERS_NONE:

            if self.r_values is None:
                if self.dbus_avail:
                    try:
                        r_value = int(self.service.GetCachedIntValue(self.device_uri, 'r_value'))
                    except dbus.exceptions.DBusException as e:
                        log.debug("dbus call to GetCachedIntValue() failed.")
                        r_value = -1

                if r_value != -1:
                    log.debug("r_value=%d" % r_value)
                    r_value, r_value_str, rg, rr = self.__parseRValues(r_value)

                    return r_value, r_value_str, rg, rr

            if self.r_values is None:

                if status_type ==  STATUS_TYPE_S and \
                    self.is_local and \
                    dynamic_counters != STATUS_DYNAMIC_COUNTERS_PML_SNMP:

                    try:
                        try:
                            r_value = self.getDynamicCounter(140)

                            if r_value is not None:
                                log.debug("r_value=%d" % r_value)
                                r_value, r_value_str, rg, rr = self.__parseRValues(r_value)

                                if self.dbus_avail:
                                    try:
                                        self.service.SetCachedIntValue(self.device_uri, 'r_value', r_value)
                                    except dbus.exceptions.DBusException as e:
                                        log.debug("dbus call to SetCachedIntValue() failed.")
                            else:
                                log.error("Error attempting to read r-value (2).")
                                r_value = 0
                        except Error:
                            log.error("Error attempting to read r-value (1).")
                            r_value = 0
                    finally:
                        self.closePrint()


                elif (status_type ==  STATUS_TYPE_S and
                      dynamic_counters == STATUS_DYNAMIC_COUNTERS_PCL and
                      not self.is_local) or \
                      dynamic_counters == STATUS_DYNAMIC_COUNTERS_PML_SNMP:

                    try:
                        result_code, r_value = self.getPML(pml.OID_R_SETTING)

                        if r_value is not None:
                            log.debug("r_value=%d" % r_value)
                            r_value, r_value_str, rg, rr = self.__parseRValues(r_value)

                            if self.dbus_avail:
                                try:
                                    self.service.SetCachedIntValue(self.device_uri, 'r_value', r_value)
                                except dbus.exceptions.DBusException as e:
                                    log.debug("dbus call to SetCachedIntValue() failed.")

                        else:
                            r_value = 0

                    finally:
                        self.closePML()

            else:
                r_value, r_value_str, rg, rr = self.r_values

        return r_value, r_value_str, rg, rr


    def __queryFax(self, quick=False, reread_cups_printers=False):
        io_mode = self.mq.get('io-mode', IO_MODE_UNI)
        self.status_code = STATUS_PRINTER_IDLE

        if io_mode != IO_MODE_UNI:

            if self.device_state != DEVICE_STATE_NOT_FOUND:
                if self.tech_type in (TECH_TYPE_MONO_INK, TECH_TYPE_COLOR_INK):
                    try:
                        self.getDeviceID()
                    except Error as e:
                        log.error("Error getting device ID.")
                        self.last_event = Event(self.device_uri, '', ERROR_DEVICE_IO_ERROR,
                            prop.username, 0, '', time.time())

                        raise Error(ERROR_DEVICE_IO_ERROR)

                status_desc = self.queryString(self.status_code)

                self.dq.update({
                    'serial'           : self.serial,
                    'cups-printers'    : self.cups_printers,
                    'status-code'      : self.status_code,
                    'status-desc'      : status_desc,
                    'deviceid'         : self.raw_deviceID,
                    'panel'            : 0,
                    'panel-line1'      : '',
                    'panel-line2'      : '',
                    'device-state'     : self.device_state,
                    'error-state'      : self.error_state,
                    })


            log.debug("Fax activity check...")

            tx_active, rx_active = status.getFaxStatus(self)

            if tx_active:
                self.status_code = STATUS_FAX_TX_ACTIVE
            elif rx_active:
                self.status_code = STATUS_FAX_RX_ACTIVE

            self.error_state = STATUS_TO_ERROR_STATE_MAP.get(self.status_code, ERROR_STATE_CLEAR)
            self.error_code = self.status_code
            self.sendEvent(self.error_code)

            try:
                self.dq.update({'status-desc' : self.queryString(self.status_code),
                                'error-state' : self.error_state,
                                })

            except (KeyError, Error):
                self.dq.update({'status-desc' : '',
                                'error-state' : ERROR_STATE_CLEAR,
                                })


            if self.panel_check:
                self.panel_check = bool(self.mq.get('panel-check-type', 0))

            status_type = self.mq.get('status-type', STATUS_TYPE_NONE)
            if self.panel_check and \
                status_type in (STATUS_TYPE_LJ, STATUS_TYPE_S, STATUS_TYPE_VSTATUS) and \
                io_mode != IO_MODE_UNI:

                log.debug("Panel check...")
                try:
                    self.panel_check, line1, line2 = status.PanelCheck(self)
                finally:
                    self.closePML()

                self.dq.update({'panel': int(self.panel_check),
                                  'panel-line1': line1,
                                  'panel-line2': line2,})

            if not quick and reread_cups_printers:
                self.updateCUPSPrinters()

        for d in self.dq:
            self.__dict__[d.replace('-','_')] = self.dq[d]

        self.last_event = Event(self.device_uri, '', self.status_code, prop.username, 0, '', time.time())

        log.debug(self.dq)



    def updateCUPSPrinters(self):
        self.cups_printers = []
        log.debug("Re-reading CUPS printer queue information.")
        printers = cups.getPrinters()
        for p in printers:
            if self.device_uri == p.device_uri:
                self.cups_printers.append(p.name)
                self.state = p.state # ?

                if self.io_state == IO_STATE_NON_HP:
                    self.model = p.makemodel.split(',')[0]

        self.dq.update({'cups-printers' : self.cups_printers})

        try:
            self.first_cups_printer = self.cups_printers[0]
        except IndexError:
            self.first_cups_printer = ''




    def queryDevice(self, quick=False, reread_cups_printers=False):
        if not self.supported:
            self.dq = {}

            self.last_event = Event(self.device_uri, '', STATUS_DEVICE_UNSUPPORTED,
                prop.username, 0, '', time.time())

            return

        if self.device_type == DEVICE_TYPE_FAX:
            return self.__queryFax(quick, reread_cups_printers)

        r_type = self.mq.get('r-type', 0)
        tech_type = self.mq.get('tech-type', TECH_TYPE_NONE)
        status_type = self.mq.get('status-type', STATUS_TYPE_NONE)
        battery_check = self.mq.get('status-battery-check', STATUS_BATTERY_CHECK_NONE)
        dynamic_counters = self.mq.get('status-dynamic-counters', STATUS_DYNAMIC_COUNTERS_NONE)
        io_mode = self.mq.get('io-mode', IO_MODE_UNI)
        io_mfp_mode = self.mq.get('io-mfp-mode', IO_MODE_UNI)
        status_code = STATUS_UNKNOWN

        # Turn off status if local connection and bi-di not avail.
        #if io_mode  == IO_MODE_UNI and self.back_end != 'net':
        #    status_type = STATUS_TYPE_NONE

        agents = []

        if self.device_state != DEVICE_STATE_NOT_FOUND:
            if self.tech_type in (TECH_TYPE_MONO_INK, TECH_TYPE_COLOR_INK):
                try:
                    self.getDeviceID()
                except Error as e:
                    log.error("Error getting device ID.")
                    self.last_event = Event(self.device_uri, '', ERROR_DEVICE_IO_ERROR,
                        prop.username, 0, '', time.time())

                    raise Error(ERROR_DEVICE_IO_ERROR)

            status_desc = self.queryString(self.status_code)

            self.dq.update({
                'serial'           : self.serial,
                'cups-printers'    : self.cups_printers,
                'status-code'      : self.status_code,
                'status-desc'      : status_desc,
                'deviceid'         : self.raw_deviceID,
                'panel'            : 0,
                'panel-line1'      : '',
                'panel-line2'      : '',
                'device-state'     : self.device_state,
                'error-state'      : self.error_state,
                })

            status_block = {}

            if status_type == STATUS_TYPE_NONE:
                log.warn("No status available for device.")
                status_block = {'status-code' : STATUS_UNKNOWN}

            elif status_type in (STATUS_TYPE_VSTATUS, STATUS_TYPE_S):
                log.debug("Type 1/2 (S: or VSTATUS:) status")
                status_block = status.parseStatus(self.deviceID)

            elif status_type in (STATUS_TYPE_LJ, STATUS_TYPE_PML_AND_PJL):
                log.debug("Type 3/9 LaserJet PML(+PJL) status")
                status_block = status.StatusType3(self, self.deviceID)

            elif status_type == STATUS_TYPE_LJ_XML:
                log.debug("Type 6: LJ XML")
                status_block = status.StatusType6(self)

            elif status_type == STATUS_TYPE_PJL:
                log.debug("Type 8: LJ PJL")
                status_block = status.StatusType8(self)

            elif status_type == STATUS_TYPE_LEDM:
                log.debug("Type 10: LEDM")
                status_block = status.StatusType10(self.getEWSUrl_LEDM)

            elif status_type == STATUS_TYPE_LEDM_FF_CC_0:
                log.debug("Type 11: LEDM_FF_CC_0")
                status_block = status.StatusType10(self.getUrl_LEDM)

            elif status_type == STATUS_TYPE_IPP:
                log.debug("Type 12: IPP")
                status_block = status.StatusTypeIPP(self.device_uri)

            else:
                log.error("Unimplemented status type: %d" % status_type)

            if battery_check and \
                io_mode != IO_MODE_UNI:

                log.debug("Battery check...")
                status.BatteryCheck(self, status_block, battery_check)

            if status_block:
                log.debug(status_block)
                self.dq.update(status_block)
                try:
                    status_block['agents']
                except KeyError:
                    pass
                else:
                    agents = status_block['agents']
                    del self.dq['agents']


            status_code = self.dq.get('status-code', STATUS_UNKNOWN)

            self.error_state = STATUS_TO_ERROR_STATE_MAP.get(status_code, ERROR_STATE_CLEAR)
            self.error_code = status_code
            self.sendEvent(self.error_code)

            try:
                self.dq.update({'status-desc' : self.queryString(status_code),
                                'error-state' : self.error_state,
                                })

            except (KeyError, Error):
                self.dq.update({'status-desc' : '',
                                'error-state' : ERROR_STATE_CLEAR,
                                })

            r_value = 0

            if not quick and status_type != STATUS_TYPE_NONE:
                if self.panel_check:
                    self.panel_check = bool(self.mq.get('panel-check-type', 0))

                if self.panel_check and \
                    status_type in (STATUS_TYPE_LJ, STATUS_TYPE_S, STATUS_TYPE_VSTATUS) and \
                    io_mode != IO_MODE_UNI:

                    log.debug("Panel check...")
                    try:
                        self.panel_check, line1, line2 = status.PanelCheck(self)
                    finally:
                        self.closePML()

                    self.dq.update({'panel': int(self.panel_check),
                                      'panel-line1': line1,
                                      'panel-line2': line2,})


                if dynamic_counters != STATUS_DYNAMIC_COUNTERS_NONE and \
                    io_mode != IO_MODE_UNI:

                    r_value, r_value_str, rg, rr = self.getRValues(r_type, status_type, dynamic_counters)
                else:
                    r_value, r_value_str, rg, rr = 0, '000000000', '000', '000000'

                self.dq.update({'r'  : r_value,
                                'rs' : r_value_str,
                                'rg' : rg,
                                'rr' : rr,
                              })

            if not quick and reread_cups_printers:
                self.updateCUPSPrinters()

            if not quick:
                # Make sure there is some valid agent data for this r_value
                # If not, fall back to r_value == 0
                if r_value > 0 and self.mq.get('r%d-agent1-kind' % r_value, 0) == 0:
                    r_value = 0
                    self.dq.update({'r'  : r_value,
                                    'rs' : r_value_str,
                                    'rg' : rg,
                                    'rr' : rr,
                                  })

                #Check if device itself is sending the supplies info. If so, then in that case we need not check model.dat static data and
                #compare with region, kind and type values.
                dynamic_sku_data = False
                for agent in agents:
                    try:
                        if agent['agent-sku'] != '':
                            dynamic_sku_data = True
                            break
                    except:
                        pass

                a, aa = 1, 1
                while True:
                    if dynamic_sku_data:
                        if a > len(agents):
                            break
                        agent = agents[a-1]
                        mq_agent_sku = agent['agent-sku']
                        agent_kind = agent['kind']
                        agent_type = agent['type']
                        found = True
                    else:
                        mq_agent_kind = self.mq.get('r%d-agent%d-kind' % (r_value, a), -1)
                        if mq_agent_kind == -1:
                            break
                        mq_agent_type = self.mq.get('r%d-agent%d-type' % (r_value, a), 0)
                        mq_agent_sku = self.mq.get('r%d-agent%d-sku' % (r_value, a), '')
                        found = False

                        log.debug("Looking for kind=%d, type=%d..." % (mq_agent_kind, mq_agent_type))
                        for agent in agents:
                            agent_kind = agent['kind']
                            agent_type = agent['type']

                            if agent_kind == mq_agent_kind and \
                               agent_type == mq_agent_type:
                                   found = True
                                   break

                    if found:
                        log.debug("found: r%d-kind%d-type%d" % (r_value, agent_kind, agent_type))

                        agent_health = agent.get('health', AGENT_HEALTH_OK)
                        agent_level = agent.get('level', 100)
                        agent_level_trigger = agent.get('level-trigger',
                            AGENT_LEVEL_TRIGGER_SUFFICIENT_0)

                        log.debug("health=%d, level=%d, level_trigger=%d, status_code=%d" %
                            (agent_health, agent_level, agent_level_trigger, status_code))

                        query = 'agent_%s_%s' % (AGENT_types.get(agent_type, 'unknown'),
                                                 AGENT_kinds.get(agent_kind, 'unknown'))

                        agent_desc = self.queryString(query)
                        query = 'agent_health_ok'

                        # If printer is not in an error state, and
                        # if agent health is OK, check for low supplies. If low, use
                        # the agent level trigger description for the agent description.
                        # Otherwise, report the agent health.
                        if (status_code == STATUS_PRINTER_POWER_SAVE or status_code == STATUS_PRINTER_IDLE or status_code == STATUS_PRINTER_OUT_OF_INK) and \
                            (agent_health == AGENT_HEALTH_OK or
                             (agent_health == AGENT_HEALTH_FAIR_MODERATE and agent_kind == AGENT_KIND_HEAD)) and \
                            agent_level_trigger >= AGENT_LEVEL_TRIGGER_MAY_BE_LOW:

                            query = 'agent_level_%s' % AGENT_levels.get(agent_level_trigger, 'unknown')

                            if tech_type in (TECH_TYPE_MONO_INK, TECH_TYPE_COLOR_INK):
                                code = agent_type + STATUS_PRINTER_LOW_INK_BASE
                            else:
                                code = agent_type + STATUS_PRINTER_LOW_TONER_BASE

                            self.dq['status-code'] = code
                            self.dq['status-desc'] = self.queryString(code)

                            self.dq['error-state'] = STATUS_TO_ERROR_STATE_MAP.get(code, ERROR_STATE_LOW_SUPPLIES)
                            self.error_code = code
                            self.sendEvent(self.error_code)

                            if agent_level_trigger in \
                                (AGENT_LEVEL_TRIGGER_PROBABLY_OUT, AGENT_LEVEL_TRIGGER_ALMOST_DEFINITELY_OUT):

                                query = 'agent_level_out'
                            else:
                                query = 'agent_level_low'

                            agent_health_desc = self.queryString(query)

                            self.dq.update(
                            {
                                'agent%d-kind' % aa :          agent_kind,
                                'agent%d-type' % aa :          agent_type,
                                'agent%d-known' % aa :         agent.get('known', False),
                                'agent%d-sku' % aa :           mq_agent_sku,
                                'agent%d-level' % aa :         agent_level,
                                'agent%d-level-trigger' % aa : agent_level_trigger,
                                'agent%d-ack' % aa :           agent.get('ack', False),
                                'agent%d-hp-ink' % aa :        agent.get('hp-ink', False),
                                'agent%d-health' % aa :        agent_health,
                                'agent%d-dvc' % aa :           agent.get('dvc', 0),
                                'agent%d-virgin' % aa :        agent.get('virgin', False),
                                'agent%d-desc' % aa :          agent_desc,
                                'agent%d-id' % aa :            agent.get('id', 0),
                                'agent%d-health-desc' % aa :   agent_health_desc,
                            })

                        else:
                            query = 'agent_health_%s' % AGENT_healths.get(agent_health, AGENT_HEALTH_OK)
                            agent_health_desc = self.queryString(query)

                            self.dq.update(
                            {
                                'agent%d-kind' % aa :          agent_kind,
                                'agent%d-type' % aa :          agent_type,
                                'agent%d-known' % aa :         False,
                                'agent%d-sku' % aa :           mq_agent_sku,
                                'agent%d-level' % aa :         agent_level,
                                'agent%d-level-trigger' % aa : agent_level_trigger,
                                'agent%d-ack' % aa :           False,
                                'agent%d-hp-ink' % aa :        False,
                                'agent%d-health' % aa :        agent_health,
                                'agent%d-dvc' % aa :           0,
                                'agent%d-virgin' % aa :        False,
                                'agent%d-desc' % aa :          agent_desc,
                                'agent%d-id' % aa :            0,
                                'agent%d-health-desc' % aa :   agent_health_desc,
                            })

                        aa += 1

                    else:
                        log.debug("Not found: %d" % a)

                    a += 1

        else: # Create agent keys for not-found devices

            r_value = 0
            if r_type > 0 and self.r_values is not None:
                r_value = self.r_values[0]

            # Make sure there is some valid agent data for this r_value
            # If not, fall back to r_value == 0
            if r_value > 0 and self.mq.get('r%d-agent1-kind', 0) == 0:
                r_value = 0

            a = 1
            while True:
                mq_agent_kind = self.mq.get('r%d-agent%d-kind' % (r_value, a), 0)

                if mq_agent_kind == 0:
                    break

                mq_agent_type = self.mq.get('r%d-agent%d-type' % (r_value, a), 0)
                mq_agent_sku = self.mq.get('r%d-agent%d-sku' % (r_value, a), '')
                query = 'agent_%s_%s' % (AGENT_types.get(mq_agent_type, 'unknown'),
                                         AGENT_kinds.get(mq_agent_kind, 'unknown'))

                agent_desc = self.queryString(query)

                self.dq.update(
                {
                    'agent%d-kind' % a :          mq_agent_kind,
                    'agent%d-type' % a :          mq_agent_type,
                    'agent%d-known' % a :         False,
                    'agent%d-sku' % a :           mq_agent_sku,
                    'agent%d-level' % a :         0,
                    'agent%d-level-trigger' % a : AGENT_LEVEL_TRIGGER_ALMOST_DEFINITELY_OUT,
                    'agent%d-ack' % a :           False,
                    'agent%d-hp-ink' % a :        False,
                    'agent%d-health' % a :        AGENT_HEALTH_MISINSTALLED,
                    'agent%d-dvc' % a :           0,
                    'agent%d-virgin' % a :        False,
                    'agent%d-health-desc' % a :   self.queryString('agent_health_unknown'),
                    'agent%d-desc' % a :          agent_desc,
                    'agent%d-id' % a :            0,
                })

                a += 1

        for d in self.dq:
            self.__dict__[d.replace('-','_')] = self.dq[d]

        self.last_event = Event(self.device_uri, '', status_code, prop.username, 0, '', time.time())
        log.debug(self.dq)


    def isBusyOrInErrorState(self):
        try:
            self.queryDevice(quick=True)
        except Error:
            return True
        return self.error_state in (ERROR_STATE_ERROR, ERROR_STATE_BUSY)


    def isIdleAndNoError(self):
        try:
            self.queryDevice(quick=True)
        except Error:
            return False
        return self.error_state not in (ERROR_STATE_ERROR, ERROR_STATE_BUSY)


    def getPML(self, oid, desired_int_size=pml.INT_SIZE_INT): # oid => ( 'dotted oid value', pml type )
        channel_id = self.openPML()
        result_code, data, typ, pml_result_code = \
            hpmudext.get_pml(self.device_id, channel_id, pml.PMLToSNMP(oid[0]), oid[1])
        if pml_result_code > pml.ERROR_MAX_OK:
            log.debug("PML/SNMP GET %s failed (result code = 0x%x)" % (oid[0], pml_result_code))
            return pml_result_code, None

        converted_data = pml.ConvertFromPMLDataFormat(data, oid[1], desired_int_size)

        if log.is_debug():
            if oid[1] in (pml.TYPE_STRING, pml.TYPE_BINARY):

                log.debug("PML/SNMP GET %s (result code = 0x%x) returned:" %
                    (oid[0], pml_result_code))
                log.log_data(data)
            else:
                log.debug("PML/SNMP GET %s (result code = 0x%x) returned: %s" %
                    (oid[0], pml_result_code, repr(converted_data)))
        return pml_result_code, converted_data


    def setPML(self, oid, value): # oid => ( 'dotted oid value', pml type )
        channel_id = self.openPML()
        value = pml.ConvertToPMLDataFormat(value, oid[1])
        result_code, pml_result_code = \
            hpmudext.set_pml(self.device_id, channel_id, pml.PMLToSNMP(oid[0]), oid[1], value)

        if log.is_debug():
            if oid[1] in (pml.TYPE_STRING, pml.TYPE_BINARY):

                log.debug("PML/SNMP SET %s (result code = 0x%x) to:" %
                    (oid[0], pml_result_code))
            else:
                log.debug("PML/SNMP SET %s (result code = 0x%x) to: %s" %
                    (oid[0], pml_result_code, repr(value.decode('utf-8'))))

        return pml_result_code


    def getDynamicCounter(self, counter, convert_to_int=True):
        dynamic_counters = self.mq.get('status-dynamic-counters', STATUS_DYNAMIC_COUNTERS_NONE)
        log.debug("Dynamic counters: %d" % dynamic_counters)
        if dynamic_counters != STATUS_DYNAMIC_COUNTERS_NONE:

            if dynamic_counters == STATUS_DYNAMIC_COUNTERS_LIDIL_0_5_4:
                self.printData(ldl.buildResetPacket(), direct=True)
                self.printData(ldl.buildDynamicCountersPacket(counter), direct=True)
            else:
                self.printData(pcl.buildDynamicCounter(counter), direct=True)

            value, tries, times_seen, sleepy_time, max_tries = 0, 0, 0, 0.1, 5
            time.sleep(0.1)

            while True:

                if self.callback:
                    self.callback()

                sleepy_time += 0.1
                tries += 1

                time.sleep(sleepy_time)

                self.getDeviceID()

                if 'CTR' in self.deviceID and \
                    pat_dynamic_ctr.search(self.raw_deviceID) is not None:
                    dev_counter, value = parseDynamicCounter(self.deviceID['CTR'], convert_to_int)

                    if counter == dev_counter:
                        self.printData(pcl.buildDynamicCounter(0), direct=True)
                        # protect the value as a string during msg handling
                        if not convert_to_int:
                            value = '#' + value
                        return value

                if tries > max_tries:
                    if dynamic_counters == STATUS_DYNAMIC_COUNTERS_LIDIL_0_5_4:
                        self.printData(ldl.buildResetPacket())
                        self.printData(ldl.buildDynamicCountersPacket(counter), direct=True)
                    else:
                        self.printData(pcl.buildDynamicCounter(0), direct=True)

                    return None

                if dynamic_counters == STATUS_DYNAMIC_COUNTERS_LIDIL_0_5_4:
                    self.printData(ldl.buildResetPacket())
                    self.printData(ldl.buildDynamicCountersPacket(counter), direct=True)
                else:
                    self.printData(pcl.buildDynamicCounter(counter), direct=True)

        else:
            raise Error(ERROR_DEVICE_DOES_NOT_SUPPORT_OPERATION)


    def readPrint(self, bytes_to_read, stream=None, timeout=prop.read_timeout, allow_short_read=False):
        return self.__readChannel(self.openPrint, bytes_to_read, stream, timeout, allow_short_read)

    def readPCard(self, bytes_to_read, stream=None, timeout=prop.read_timeout, allow_short_read=False):
        return self.__readChannel(self.openPCard, bytes_to_read, stream, timeout, allow_short_read)

    def readFax(self, bytes_to_read, stream=None, timeout=prop.read_timeout, allow_short_read=False):
        return self.__readChannel(self.openFax, bytes_to_read, stream, timeout, allow_short_read)

    def readCfgUpload(self, bytes_to_read, stream=None, timeout=prop.read_timeout, allow_short_read=False):
        return self.__readChannel(self.openCfgUpload, bytes_to_read, stream, timeout, allow_short_read)

    def readEWS(self, bytes_to_read, stream=None, timeout=prop.read_timeout, allow_short_read=True):
        return self.__readChannel(self.openEWS, bytes_to_read, stream, timeout, allow_short_read)

    def readEWS_LEDM(self, bytes_to_read, stream=None, timeout=prop.read_timeout, allow_short_read=True):
        return self.__readChannel(self.openEWS_LEDM, bytes_to_read, stream, timeout, allow_short_read)

    def readLEDM(self, bytes_to_read, stream=None, timeout=prop.read_timeout, allow_short_read=True):
        return self.__readChannel(self.openLEDM, bytes_to_read, stream, timeout, allow_short_read)

    def readMarvell_EWS(self, bytes_to_read, stream=None, timeout=prop.read_timeout, allow_short_read=True):
        return self.__readChannel(self.openMarvell_EWS, bytes_to_read, stream, timeout, allow_short_read)

    def readSoapFax(self, bytes_to_read, stream=None, timeout=prop.read_timeout, allow_short_read=True):
        return self.__readChannel(self.openSoapFax, bytes_to_read, stream, timeout, allow_short_read)

    def readMarvellFax(self, bytes_to_read, stream=None, timeout=prop.read_timeout, allow_short_read=True):
        return self.__readChannel(self.openMarvellFax, bytes_to_read, stream, timeout, allow_short_read)

    def readWifiConfig(self, bytes_to_read, stream=None, timeout=prop.read_timeout, allow_short_read=True):
        return self.__readChannel(self.openWifiConfig, bytes_to_read, stream, timeout, allow_short_read)

#Common handling of reading chunked or unchunked data from LEDM devices
    def readLEDMData(dev, func, reply, timeout=6):

        END_OF_DATA=to_bytes_utf8("0\r\n\r\n")
        bytes_requested = 1024
        bytes_remaining = 0
        chunkedFlag = True

        bytes_read = func(bytes_requested, reply, timeout)

        for line in reply.getvalue().splitlines():
            if line.lower().find(to_bytes_utf8("content-length")) != -1:
                 bytes_remaining = int(line.split(to_bytes_utf8(":"))[1])
                 chunkedFlag = False
                 break

        xml_data_start = reply.getvalue().find(to_bytes_utf8("<?xml"))
        if (xml_data_start != -1):
            bytes_remaining = bytes_remaining - (len(reply.getvalue())  - xml_data_start)

        while bytes_read > 0:
            temp_buf = xStringIO()
            bytes_read = func(bytes_requested, temp_buf, timeout)

            reply.write(temp_buf.getvalue())

            if not chunkedFlag:     # Unchunked data
                bytes_remaining = bytes_remaining - bytes_read
                if bytes_remaining <= 0:
                    break
            elif END_OF_DATA == temp_buf.getvalue():   # Chunked data end
                    break



    def __readChannel(self, opener, bytes_to_read, stream=None,
                      timeout=prop.read_timeout, allow_short_read=False):

        channel_id = opener()

        log.debug("Reading channel %d (device-id=%d, bytes_to_read=%d, allow_short=%s, timeout=%d)..." %
            (channel_id, self.device_id, bytes_to_read, allow_short_read, timeout))

        num_bytes = 0

        if stream is None:
            buffer = to_bytes_utf8('')

        while True:
            result_code, data = \
                hpmudext.read_channel(self.device_id, channel_id, bytes_to_read, timeout)

            log.debug("Result code=%d" % result_code)

            l = len(data)

            if result_code == hpmudext.HPMUD_R_IO_TIMEOUT:
                log.debug("I/O timeout")
                break

            if result_code != hpmudext.HPMUD_R_OK:
                log.error("Channel read error")
                raise Error(ERROR_DEVICE_IO_ERROR)

            if not l:
                log.debug("End of data")
                break

            if stream is None:
                buffer = to_bytes_utf8('').join([buffer, data])
            else:
                stream.write(data)

            num_bytes += l

            if self.callback is not None:
                self.callback()

            if num_bytes == bytes_to_read:
                log.debug("Full read complete.")
                break

            if allow_short_read and num_bytes < bytes_to_read:
                log.debug("Allowed short read of %d of %d bytes complete." % (num_bytes, bytes_to_read))
                break

        if stream is None:
            log.debug("Returned %d total bytes in buffer." % num_bytes)
            return buffer
        else:
            log.debug("Saved %d total bytes to stream." % num_bytes)
            return num_bytes


    def writePrint(self, data):
        return self.__writeChannel(self.openPrint, data)

    def writePCard(self, data):
        return self.__writeChannel(self.openPCard, data)

    def writeFax(self, data):
        return self.__writeChannel(self.openFax, data)

    def writeEWS(self, data):
        return self.__writeChannel(self.openEWS, data)

    def writeEWS_LEDM(self, data):
        return self.__writeChannel(self.openEWS_LEDM, data)

    def writeLEDM(self, data):
        return self.__writeChannel(self.openLEDM, data)

    def writeMarvell_EWS(self, data):
        return self.__writeChannel(self.openMarvell_EWS, data)

    def writeCfgDownload(self, data):
        return self.__writeChannel(self.openCfgDownload, data)

    def writeSoapFax(self, data):
        return self.__writeChannel(self.openSoapFax, data)

    def writeMarvellFax(self, data):
        if not isinstance(data, bytes) and hasattr(data, 'tobytes'):   # hasattr function used for supporting 2.6
            data = data.tobytes()
        return self.__writeChannel(self.openMarvellFax, data)

    def writeWifiConfig(self, data):
        return self.__writeChannel(self.openWifiConfig, data)

    def __writeChannel(self, opener, data):
        channel_id = opener()
        buffer, bytes_out, total_bytes_to_write = data, 0, len(data)
        log.debug("Writing %d bytes to channel %d (device-id=%d)..." % (total_bytes_to_write, channel_id, self.device_id))

        while len(buffer) > 0:
            result_code, bytes_written = \
                hpmudext.write_channel(self.device_id, channel_id, 
                    buffer[:prop.max_message_len])
 
            log.debug("Result code=%d" % result_code)

            if result_code != hpmudext.HPMUD_R_OK:
                log.error("Channel write error")
                raise Error(ERROR_DEVICE_IO_ERROR)

            buffer = buffer[prop.max_message_len:]
            bytes_out += bytes_written

            if self.callback is not None:
                self.callback()

        if total_bytes_to_write != bytes_out:
            raise Error(ERROR_DEVICE_IO_ERROR)

        return bytes_out


    def writeEmbeddedPML(self, oid, value, style=1, direct=True):
        if style == 1:
            func = pcl.buildEmbeddedPML2
        else:
            func = pcl.buildEmbeddedPML

        data = func(pcl.buildPCLCmd('&', 'b', 'W',
                     pml.buildEmbeddedPMLSetPacket(oid[0],
                                                    value,
                                                    oid[1])))

        #log.log_data(data)

        self.printData(data, direct=direct, raw=True)

    def post(self, url, post):
        status_type = self.mq.get('status-type', STATUS_TYPE_NONE)
        data = """POST %s HTTP/1.1\r
Connection: Keep-alive\r
User-agent: hplip/2.0\r
Host: %s\r
Content-type: text/xml\r
Content-length: %d\r
\r
%s""" % (url, self.http_host, len(post), post)
        log.log_data(data)
        if status_type == STATUS_TYPE_LEDM:
            log.debug("status-type: %d" % status_type)
            self.writeEWS_LEDM(data)
            response = BytesIO()

            self.readLEDMData(self.readEWS_LEDM, response)

            response = response.getvalue()
            log.log_data(response)
            self.closeEWS_LEDM()

        elif status_type == STATUS_TYPE_LEDM_FF_CC_0:
            log.debug("status-type: %d" % status_type)
            self.writeLEDM(data)
            response = BytesIO()

            self.readLEDMData(self.readLEDM, response)

            response = response.getvalue()
            log.log_data(response)
            self.closeLEDM()

        else:
            log.error("Not an LEDM status-type: %d" % status_type)

        match = http_result_pat.match(to_string_utf8(response))
        if match is None: return HTTP_OK
        try:
            code = int(match.group(1))
        except (ValueError, TypeError):
            code = HTTP_ERROR

        return code == HTTP_OK

    def printGzipFile(self, file_name, printer_name=None, direct=False, raw=True, remove=False):
        return self.printFile(file_name, printer_name, direct, raw, remove)

    def printParsedGzipPostscript(self, print_file, printer_name=None):
        # always: direct=False, raw=False, remove=True
        try:
            os.stat(print_file)
        except OSError:
            log.error("File not found: %s" % print_file)
            return

        temp_file_fd, temp_file_name = utils.make_temp_file()
        f = gzip.open(print_file, 'r')

        x = f.readline()
        while not x.startswith(to_bytes_utf8('%PY_BEGIN')):
            os.write(temp_file_fd, x)
            x = f.readline()

        sub_lines = []
        x = f.readline()
        while not x.startswith(to_bytes_utf8('%PY_END')):
            sub_lines.append(x)
            x = f.readline()

        SUBS = {'VERSION' : prop.version,
                 'MODEL'   : self.model_ui,
                 'URI'     : self.device_uri,
                 'BUS'     : self.bus,
                 'SERIAL'  : self.serial,
                 'IP'      : self.host,
                 'PORT'    : self.port,
                 'DEVNODE' : self.dev_file,
                 }

        if self.bus == 'net' :
            SUBS['DEVNODE'] = 'n/a'
        else:
            SUBS['IP']= 'n/a'
            SUBS['PORT'] = 'n/a'
        
        if PY3:
            sub_lines = [s.decode('utf-8') for s in sub_lines]
        
            
        for s in sub_lines:
            os.write(temp_file_fd, to_bytes_utf8((s % SUBS)))
        

        os.write(temp_file_fd, f.read())
        f.close()
        os.close(temp_file_fd)

        self.printFile(temp_file_name, printer_name, direct=False, raw=False, remove=True)

    def printFile(self, file_name, printer_name=None, direct=False, raw=True, remove=False):
        is_gzip = os.path.splitext(file_name)[-1].lower() == '.gz'

        if printer_name is None:
            printer_name = self.first_cups_printer

            if not printer_name:
                raise Error(ERROR_NO_CUPS_QUEUE_FOUND_FOR_DEVICE)

        log.debug("Printing file '%s' to queue '%s' (gzip=%s, direct=%s, raw=%s, remove=%s)" %
                   (file_name, printer_name, is_gzip, direct, raw, remove))

        if direct: # implies raw==True
            if is_gzip:
                self.writePrint(gzip.open(file_name, 'r').read())
            else:
                self.writePrint(open(file_name, 'r').read())

        else:
            if not utils.which('lpr'):
                lp_opt = ''

                if raw:
                    lp_opt = '-oraw'

                if is_gzip:
                    c = 'gunzip -c %s | lp -c -d%s %s' % (file_name, printer_name, lp_opt)
                else:
                    c = 'lp -c -d%s %s %s' % (printer_name, lp_opt, file_name)

                exit_code = os_utils.execute(c)

                if exit_code != 0:
                    log.error("Print command failed with exit code %d!" % exit_code)

                if remove:
                    os.remove(file_name)

            else:
                raw_str, rem_str = '', ''
                if raw: raw_str = '-o raw'
                if remove: rem_str = '-r'

                if is_gzip:
                    c = 'gunzip -c %s | lpr %s %s -P%s' % (file_name, raw_str, rem_str, printer_name)
                else:
                    c = 'lpr -P%s %s %s %s' % (printer_name, raw_str, rem_str, file_name)

                exit_code = os_utils.execute(c)

                if exit_code != 0:
                    log.error("Print command failed with exit code %d!" % exit_code)


    def printTestPage(self, printer_name=None):
        return self.printParsedGzipPostscript(os.path.join( prop.home_dir, 'data',
                                              'ps', 'testpage.ps.gz' ), printer_name)


    def printData(self, data, printer_name=None, direct=True, raw=True):
        if direct:
            self.writePrint(data)
        else:
            temp_file_fd, temp_file_name = utils.make_temp_file()
            os.write(temp_file_fd, data)
            os.close(temp_file_fd)

            self.printFile(temp_file_name, printer_name, False, raw, remove=True)


    def cancelJob(self, jobid):
        cups.cancelJob(jobid)
        self.error_code = STATUS_PRINTER_CANCELING
        self.sendEvent(self.error_code)


    def queryHistory(self):
        result = []

        if self.dbus_avail:
            try:
                device_uri, history = self.service.GetHistory(self.device_uri)
            except dbus.exceptions.DBusException as e:
                log.error("dbus call to GetHistory() failed.")
                return []

            history.reverse()

            for h in history:
                result.append(Event(*tuple(h)))

            try:
                self.error_code = result[0].event_code
            except IndexError:
                self.error_code = STATUS_UNKNOWN

            self.error_state = STATUS_TO_ERROR_STATE_MAP.get(self.error_code, ERROR_STATE_CLEAR)

        else:
            self.error_code = STATUS_UNKNOWN
            self.error_state = ERROR_STATE_CLEAR

        self.hist = result
        return result

    def getEWSUrl(self, url, stream):
        try:
            if self.is_local:
                url2 = "%s&loc=%s" % (self.device_uri.replace('hpfax:', 'hp:'), url)
                data = self
            else:
                url2 = "http://%s%s" % (self.host, url)
                if self.zc:
                    status, ip = hpmudext.get_zc_ip_address(self.zc)
                    if status == hpmudext.HPMUD_R_OK:
                        url2 = "http://%s%s" % (ip, url)
                data = None

            log.debug("Opening: %s" % url2)
            opener = LocalOpener({})
            try:
                f = opener.open(url2, data)
                
            except Error:
                log.error("Status read failed: %s" % url2)
                stream.seek(0)
                stream.truncate()
            else:
                try:
                    stream.write(f.fp.read())
                    #stream.write(f)
                finally:
                    f.close()

        finally:
            self.closeEWS()

    def getEWSUrl_LEDM(self, url, stream, footer=""):
        try:
            url2 = "%s&loc=%s" % (self.device_uri.replace('hpfax:', 'hp:'), url)
            data = self
            opener = LocalOpenerEWS_LEDM({})
            try:
                if footer:
                    return opener.open_hp(url2, data, footer)
                else:
                    return opener.open_hp(url2, data)
            except Error:
                log.debug("Status read failed: %s" % url2)
        finally:
            self.closeEWS_LEDM()

    def getUrl_LEDM(self, url, stream, footer=""):
        try:
            url2 = "%s&loc=%s" % (self.device_uri.replace('hpfax:', 'hp:'), url)
            data = self
            opener = LocalOpener_LEDM({})
            try:
                if footer:
                    return opener.open_hp(url2, data, footer)
                else:
                    return opener.open_hp(url2, data)
            except Error:
                log.debug("Status read failed: %s" % url2)

        finally:
            self.closeLEDM()

    def FetchLEDMUrl(self, url, footer=""):
        data_fp = BytesIO()
        if footer:
            data = self.getUrl_LEDM(url, data_fp, footer)
        else:
            data = self.getUrl_LEDM(url, data_fp)
        if data:
            data = data.split(to_bytes_utf8('\r\n\r\n'), 1)[1]
            if data:
                data = status.ExtractXMLData(data)
        return data

#-------------------------For LEDM SOAP PROTOCOL(FAX) Devices----------------------------------------------------------------------#

    def FetchEWS_LEDMUrl(self, url, footer=""):
        data_fp = BytesIO()
        if footer:
            data = self.getEWSUrl_LEDM(url, data_fp, footer)
        else:
            data = self.getEWSUrl_LEDM(url, data_fp)
        if data:
            data = data.split(to_bytes_utf8('\r\n\r\n'), 1)[1]
            if data:
                data = status.ExtractXMLData(data)
        return data

    def readAttributeFromXml_EWS(self, uri, attribute):
        stream = BytesIO()
        data = self.FetchEWS_LEDMUrl(uri)
        if not data:
            log.error("Unable To read the XML data from device")
            return ""
        xmlDict = utils.XMLToDictParser().parseXML(data)

        try:
            return xmlDict[attribute]
        except:
            return str("")

#---------------------------------------------------------------------------------------------------#

    def readAttributeFromXml(self,uri,attribute):
        stream = BytesIO()
        data = self.FetchLEDMUrl(uri)
        if not data:
            log.error("Unable To read the XML data from device")
            return ""
        xmlDict = utils.XMLToDictParser().parseXML(data )
        try:
            return xmlDict[attribute]
        except:
            return str("")


    def downloadFirmware(self, usb_bus_id=None, usb_device_id=None): # Note: IDs not currently used
        ok = False
        filename = os.path.join(prop.data_dir, "firmware", self.model.lower() + '.fw.gz')
        log.debug(filename)

        if os.path.exists(filename):
            log.debug("Downloading firmware file '%s'..." % filename)

            # Write to port directly (no MUD) so that HAL can enumerate the printer
            if 0: # this currently doesn't work because usblp is loaded...
            #if usb_bus_id is not None and usb_device_id is not None:
                try:
                    p = "/dev/bus/usb/%s/%s" % (usb_bus_id, usb_device_id)
                    log.debug("Writing to %s..." % p)
                    f = os.open(p, os.O_RDWR)
                    x = gzip.open(filename).read()
                    os.write(f, x)
                    os.close(f)
                    ok = True
                    log.debug("OK")
                except (OSError, IOError) as e:
                    log.error("An error occured: %s" % e)
            else:
                try:
                    self.openPrint()
                    bytes_written = self.writePrint(gzip.open(filename).read())
                    log.debug("%s bytes downloaded." % utils.commafy(bytes_written))
                    self.closePrint()
                    ok = True
                    log.debug("OK")
                except Error as e:
                    log.error("An error occured: %s" % e.msg)
        else:
            log.error("Firmware file '%s' not found." % filename)

        return ok



    


# URLs: hp:/usb/HP_LaserJet_3050?serial=00XXXXXXXXXX&loc=/hp/device/info_device_status.xml
class LocalOpener(urllib_request.URLopener):
    def open_hp(self, url, dev):
        log.debug("open_hp(%s)" % url)

        match_obj = http_pat_url.search(url)
        bus = match_obj.group(1) or ''
        model = match_obj.group(2) or ''
        serial = match_obj.group(3) or ''
        device = match_obj.group(4) or ''
        loc = match_obj.group(5) or ''

        dev.openEWS()
        dev.writeEWS("""GET %s HTTP/1.0\nContent-Length:0\nHost:localhost\nUser-Agent:hplip\n\n""" % loc)

        reply = xStringIO()
        while dev.readEWS(8192, reply, timeout=1):
            pass

        reply.seek(0)
        log.log_data(reply.getvalue())
        
        response = http_client.HTTPResponse(reply)
        response.begin()

        if response.status != http_client.OK:
            raise Error(ERROR_DEVICE_STATUS_NOT_AVAILABLE)
        else:
            return response#.fp

# URLs: hp:/usb/HP_OfficeJet_7500?serial=00XXXXXXXXXX&loc=/hp/device/info_device_status.xml
class LocalOpenerEWS_LEDM(urllib_request.URLopener):
    def open_hp(self, url, dev, foot=""):
        log.debug("open_hp(%s)" % url)

        match_obj = http_pat_url.search(url)
        loc = url.split("=")[url.count("=")]

        dev.openEWS_LEDM()
        if foot:
            if "PUT" in foot:
                dev.writeEWS_LEDM("""%s""" % foot)
            else:
                dev.writeEWS_LEDM("""POST %s HTTP/1.1\r\nContent-Type:text/xml\r\nContent-Length:%s\r\nAccept-Encoding: UTF-8\r\nHost:localhost\r\nUser-Agent:hplip\r\n\r\n """ % (loc, len(foot)))
                dev.writeEWS_LEDM("""%s""" % foot)
        else:
            dev.writeEWS_LEDM("""GET %s HTTP/1.1\r\nAccept: text/plain\r\nHost:localhost\r\nUser-Agent:hplip\r\n\r\n""" % loc)

        reply = xStringIO()

        dev.readLEDMData(dev.readEWS_LEDM,reply)

        reply.seek(0)
        return reply.getvalue()


# URLs: hp:/usb/HP_OfficeJet_7500?serial=00XXXXXXXXXX&loc=/hp/device/info_device_status.xml
class LocalOpener_LEDM(urllib_request.URLopener):
    def open_hp(self, url, dev, foot=""):
        log.debug("open_hp(%s)" % url)

        match_obj = http_pat_url.search(url)
        loc = url.split("=")[url.count("=")]

        dev.openLEDM()
        if foot:
            if "PUT" in foot:
                dev.writeLEDM("""%s""" % foot)
            else:
                dev.writeLEDM("""POST %s HTTP/1.1\r\nContent-Type:text/xml\r\nContent-Length:%s\r\nAccept-Encoding: UTF-8\r\nHost:localhost\r\nUser-Agent:hplip\r\n\r\n """ % (loc, len(foot)))
                dev.writeLEDM("""%s""" % foot)
        else:
            dev.writeLEDM("""GET %s HTTP/1.1\r\nAccept: text/plain\r\nHost:localhost\r\nUser-Agent:hplip\r\n\r\n""" % loc)

        reply = xStringIO()

       
        dev.readLEDMData(dev.readLEDM,reply)

        reply.seek(0)
        return reply.getvalue()

Filemanager

Name Type Size Permission Actions
__pycache__ Folder 0755
pexpect Folder 0755
LedmWifi.py File 29.18 KB 0644
__init__.py File 785 B 0644
avahi.py File 3.01 KB 0644
codes.py File 30.55 KB 0644
device.py File 95.38 KB 0644
dime.py File 3.3 KB 0644
exif.py File 35.08 KB 0644
g.py File 14.19 KB 0644
imageprocessing.py File 33.14 KB 0644
imagesize.py File 5.75 KB 0644
ldif.py File 16.33 KB 0644
logger.py File 18.06 KB 0644
magic.py File 63.2 KB 0644
maint.py File 58.36 KB 0644
mdns.py File 10.04 KB 0644
mfpdtf.py File 17.34 KB 0644
models.py File 19.04 KB 0644
module.py File 28.32 KB 0644
os_utils.py File 2.28 KB 0644
password.py File 12.73 KB 0644
pkit.py File 11.45 KB 0644
pml.py File 26.46 KB 0644
queues.py File 16.35 KB 0644
services.py File 9.74 KB 0644
six.py File 22.32 KB 0644
sixext.py File 5.78 KB 0644
slp.py File 5.8 KB 0644
smart_install.py File 11.37 KB 0644
status.py File 76.35 KB 0644
strings.py File 28.21 KB 0644
tui.py File 13.79 KB 0644
utils.py File 75.97 KB 0644
validation.py File 3.68 KB 0644
vcard.py File 43.66 KB 0644
wifi.py File 22.25 KB 0644