1#!/usr/local/bin/python3.8
2# -*- coding: utf-8 -*-
3#
4# (c) Copyright 2003-2015 HP Development Company, L.P.
5#
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 2 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19#
20# Author: Don Welch, Naga Samrat Chowdary Narla
21#
22
23# Std Lib
24import socket
25import re
26import gzip
27import os.path
28import time
29from .sixext.moves import urllib_request, urllib_parse, urllib_error
30import io
31from io import BytesIO
32from .sixext.moves import http_client
33import struct
34import string
35import time
36# Local
37from .g import *
38from .codes import *
39from . import utils
40from . import services
41from . import os_utils
42from . import status
43from . import pml
44from . import status
45from prnt import pcl, ldl, cups
46from . import models, mdns, slp, avahi
47from .strings import *
48from .sixext import PY3, to_bytes_utf8, to_unicode, to_string_latin, to_string_utf8, xStringIO
49
50http_result_pat = re.compile("""HTTP/\d.\d\s(\d+)""", re.I)
51
52HTTP_OK = 200
53HTTP_ERROR = 500
54
55try:
56    import hpmudext
57except ImportError:
58    if not os.getenv("HPLIP_BUILD"):
59        log.error("HPMUDEXT could not be loaded. Please check HPLIP installation.")
60        sys.exit(1)
61else:
62    # Workaround for build machine
63    try:
64        MAX_BUFFER = hpmudext.HPMUD_BUFFER_SIZE
65    except AttributeError:
66        MAX_BUFFER = 8192
67
68dbus_avail = False
69dbus_disabled = False
70try:
71    import dbus
72    from dbus import lowlevel, SessionBus
73    dbus_avail = True
74except ImportError:
75    log.warn("python-dbus not installed.")
76
77import warnings
78# Ignore: .../dbus/connection.py:242: DeprecationWarning: object.__init__() takes no parameters
79# (occurring on Python 2.6/dBus 0.83/Ubuntu 9.04)
80warnings.simplefilter("ignore", DeprecationWarning)
81
82
83DEFAULT_PROBE_BUS = ['usb', 'par', 'cups']
84VALID_BUSES = ('par', 'net', 'cups', 'usb') #, 'bt', 'fw')
85VALID_BUSES_WO_CUPS = ('par', 'net', 'usb')
86DEFAULT_FILTER = None
87VALID_FILTERS = ('print', 'scan', 'fax', 'pcard', 'copy')
88DEFAULT_BE_FILTER = ('hp',)
89
90pat_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)
91http_pat_url = re.compile(r"""/(.*?)/(\S*?)\?(?:serial=(\S*)|device=(\S*))&loc=(\S*)""", re.IGNORECASE)
92direct_pat = re.compile(r'direct (.*?) "(.*?)" "(.*?)" "(.*?)"', re.IGNORECASE)
93
94# Pattern to check for ; at end of CTR fields
95# Note: If ; not present, CTR value is invalid
96pat_dynamic_ctr = re.compile(r"""CTR:\d*\s.*;""", re.IGNORECASE)
97
98# Cache for model data
99model_dat = models.ModelData()
100
101ip_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)
102dev_pat = re.compile(r"""/dev/.+""", re.IGNORECASE)
103usb_pat = re.compile(r"""(\d+):(\d+)""", re.IGNORECASE)
104
105
106class Event(object):
107    def __init__(self, device_uri, printer_name, event_code,
108                 username=prop.username, job_id=0, title='',
109                 timedate=0):
110
111        self.device_uri = to_unicode(device_uri)
112        self.printer_name = to_unicode(printer_name)
113        self.event_code = int(event_code)
114        self.username = to_unicode(username)
115        self.job_id = int(job_id)
116        self.title = to_unicode(title)
117
118        if timedate:
119            self.timedate = float(timedate)
120        else:
121            self.timedate = time.time()
122
123        self.pipe_fmt = "80s80sI32sI80sf"
124        self.dbus_fmt = "ssisisd"
125
126
127    def debug(self):
128        log.debug("    device_uri=%s" % self.device_uri)
129        log.debug("    printer_name=%s" % self.printer_name)
130        log.debug("    event_code=%d" % self.event_code)
131        log.debug("    username=%s" % self.username)
132        log.debug("    job_id=%d" % self.job_id)
133        log.debug("    title=%s" % self.title)
134        log.debug("    timedate=%s" % self.timedate)
135
136
137    def pack_for_pipe(self):
138        return struct.pack(self.pipe_fmt, self.device_uri.encode('utf-8'), self.printer_name.encode('utf-8'),
139                self.event_code, self.username.encode('utf-8'), self.job_id, self.title.encode('utf-8'),
140                self.timedate)
141
142
143    def send_via_pipe(self, fd, recipient='hpssd'):
144        if fd is not None:
145            log.debug("Sending event %d to %s (via pipe %d)..." % (self.event_code, recipient, fd))
146            try:
147                os.write(fd, self.pack_for_pipe())
148                return True
149            except OSError:
150                log.debug("Failed.")
151                return False
152
153
154    def send_via_dbus(self, session_bus, interface='com.hplip.StatusService'):
155        if session_bus is not None and dbus_avail:
156            log.debug("Sending event %d to %s (via dbus)..." % (self.event_code, interface))
157            msg = lowlevel.SignalMessage('/', interface, 'Event')
158            msg.append(signature=self.dbus_fmt, *self.as_tuple())
159            session_bus.send_message(msg)
160
161
162    def copy(self):
163        return Event(*self.as_tuple())
164
165
166    def __str__(self):
167        return "<Event('%s', '%s', %d, '%s', %d, '%s', %f)>" % self.as_tuple()
168
169
170    def as_tuple(self):
171        return (self.device_uri, self.printer_name, self.event_code,
172             self.username, self.job_id, self.title, self.timedate)
173
174
175class FaxEvent(Event):
176    def __init__(self, temp_file, event):
177        Event.__init__(self, *event.as_tuple())
178        self.temp_file = temp_file
179        self.pipe_fmt = "80s80sI32sI80sfs"
180        self.dbus_fmt = "ssisisfs"
181
182
183    def debug(self):
184        log.debug("FAX:")
185        Event.debug(self)
186        log.debug("    temp_file=%s" % self.temp_file)
187
188
189    def __str__(self):
190        return "<FaxEvent('%s', '%s', %d, '%s', %d, '%s', %f, '%s')>" % self.as_tuple()
191
192
193    def as_tuple(self):
194        return (self.device_uri, self.printer_name, self.event_code,
195             self.username, self.job_id, self.title, self.timedate,
196             self.temp_file)
197
198
199
200class DeviceIOEvent(Event):
201    def __init__(self, bytes_written, event):
202        Event.__init__(self, *event.as_tuple())
203        self.bytes_written = bytes_written
204        self.pipe_fmt = "80s80sI32sI80sfI"
205        self.dbus_fmt = "ssisisfi"
206
207
208    def debug(self):
209        log.debug("DEVIO:")
210        Event.debug(self)
211        log.debug("    bytes_written=%d" % self.bytes_written)
212
213
214    def __str__(self):
215        return "<DeviceIOEvent('%s', '%s', %d, '%s', %d, '%s', %f, '%d')>" % self.as_tuple()
216
217
218    def as_tuple(self):
219        return (self.device_uri, self.printer_name, self.event_code,
220             self.username, self.job_id, self.title, self.timedate,
221             self.bytes_written)
222
223
224#
225# DBus Support
226#
227
228def init_dbus(dbus_loop=None):
229    global dbus_avail
230    service = None
231    session_bus = None
232
233    if not prop.gui_build:
234        dbus_avail = False
235        return dbus_avail, None,  None
236
237    if dbus_avail and not dbus_disabled:
238        if os.getuid() == 0:
239            log.debug("Not starting dbus: running as root.")
240            dbus_avail = False
241            return dbus_avail, None,  None
242
243        try:
244            if dbus_loop is None:
245                session_bus = dbus.SessionBus()
246            else:
247                session_bus = dbus.SessionBus(dbus_loop)
248        except dbus.exceptions.DBusException as e:
249            if os.getuid() != 0:
250                log.error("Unable to connect to dbus session bus. %s "%e)
251            else:
252                log.debug("Unable to connect to dbus session bus (running as root?). %s "%e)
253
254            dbus_avail = False
255            return dbus_avail, None,  None
256
257        try:
258            log.debug("Connecting to com.hplip.StatusService (try #1)...")
259            service = session_bus.get_object('com.hplip.StatusService', "/com/hplip/StatusService")
260            dbus_avail = True
261        except dbus.exceptions.DBusException as e:
262            try:
263                os.waitpid(-1, os.WNOHANG)
264            except OSError:
265                pass
266
267            path = utils.which('hp-systray')
268            if path:
269                path = os.path.join(path, 'hp-systray')
270            else:
271                path = os.path.join(prop.home_dir, 'systray.py')
272                if not os.path.exists(path):
273                    log.warn("Unable to start hp-systray")
274                    return False, None,  None
275
276            log.debug("Running hp-systray: %s --force-startup" % path)
277
278            os.spawnlp(os.P_NOWAIT, path, 'hp-systray', '--force-startup')
279
280            log.debug("Waiting for hp-systray to start...")
281            time.sleep(1)
282
283            t = 2
284            while True:
285                try:
286                    log.debug("Connecting to com.hplip.StatusService (try #%d)..." % t)
287                    service = session_bus.get_object('com.hplip.StatusService', "/com/hplip/StatusService")
288
289                except dbus.exceptions.DBusException as e:
290                    log.debug("Unable to connect to dbus. Is hp-systray running?")
291                    t += 1
292
293                    if t > 5:
294                        log.warn("Unable to connect to dbus. Is hp-systray running?")
295                        return False, None,  None
296
297                    time.sleep(1)
298
299                else:
300                    log.debug("Connected.")
301                    dbus_avail = True
302                    break
303
304    return dbus_avail, service,  session_bus
305
306
307#
308# Make URI from parameter (bus ID, IP address, etc)
309#
310
311def makeURI(param, port=1):
312    cups_uri, sane_uri, fax_uri = '', '', ''
313    found = False
314
315    if dev_pat.search(param) is not None: # parallel
316        log.debug("Trying parallel with %s" % param)
317
318        result_code, uri = hpmudext.make_par_uri(param)
319
320        if result_code == hpmudext.HPMUD_R_OK and uri:
321            uri = to_string_utf8(uri)
322            log.debug("Found: %s" % uri)
323            found = True
324            cups_uri = uri
325        else:
326            log.debug("Not found.")
327
328    elif usb_pat.search(param) is not None: # USB
329        match_obj = usb_pat.search(param)
330        usb_bus_id = match_obj.group(1)
331        usb_dev_id = match_obj.group(2)
332
333        log.debug("Trying USB with bus=%s dev=%s..." % (usb_bus_id, usb_dev_id))
334        result_code, uri = hpmudext.make_usb_uri(usb_bus_id, usb_dev_id)
335
336        if result_code == ERROR_SUCCESS and uri:
337            uri = to_string_utf8(uri)
338            log.debug("Found: %s" % uri)
339            found = True
340            cups_uri = uri
341        else:
342            log.debug("Not found.")
343
344    elif ip_pat.search(param) is not None: # IPv4 dotted quad
345        log.debug("Trying IP address %s" % param)
346
347        result_code, uri = hpmudext.make_net_uri(param, port)
348
349        if result_code == hpmudext.HPMUD_R_OK and uri:
350            uri = to_string_utf8(uri)
351            log.debug("Found: %s" % uri)
352            found = True
353            cups_uri = uri
354        else:
355            log.debug("Not found.")
356
357    else: # Try Zeroconf hostname
358        log.debug("Trying ZC hostname %s" % param)
359
360        result_code, uri = hpmudext.make_zc_uri(param, port)
361
362        if result_code == hpmudext.HPMUD_R_OK and uri:
363            uri = to_string_utf8(uri)
364            log.debug("Found: %s" % uri)
365            found = True
366            cups_uri = uri
367
368        else: # Try DNS hostname
369            log.debug("Device not found using mDNS hostname. Trying with DNS hostname %s" % param)
370
371            result_code, uri = hpmudext.make_net_uri(param, port)
372
373            if result_code == hpmudext.HPMUD_R_OK and uri:
374                uri = to_string_utf8(uri)
375                uri = uri.replace("ip=","hostname=")
376                log.debug("Found: %s" % uri)
377                found = True
378                cups_uri = uri
379            else:
380                log.debug("Not found.")
381
382    if not found:
383        log.debug("Trying serial number %s" % param)
384        devices = probeDevices(bus=['usb', 'par'])
385
386        for d in devices:
387            log.debug(d)
388
389            # usb has serial in URI...
390            try:
391                back_end, is_hp, bus, model, serial, dev_file, host, zc, port = \
392                    parseDeviceURI(d)
393            except Error:
394                continue
395
396            if bus == 'par': # ...parallel does not. Must get Device ID to obtain it...
397                mq = queryModelByURI(d)
398
399                result_code, device_id = \
400                    hpmudext.open_device(d, mq.get('io-mode', hpmudext.HPMUD_UNI_MODE))
401
402                if result_code == hpmudext.HPMUD_R_OK:
403                    result_code, data = hpmudext.get_device_id(device_id)
404                    serial = parseDeviceID(data).get('SN', '')
405                    hpmudext.close_device(device_id)
406
407            if serial.lower() == param.lower():
408                log.debug("Found: %s" % d)
409                found = True
410                cups_uri = d
411                break
412            else:
413                log.debug("Not found.")
414
415    if found:
416        try:
417            mq = queryModelByURI(cups_uri)
418        except Error as e:
419            log.error("Error: %s" % e.msg)
420            cups_uri, sane_uri, fax_uri = '', '', ''
421        else:
422            if mq.get('support-type', SUPPORT_TYPE_NONE) > SUPPORT_TYPE_NONE:
423                if mq.get('scan-type', 0):
424                    sane_uri = cups_uri.replace("hp:", "hpaio:")
425
426                if mq.get('fax-type', 0):
427                    fax_uri = cups_uri.replace("hp:", "hpfax:")
428
429            else:
430                cups_uri, sane_uri, fax_uri = '', '', ''
431
432    else:
433        scan_uri, fax_uri = '', ''
434
435    if cups_uri:
436        user_conf.set('last_used', 'device_uri', cups_uri)
437
438    return cups_uri, sane_uri, fax_uri
439
440
441#
442# Model Queries
443#
444
445def queryModelByModel(model):
446    model = models.normalizeModelName(model).lower()
447    return model_dat[model]
448
449
450def queryModelByURI(device_uri):
451    try:
452        back_end, is_hp, bus, model, \
453            serial, dev_file, host, zc, port = \
454            parseDeviceURI(device_uri)
455    except Error:
456        raise Error(ERROR_INVALID_DEVICE_URI)
457    else:
458        return queryModelByModel(model)
459
460
461#
462# Device Discovery
463#
464
465def probeDevices(bus=DEFAULT_PROBE_BUS, timeout=10,
466                 ttl=4, filter=DEFAULT_FILTER,  search='', net_search='slp',
467                 back_end_filter=('hp',)):
468
469    num_devices, ret_devices = 0, {}
470
471    if search:
472        try:
473            search_pat = re.compile(search, re.IGNORECASE)
474        except:
475            log.error("Invalid search pattern. Search uses standard regular expressions. For more info, see: http://www.amk.ca/python/howto/regex/")
476            search = ''
477
478    for b in bus:
479        log.debug("Probing bus: %s" % b)
480        if b not in VALID_BUSES:
481            log.error("Invalid bus: %s" % b)
482            continue
483
484        if b == 'net':
485            if net_search == 'slp':
486                try:
487                    detected_devices = slp.detectNetworkDevices(ttl, timeout)
488                except Error as socket_error:
489                    socket.error = socket_error
490                    log.error("An error occured during network probe.[%s]"%socket_error)
491                    raise ERROR_INTERNAL
492            elif net_search == 'avahi':
493                try:
494                    detected_devices = avahi.detectNetworkDevices(ttl, timeout)
495                except Error as socket_error:
496                    socket.error = socket_error
497                    log.error("An error occured during network probe.[%s]"%socket_error)
498                    raise ERROR_INTERNAL
499            else :#if net_search = 'mdns'
500                try:
501                    detected_devices = mdns.detectNetworkDevices(ttl, timeout)
502                except Error as socket_error:
503                    socket.error = socket_error
504                    log.error("An error occured during network probe.[%s]"%socket_error)
505                    raise ERROR_INTERNAL
506
507            for ip in detected_devices:
508                update_spinner()
509                hn = detected_devices[ip].get('hn', '?UNKNOWN?')
510                num_devices_on_jd = detected_devices[ip].get('num_devices', 0)
511                num_ports_on_jd = detected_devices[ip].get('num_ports', 1)
512
513                if num_devices_on_jd > 0:
514                    for port in range(num_ports_on_jd):
515                        dev = detected_devices[ip].get('device%d' % (port+1), '0')
516
517                        if dev is not None and dev != '0':
518                            device_id = parseDeviceID(dev)
519                            model = models.normalizeModelName(device_id.get('MDL', '?UNKNOWN?'))
520
521                            if num_ports_on_jd == 1:
522                                if net_search == 'slp':
523                                    device_uri = 'hp:/net/%s?ip=%s' % (model, ip)
524                                else:
525                                    device_uri = 'hp:/net/%s?zc=%s' % (model, hn)
526                            else:
527                                if net_search == 'slp':
528                                    device_uri = 'hp:/net/%s?ip=%s&port=%d' % (model, ip, (port + 1))
529                                else:
530                                    device_uri = 'hp:/net/%s?zc=%s&port=%d' % (model, hn, (port + 1))
531
532                            include = True
533                            mq = queryModelByModel(model)
534
535                            if not mq:
536                                log.debug("Not found.")
537                                include = False
538
539                            elif int(mq.get('support-type', SUPPORT_TYPE_NONE)) == SUPPORT_TYPE_NONE:
540                                log.debug("Not supported.")
541                                include = False
542
543                            elif filter not in (None, 'print', 'print-type'):
544                                include = __checkFilter(filter, mq)
545
546                            if include:
547                                ret_devices[device_uri] = (model, model, hn)
548
549        elif b in ('usb', 'par'):
550            if b == 'par':
551                bn = hpmudext.HPMUD_BUS_PARALLEL
552            else:
553                bn = hpmudext.HPMUD_BUS_USB
554
555            result_code, data = hpmudext.probe_devices(bn)
556            if result_code == hpmudext.HPMUD_R_OK:
557                for x in data.splitlines():
558                    m = direct_pat.match(x)
559
560                    uri = m.group(1) or ''
561                    mdl = m.group(2) or ''
562                    desc = m.group(3) or ''
563                    devid = m.group(4) or ''
564
565                    log.debug(uri)
566                    #if("scanjet" in  mdl.lower()):
567                    #    continue # Do not include HP Scanjets
568
569                    try:
570                        back_end, is_hp, bb, model, serial, dev_file, host, zc, port = \
571                            parseDeviceURI(uri)
572                    except Error:
573                        continue
574
575                    include = True
576
577                    if mdl and uri and is_hp:
578                        mq = queryModelByModel(model)
579
580                        if not mq:
581                            log.debug("Not found.")
582                            include = False
583
584                        elif int(mq.get('support-type', SUPPORT_TYPE_NONE)) == SUPPORT_TYPE_NONE:
585                            log.debug("Not supported.")
586                            include = False
587
588                        elif filter not in (None, 'print', 'print-type'):
589                            include = __checkFilter(filter, mq)
590
591                        if include:
592                            ret_devices[uri] = (mdl, desc, devid) # model w/ _'s, mdl w/o
593
594        elif b == 'cups':
595            cups_printers = cups.getPrinters()
596            x = len(cups_printers)
597
598            for p in cups_printers:
599                device_uri = p.device_uri
600                log.debug("%s: %s" % (device_uri, p.name))
601
602                if device_uri != '':
603                    try:
604                        back_end, is_hp, bs, model, serial, dev_file, host, zc, port = \
605                            parseDeviceURI(device_uri)
606                    except Error:
607                        log.debug("Unrecognized URI: %s" % device_uri)
608                        continue
609
610                    if not is_hp:
611                        continue
612
613                    include = True
614                    mq = queryModelByModel(model)
615
616                    if not mq:
617                        include = False
618                        log.debug("Not found.")
619
620                    elif int(mq.get('support-type', SUPPORT_TYPE_NONE)) == SUPPORT_TYPE_NONE:
621                        log.debug("Not supported.")
622                        include = False
623
624                    elif filter not in (None, 'print', 'print-type'):
625                        include = __checkFilter(filter, mq)
626
627                    if include:
628                        ret_devices[device_uri] = (model, model, '')
629
630    probed_devices = {}
631    for uri in ret_devices:
632        num_devices += 1
633        mdl, model, devid_or_hn = ret_devices[uri]
634
635        include = True
636        if search:
637            match_obj = search_pat.search("%s %s %s %s" % (mdl, model, devid_or_hn, uri))
638
639            if match_obj is None:
640                log.debug("%s %s %s %s: Does not match search '%s'." % (mdl, model, devid_or_hn, uri, search))
641                include = False
642
643        if include:
644            probed_devices[uri] = ret_devices[uri]
645
646    cleanup_spinner()
647    return probed_devices
648
649#
650# CUPS Devices
651#
652
653def getSupportedCUPSDevices(back_end_filter=['hp'], filter=DEFAULT_FILTER):
654    devices = {}
655    printers = cups.getPrinters()
656    log.debug(printers)
657
658    for p in printers:
659        try:
660            back_end, is_hp, bus, model, serial, dev_file, host, zc, port = \
661                parseDeviceURI(p.device_uri)
662
663        except Error:
664            continue
665
666        if (back_end_filter == '*' or back_end in back_end_filter or \
667            ('hpaio' in back_end_filter and back_end == 'hp')) and \
668            model and is_hp:
669
670            include = True
671            mq = queryModelByModel(model)
672
673            if not mq:
674                log.debug("Not found.")
675                include = False
676
677            elif int(mq.get('support-type', SUPPORT_TYPE_NONE)) == SUPPORT_TYPE_NONE:
678                log.debug("Not supported.")
679                include = False
680
681            elif filter not in (None, 'print', 'print-type'):
682                include = __checkFilter(filter, mq)
683
684            if include:
685                if 'hpaio' in back_end_filter:
686                    d = p.device_uri.replace('hp:', 'hpaio:')
687                else:
688                    d = p.device_uri
689
690                try:
691                    devices[d]
692                except KeyError:
693                    devices[d] = [p.name]
694                else:
695                    devices[d].append(p.name)
696
697    return devices # { 'device_uri' : [ CUPS printer list ], ... }
698
699
700def getSupportedCUPSPrinters(back_end_filter=['hp'], filter=DEFAULT_FILTER):
701    printer_list = []
702    printers = cups.getPrinters()
703
704    for p in printers:
705        try:
706            back_end, is_hp, bus, model, serial, dev_file, host, zc, port = \
707                parseDeviceURI(p.device_uri)
708
709        except Error:
710            continue
711
712        if (back_end_filter == '*' or back_end in back_end_filter) and model and is_hp:
713            include = True
714            mq = queryModelByModel(model)
715
716            if not mq:
717                log.debug("Not found.")
718                include = False
719
720            elif int(mq.get('support-type', SUPPORT_TYPE_NONE)) == SUPPORT_TYPE_NONE:
721                log.debug("Not supported.")
722                include = False
723
724            elif filter not in (None, 'print', 'print-type'):
725                include = __checkFilter(filter, mq)
726
727            if include:
728                printer_list.append(p)
729
730
731    return printer_list # [ cupsext.Printer, ... ]
732
733
734def getSupportedCUPSPrinterNames(back_end_filter=['hp'], filter=DEFAULT_FILTER):
735    printers = getSupportedCUPSPrinters(back_end_filter, filter)
736    return [p.name for p in printers]
737
738
739def getDeviceURIByPrinterName(printer_name, scan_uri_flag=False):
740    if printer_name is None:
741        return None
742
743    device_uri = None
744    printers = cups.getPrinters()
745
746    for p in printers:
747        try:
748            back_end, is_hp, bus, model, serial, dev_file, host, zc, port = \
749                parseDeviceURI(p.device_uri)
750
751        except Error:
752            continue
753
754        if is_hp and p.name == printer_name:
755            if scan_uri_flag:
756                device_uri = p.device_uri.replace('hp:', 'hpaio:')
757            else:
758                device_uri = p.device_uri
759            break
760
761    return device_uri
762
763#
764# IEEE-1284 Device ID parsing
765#
766
767def parseDeviceID(device_id):
768    d= {}
769    x = [y.strip() for y in device_id.strip().split(';') if y]
770
771    for z in x:
772        y = z.split(':')
773        try:
774            d.setdefault(y[0].strip(), y[1])
775        except IndexError:
776            d.setdefault(y[0].strip(), None)
777
778    d.setdefault('MDL', '')
779    d.setdefault('SN',  '')
780
781    if 'MODEL' in d:
782        d['MDL'] = d['MODEL']
783        del d['MODEL']
784
785    if 'SERIAL' in d:
786        d['SN'] = d['SERIAL']
787        del d['SERIAL']
788
789    elif 'SERN' in d:
790        d['SN'] = d['SERN']
791        del d['SERN']
792
793    if d['SN'].startswith('X'):
794        d['SN'] = ''
795
796    return d
797
798#
799# IEEE-1284 Device ID Dynamic Counter Parsing
800#
801
802def parseDynamicCounter(ctr_field, convert_to_int=True):
803    counter, value = ctr_field.split(' ')
804    try:
805        counter = int(utils.xlstrip(str(counter), '0') or '0')
806
807        if convert_to_int:
808            value = int(utils.xlstrip(str(value), '0') or '0')
809    except ValueError:
810        if convert_to_int:
811            counter, value = 0, 0
812        else:
813            counter, value = 0, ''
814
815    return counter, value
816
817
818#
819# Parse Device URI Strings
820#
821
822def parseDeviceURI(device_uri):
823    m = pat_deviceuri.match(device_uri)
824    if m is None:
825        log.debug("Device URI %s is invalid/unknown" % device_uri)
826        raise Error(ERROR_INVALID_DEVICE_URI)
827
828    back_end = m.group(1).lower() or ''
829    is_hp = (back_end in ('hp', 'hpfax', 'hpaio'))
830    bus = m.group(2).lower() or ''
831
832    if bus not in ('usb', 'net', 'bt', 'fw', 'par'):
833        log.debug("Device URI %s is invalid/unknown" % device_uri)
834        raise Error(ERROR_INVALID_DEVICE_URI)
835
836    model = m.group(3) or ''
837    serial = m.group(4) or ''
838    dev_file = m.group(5) or ''
839    host = m.group(6) or ''
840    zc = m.group(7) or ''
841    hostname = m.group(8) or ''
842
843    if hostname:
844        host = hostname
845    elif zc:
846        host = zc
847
848    port = m.group(8) or 1
849
850    if bus == 'net':
851        try:
852            port = int(port)
853        except (ValueError, TypeError):
854            port = 1
855
856        if port == 0:
857            port = 1
858
859    log.debug("%s: back_end:%s is_hp:%s bus:%s model:%s serial:%s dev_file:%s host:%s zc:%s port:%s" %
860        (device_uri, back_end, is_hp, bus, model, serial, dev_file, host, zc, port))
861
862    return back_end, is_hp, bus, model, serial, dev_file, host, zc, port
863
864
865def isLocal(bus):
866    return bus in ('par', 'usb', 'fw', 'bt')
867
868
869def isNetwork(bus):
870    return bus in ('net',)
871
872
873#
874# Misc
875#
876
877def __checkFilter(filter, mq):
878    for f, p in list(filter.items()):
879        if f is not None:
880            op, val = p
881            if not op(mq[f], val):
882                return False
883
884    return True
885
886
887def validateBusList(bus, allow_cups=True):
888    for b in bus:
889        if allow_cups:
890            vb = VALID_BUSES
891        else:
892            vb = VALID_BUSES_WO_CUPS
893
894        if b not in vb:
895            log.error("Invalid bus name: %s" %b)
896            return False
897
898    return True
899
900
901def validateFilterList(filter):
902    if filter is None:
903        return True
904
905    for f in filter:
906        if f not in VALID_FILTERS:
907            log.error("Invalid term '%s' in filter list" % f)
908            return False
909
910    return True
911
912
913AGENT_types = { AGENT_TYPE_NONE        : 'invalid',
914                AGENT_TYPE_BLACK       : 'black',
915                AGENT_TYPE_BLACK_B8800 : 'black',
916                AGENT_TYPE_CMY         : 'cmy',
917                AGENT_TYPE_KCM         : 'kcm',
918                AGENT_TYPE_CYAN        : 'cyan',
919                AGENT_TYPE_MAGENTA     : 'magenta',
920                AGENT_TYPE_YELLOW      : 'yellow',
921                AGENT_TYPE_CYAN_LOW    : 'photo_cyan',
922                AGENT_TYPE_MAGENTA_LOW : 'photo_magenta',
923                AGENT_TYPE_YELLOW_LOW  : 'photo_yellow',
924                AGENT_TYPE_GGK         : 'photo_gray',
925                AGENT_TYPE_BLUE        : 'photo_blue',
926                AGENT_TYPE_KCMY_CM     : 'kcmy_cm',
927                AGENT_TYPE_LC_LM       : 'photo_cyan_and_photo_magenta',
928                #AGENT_TYPE_Y_M         : 'yellow_and_magenta',
929                #AGENT_TYPE_C_K         : 'cyan_and_black',
930                AGENT_TYPE_LG_PK       : 'light_gray_and_photo_black',
931                AGENT_TYPE_LG          : 'light_gray',
932                AGENT_TYPE_G           : 'medium_gray',
933                AGENT_TYPE_PG          : 'photo_gray',
934                AGENT_TYPE_C_M         : 'cyan_and_magenta',
935                AGENT_TYPE_K_Y         : 'black_and_yellow',
936                AGENT_TYPE_PHOTO_BLACK : 'photo_black',
937                AGENT_TYPE_MATTE_BLACK : 'matte_black',
938                AGENT_TYPE_UNSPECIFIED : 'unspecified', # Kind=5,6
939            }
940
941AGENT_kinds = {AGENT_KIND_NONE            : 'invalid',
942                AGENT_KIND_HEAD            : 'head',
943                AGENT_KIND_SUPPLY          : 'supply',
944                AGENT_KIND_HEAD_AND_SUPPLY : 'cartridge',
945                AGENT_KIND_TONER_CARTRIDGE : 'toner',
946                AGENT_KIND_MAINT_KIT       : 'maint_kit', # fuser
947                AGENT_KIND_ADF_KIT         : 'adf_kit',
948                AGENT_KIND_DRUM_KIT        : 'drum_kit',
949                AGENT_KIND_TRANSFER_KIT    : 'transfer_kit',
950                AGENT_KIND_INT_BATTERY     : 'battery',
951                AGENT_KIND_UNKNOWN         : 'unknown',
952              }
953
954AGENT_healths = {AGENT_HEALTH_OK           : 'ok',
955                  AGENT_HEALTH_MISINSTALLED : 'misinstalled', # supply/cart
956                  #AGENT_HEALTH_FAIR_MODERATE : '',
957                  AGENT_HEALTH_INCORRECT    : 'incorrect',
958                  AGENT_HEALTH_FAILED       : 'failed',
959                  AGENT_HEALTH_OVERTEMP     : 'overtemp', # battery
960                  AGENT_HEALTH_CHARGING     : 'charging', # battery
961                  AGENT_HEALTH_DISCHARGING  : 'discharging', # battery
962                  AGENT_HEALTH_UNKNOWN      : 'unknown',
963                }
964
965
966AGENT_levels = {AGENT_LEVEL_TRIGGER_MAY_BE_LOW : 'low',
967                 AGENT_LEVEL_TRIGGER_PROBABLY_OUT : 'low',
968                 AGENT_LEVEL_TRIGGER_ALMOST_DEFINITELY_OUT : 'out',
969               }
970
971
972
973
974# **************************************************************************** #
975
976string_cache = {}
977
978class Device(object):
979    def __init__(self, device_uri, printer_name=None,
980                 service=None, callback=None, disable_dbus=False):
981
982        log.debug("Device URI: %s" % device_uri)
983        log.debug("Printer: %s" % printer_name)
984
985        global dbus_disabled
986        dbus_disabled = disable_dbus
987
988        if not disable_dbus:
989            if service is None:
990                self.dbus_avail, self.service,  session_bus = init_dbus()
991            else:
992                self.dbus_avail = True
993                self.service = service
994        else:
995            self.dbus_avail = False
996            self.service = None
997
998        self.last_event = None # Used in devmgr if dbus is disabled
999
1000        printers = cups.getPrinters()
1001
1002        if device_uri is None and printer_name is not None:
1003            for p in printers:
1004                if p.name.lower() == printer_name.lower():
1005                    device_uri = p.device_uri
1006                    log.debug("Device URI: %s" % device_uri)
1007                    break
1008            else:
1009                raise Error(ERROR_DEVICE_NOT_FOUND)
1010
1011        self.device_uri = device_uri
1012        self.callback = callback
1013        self.device_type = DEVICE_TYPE_UNKNOWN
1014
1015        if self.device_uri is None:
1016            raise Error(ERROR_DEVICE_NOT_FOUND)
1017
1018        if self.device_uri.startswith('hp:'):
1019            self.device_type = DEVICE_TYPE_PRINTER
1020
1021        elif self.device_uri.startswith('hpaio:'):
1022            self.device_type = DEVICE_TYPE_SCANNER
1023
1024        elif self.device_uri.startswith('hpfax:'):
1025            self.device_type = DEVICE_TYPE_FAX
1026
1027        try:
1028            self.back_end, self.is_hp, self.bus, self.model, \
1029                self.serial, self.dev_file, self.host, self.zc, self.port = \
1030                parseDeviceURI(self.device_uri)
1031        except Error:
1032            self.io_state = IO_STATE_NON_HP
1033            raise Error(ERROR_INVALID_DEVICE_URI)
1034
1035        log.debug("URI: backend=%s, is_hp=%s, bus=%s, model=%s, serial=%s, dev=%s, host=%s, port=%d" % \
1036            (self.back_end, self.is_hp, self.bus, self.model, self.serial, self.dev_file, self.host, self.port))
1037
1038        self.model_ui = models.normalizeModelUIName(self.model)
1039        self.model = models.normalizeModelName(self.model)
1040
1041        log.debug("Model/UI model: %s/%s" % (self.model, self.model_ui))
1042
1043        if self.bus == 'net':
1044            self.http_host = self.host
1045        else:
1046            self.http_host = 'localhost'
1047
1048        # TODO:
1049        #service.setAlertsEx(self.hpssd_sock)
1050
1051        self.mq = {} # Model query
1052        self.dq = {} # Device query
1053        self.icon = "default_printer"
1054        self.cups_printers = []
1055        self.channels = {} # { 'SERVICENAME' : channel_id, ... }
1056        self.device_id = -1
1057        self.r_values = None # ( r_value, r_value_str, rg, rr )
1058        self.deviceID = ''
1059        self.panel_check = True
1060        self.io_state = IO_STATE_HP_READY
1061        self.is_local = isLocal(self.bus)
1062        self.hist = []
1063
1064        self.supported = False
1065
1066        self.queryModel()
1067        if not self.supported:
1068            log.error("Unsupported model: %s" % self.model)
1069            self.error_code = STATUS_DEVICE_UNSUPPORTED
1070            self.sendEvent(self.error_code)
1071        else:
1072            self.supported = True
1073
1074
1075        self.mq.update({'model'    : self.model,
1076                        'model-ui' : self.model_ui})
1077
1078        self.error_state = ERROR_STATE_ERROR
1079        self.device_state = DEVICE_STATE_NOT_FOUND
1080        self.status_code = EVENT_ERROR_DEVICE_NOT_FOUND
1081
1082        self.updateCUPSPrinters()
1083
1084        if self.mq.get('fax-type', FAX_TYPE_NONE) != FAX_TYPE_NONE:
1085            self.dq.update({ 'fax-uri' : self.device_uri.replace('hp:/', 'hpfax:/').replace('hpaio:/', 'hpfax:/')})
1086
1087        if self.mq.get('scan-type', SCAN_TYPE_NONE) != SCAN_TYPE_NONE:
1088            self.dq.update({ 'scan-uri' : self.device_uri.replace('hp:/', 'hpaio:/').replace('hpfax:/', 'hpaio:/')})
1089
1090        self.dq.update({
1091            'back-end'         : self.back_end,
1092            'is-hp'            : self.is_hp,
1093            'serial'           : self.serial,
1094            'dev-file'         : self.dev_file,
1095            'host'             : self.host,
1096            'port'             : self.port,
1097            'cups-printers'    : self.cups_printers,
1098            'status-code'      : self.status_code,
1099            'status-desc'      : '',
1100            'deviceid'         : '',
1101            'panel'            : 0,
1102            'panel-line1'      : '',
1103            'panel-line2'      : '',
1104            'device-state'     : self.device_state,
1105            'error-state'      : self.error_state,
1106            'device-uri'       : self.device_uri,
1107            'cups-uri'         : self.device_uri.replace('hpfax:/', 'hp:/').replace('hpaio:/', 'hp:/'),
1108            })
1109
1110        self.device_vars = {
1111            'URI'        : self.device_uri,
1112            'DEVICE_URI' : self.device_uri,
1113            'SCAN_URI'   : self.device_uri.replace('hp:', 'hpaio:'),
1114            'SANE_URI'   : self.device_uri.replace('hp:', 'hpaio:'),
1115            'FAX_URI'    : self.device_uri.replace('hp:', 'hpfax:'),
1116            'PRINTER'    : self.first_cups_printer,
1117            'HOME'       : prop.home_dir,
1118                           }
1119
1120
1121
1122
1123    def sendEvent(self, event_code, printer_name='', job_id=0, title=''):
1124        if self.dbus_avail and self.service is not None:
1125            try:
1126                log.debug("Sending event %d to hpssd..." % event_code)
1127                self.service.SendEvent(self.device_uri, printer_name, event_code, prop.username, job_id, title)
1128            except dbus.exceptions.DBusException as e:
1129                log.debug("dbus call to SendEvent() failed.")
1130
1131
1132    def quit(self):
1133        pass
1134
1135
1136    def queryModel(self):
1137        if not self.mq:
1138            self.mq = queryModelByURI(self.device_uri)
1139
1140        self.supported = bool(self.mq)
1141
1142        if self.supported:
1143            for m in self.mq:
1144                self.__dict__[m.replace('-','_')] = self.mq[m]
1145
1146
1147    def queryString(self, string_id):
1148        return queryString(string_id)
1149
1150
1151    def open(self, open_for_printing=False):
1152        if self.supported and self.io_state in (IO_STATE_HP_READY, IO_STATE_HP_NOT_AVAIL):
1153            prev_device_state = self.device_state
1154            self.io_state = IO_STATE_HP_NOT_AVAIL
1155            self.device_state = DEVICE_STATE_NOT_FOUND
1156            self.error_state = ERROR_STATE_ERROR
1157            self.status_code = EVENT_ERROR_DEVICE_NOT_FOUND
1158            self.device_id = -1
1159            self.open_for_printing = open_for_printing
1160
1161            if open_for_printing:
1162                log.debug("Opening device: %s (for printing)" % self.device_uri)
1163                self.io_mode = self.mq.get('io-mode', hpmudext.HPMUD_UNI_MODE)
1164            else:
1165                log.debug("Opening device: %s (not for printing)" % self.device_uri)
1166                self.io_mode = self.mq.get('io-mfp-mode', hpmudext.HPMUD_UNI_MODE)
1167
1168            log.debug("I/O mode=%d" % self.io_mode)
1169            result_code, self.device_id = \
1170                hpmudext.open_device(self.device_uri, self.io_mode)
1171
1172            if result_code != hpmudext.HPMUD_R_OK:
1173                self.error_state = ERROR_STATE_ERROR
1174                self.error_code = result_code+ERROR_CODE_BASE
1175                self.sendEvent(self.error_code)
1176
1177                if result_code == hpmudext.HPMUD_R_DEVICE_BUSY:
1178                    log.error("Device busy: %s" % self.device_uri)
1179                else:
1180                    log.error("Unable to communicate with device (code=%d): %s" % (result_code, self.device_uri))
1181
1182                self.last_event = Event(self.device_uri, '', EVENT_ERROR_DEVICE_NOT_FOUND,
1183                        prop.username, 0, '', time.time())
1184
1185                raise Error(ERROR_DEVICE_NOT_FOUND)
1186
1187            else:
1188                log.debug("device-id=%d" % self.device_id)
1189                self.io_state = IO_STATE_HP_OPEN
1190                self.error_state = ERROR_STATE_CLEAR
1191                log.debug("Opened device: %s (backend=%s, is_hp=%s, bus=%s, model=%s, dev=%s, serial=%s, host=%s, port=%d)" %
1192                    (self.back_end, self.device_uri, self.is_hp, self.bus, self.model,
1193                     self.dev_file, self.serial, self.host, self.port))
1194
1195                if prev_device_state == DEVICE_STATE_NOT_FOUND:
1196                    self.device_state = DEVICE_STATE_JUST_FOUND
1197                else:
1198                    self.device_state = DEVICE_STATE_FOUND
1199
1200                self.getDeviceID()
1201                self.getSerialNumber()
1202                return self.device_id
1203
1204
1205    def close(self):
1206        if self.io_state == IO_STATE_HP_OPEN:
1207            log.debug("Closing device...")
1208
1209            if len(self.channels) > 0:
1210
1211                for c in list(self.channels.keys()):
1212                    self.__closeChannel(c)
1213
1214            result_code = hpmudext.close_device(self.device_id)
1215            log.debug("Result-code = %d" % result_code)
1216
1217            self.channels.clear()
1218            self.io_state = IO_STATE_HP_READY
1219
1220
1221    def __openChannel(self, service_name):
1222        try:
1223            if self.io_state == IO_STATE_HP_OPEN:
1224                if service_name == hpmudext.HPMUD_S_PRINT_CHANNEL and not self.open_for_printing:
1225                    self.close()
1226                    self.open(True)
1227                elif service_name != hpmudext.HPMUD_S_PRINT_CHANNEL and self.open_for_printing:
1228                    self.close()
1229                    self.open(False)
1230            else:
1231                self.open(service_name == hpmudext.HPMUD_S_PRINT_CHANNEL)
1232        except:
1233            log.error("unable to open channel")
1234            return -1
1235
1236        #if not self.mq['io-mode'] == IO_MODE_UNI:
1237        if 1:
1238            service_name = service_name.upper()
1239
1240            if service_name not in self.channels:
1241                log.debug("Opening %s channel..." % service_name)
1242
1243                result_code, channel_id = hpmudext.open_channel(self.device_id, service_name)
1244
1245                self.channels[service_name] = channel_id
1246                log.debug("channel-id=%d" % channel_id)
1247                return channel_id
1248            else:
1249                return self.channels[service_name]
1250        else:
1251            return -1
1252
1253
1254    def openChannel(self, service_name):
1255        return self.__openChannel(service_name)
1256
1257    def openPrint(self):
1258        return self.__openChannel(hpmudext.HPMUD_S_PRINT_CHANNEL)
1259
1260    def openFax(self):
1261        return self.__openChannel(hpmudext.HPMUD_S_FAX_SEND_CHANNEL)
1262
1263    def openPCard(self):
1264        return self.__openChannel(hpmudext.HPMUD_S_MEMORY_CARD_CHANNEL)
1265
1266    def openEWS(self):
1267        return self.__openChannel(hpmudext.HPMUD_S_EWS_CHANNEL)
1268
1269    def openEWS_LEDM(self):
1270        return self.__openChannel(hpmudext.HPMUD_S_EWS_LEDM_CHANNEL)
1271
1272    def openLEDM(self):
1273        return self.__openChannel(hpmudext.HPMUD_S_LEDM_SCAN)
1274
1275    def openMarvell_EWS(self):
1276        return self.__openChannel(hpmudext.HPMUD_S_MARVELL_EWS_CHANNEL)
1277
1278    def closePrint(self):
1279        return self.__closeChannel(hpmudext.HPMUD_S_PRINT_CHANNEL)
1280
1281    def closePCard(self):
1282        return self.__closeChannel(hpmudext.HPMUD_S_MEMORY_CARD_CHANNEL)
1283
1284    def closeFax(self):
1285        return self.__closeChannel(hpmudext.HPMUD_S_FAX_SEND_CHANNEL)
1286
1287    def openPML(self):
1288        return self.__openChannel(hpmudext.HPMUD_S_PML_CHANNEL)
1289
1290    def openWifiConfig(self):
1291        return self.__openChannel(hpmudext.HPMUD_S_WIFI_CHANNEL)
1292
1293    def closePML(self):
1294        return self.__closeChannel(hpmudext.HPMUD_S_PML_CHANNEL)
1295
1296    def closeEWS(self):
1297        return self.__closeChannel(hpmudext.HPMUD_S_EWS_CHANNEL)
1298
1299    def closeEWS_LEDM(self):
1300        return self.__closeChannel(hpmudext.HPMUD_S_EWS_LEDM_CHANNEL)
1301
1302    def closeLEDM(self):
1303        return self.__closeChannel(hpmudext.HPMUD_S_LEDM_SCAN)
1304
1305    def closeMarvell_EWS(self):
1306        return self.__closeChannel(hpmudext.HPMUD_S_MARVELL_EWS_CHANNEL)
1307
1308    def openCfgUpload(self):
1309        return self.__openChannel(hpmudext.HPMUD_S_CONFIG_UPLOAD_CHANNEL)
1310
1311    def closeCfgUpload(self):
1312        return self.__closeChannel(hpmudext.HPMUD_S_CONFIG_UPLOAD_CHANNEL)
1313
1314    def openCfgDownload(self):
1315        return self.__openChannel(hpmudext.HPMUD_S_CONFIG_DOWNLOAD_CHANNEL)
1316
1317    def closeCfgDownload(self):
1318        return self.__closeChannel(hpmudext.HPMUD_S_CONFIG_DOWNLOAD_CHANNEL)
1319
1320    def openSoapFax(self):
1321        return self.__openChannel(hpmudext.HPMUD_S_SOAP_FAX)
1322
1323    def openMarvellFax(self):
1324        return self.__openChannel(hpmudext.HPMUD_S_MARVELL_FAX_CHANNEL)
1325
1326    def closeSoapFax(self):
1327        return self.__closeChannel(hpmudext.HPMUD_S_SOAP_FAX)
1328
1329    def closeMarvellFax(self):
1330        return self.__closeChannel(hpmudext.HPMUD_S_MARVELL_FAX_CHANNEL)
1331
1332    def closeWifiConfig(self):
1333        return self.__closeChannel(hpmudext.HPMUD_S_WIFI_CHANNEL)
1334
1335    def __closeChannel(self, service_name):
1336        #if not self.mq['io-mode'] == IO_MODE_UNI and \
1337        if self.io_state == IO_STATE_HP_OPEN:
1338
1339            service_name = service_name.upper()
1340
1341            if service_name in self.channels:
1342                log.debug("Closing %s channel..." % service_name)
1343
1344                result_code = hpmudext.close_channel(self.device_id,
1345                    self.channels[service_name])
1346
1347                del self.channels[service_name]
1348
1349
1350    def closeChannel(self, service_name):
1351        return self.__closeChannel(service_name)
1352
1353
1354    def getDeviceID(self):
1355        needs_close = False
1356        self.raw_deviceID = ''
1357        self.deviceID = {}
1358
1359        if self.io_state != IO_STATE_HP_OPEN:
1360           try:
1361               self.open()
1362           except:
1363               return -1
1364           needs_close = True
1365
1366        result_code, data = hpmudext.get_device_id(self.device_id)
1367
1368        if result_code == hpmudext.HPMUD_R_OK:
1369            self.raw_deviceID = data
1370            self.deviceID = parseDeviceID(data)
1371
1372        if needs_close:
1373            self.close()
1374
1375        return self.deviceID
1376
1377
1378    def getSerialNumber(self):
1379        if self.serial:
1380            return
1381
1382        try:
1383            self.serial = self.deviceID['SN']
1384        except KeyError:
1385            pass
1386        else:
1387            if self.serial:
1388                return
1389
1390        if self.mq.get('status-type', STATUS_TYPE_NONE) != STATUS_TYPE_NONE: # and \
1391            #not self.mq.get('io-mode', IO_MODE_UNI) == IO_MODE_UNI:
1392
1393            try:
1394                try:
1395                    error_code, self.serial = self.getPML(pml.OID_SERIAL_NUMBER)
1396                except Error:
1397                    self.serial = ''
1398            finally:
1399                self.closePML()
1400
1401        if self.serial is None:
1402            self.serial = ''
1403
1404
1405    def getThreeBitStatus(self):
1406        pass
1407
1408
1409    def getStatusFromDeviceID(self):
1410        self.getDeviceID()
1411        return status.parseStatus(parseDeviceID(self.raw_deviceID))
1412
1413
1414    def __parseRValues(self, r_value):
1415        r_value_str = str(r_value)
1416        r_value_str = ''.join(['0'*(9 - len(r_value_str)), r_value_str])
1417        rg, rr = r_value_str[:3], r_value_str[3:]
1418        r_value = int(rr)
1419        self.r_values = r_value, r_value_str, rg, rr
1420        return r_value, r_value_str, rg, rr
1421
1422
1423    def getRValues(self, r_type, status_type, dynamic_counters):
1424        r_value, r_value_str, rg, rr = 0, '000000000', '000', '000000'
1425
1426        if r_type > 0 and \
1427            dynamic_counters != STATUS_DYNAMIC_COUNTERS_NONE:
1428
1429            if self.r_values is None:
1430                if self.dbus_avail:
1431                    try:
1432                        r_value = int(self.service.GetCachedIntValue(self.device_uri, 'r_value'))
1433                    except dbus.exceptions.DBusException as e:
1434                        log.debug("dbus call to GetCachedIntValue() failed.")
1435                        r_value = -1
1436
1437                if r_value != -1:
1438                    log.debug("r_value=%d" % r_value)
1439                    r_value, r_value_str, rg, rr = self.__parseRValues(r_value)
1440
1441                    return r_value, r_value_str, rg, rr
1442
1443            if self.r_values is None:
1444
1445                if status_type ==  STATUS_TYPE_S and \
1446                    self.is_local and \
1447                    dynamic_counters != STATUS_DYNAMIC_COUNTERS_PML_SNMP:
1448
1449                    try:
1450                        try:
1451                            r_value = self.getDynamicCounter(140)
1452
1453                            if r_value is not None:
1454                                log.debug("r_value=%d" % r_value)
1455                                r_value, r_value_str, rg, rr = self.__parseRValues(r_value)
1456
1457                                if self.dbus_avail:
1458                                    try:
1459                                        self.service.SetCachedIntValue(self.device_uri, 'r_value', r_value)
1460                                    except dbus.exceptions.DBusException as e:
1461                                        log.debug("dbus call to SetCachedIntValue() failed.")
1462                            else:
1463                                log.error("Error attempting to read r-value (2).")
1464                                r_value = 0
1465                        except Error:
1466                            log.error("Error attempting to read r-value (1).")
1467                            r_value = 0
1468                    finally:
1469                        self.closePrint()
1470
1471
1472                elif (status_type ==  STATUS_TYPE_S and
1473                      dynamic_counters == STATUS_DYNAMIC_COUNTERS_PCL and
1474                      not self.is_local) or \
1475                      dynamic_counters == STATUS_DYNAMIC_COUNTERS_PML_SNMP:
1476
1477                    try:
1478                        result_code, r_value = self.getPML(pml.OID_R_SETTING)
1479
1480                        if r_value is not None:
1481                            log.debug("r_value=%d" % r_value)
1482                            r_value, r_value_str, rg, rr = self.__parseRValues(r_value)
1483
1484                            if self.dbus_avail:
1485                                try:
1486                                    self.service.SetCachedIntValue(self.device_uri, 'r_value', r_value)
1487                                except dbus.exceptions.DBusException as e:
1488                                    log.debug("dbus call to SetCachedIntValue() failed.")
1489
1490                        else:
1491                            r_value = 0
1492
1493                    finally:
1494                        self.closePML()
1495
1496            else:
1497                r_value, r_value_str, rg, rr = self.r_values
1498
1499        return r_value, r_value_str, rg, rr
1500
1501
1502    def __queryFax(self, quick=False, reread_cups_printers=False):
1503        io_mode = self.mq.get('io-mode', IO_MODE_UNI)
1504        self.status_code = STATUS_PRINTER_IDLE
1505
1506        if io_mode != IO_MODE_UNI:
1507
1508            if self.device_state != DEVICE_STATE_NOT_FOUND:
1509                if self.tech_type in (TECH_TYPE_MONO_INK, TECH_TYPE_COLOR_INK):
1510                    try:
1511                        self.getDeviceID()
1512                    except Error as e:
1513                        log.error("Error getting device ID.")
1514                        self.last_event = Event(self.device_uri, '', ERROR_DEVICE_IO_ERROR,
1515                            prop.username, 0, '', time.time())
1516
1517                        raise Error(ERROR_DEVICE_IO_ERROR)
1518
1519                status_desc = self.queryString(self.status_code)
1520
1521                self.dq.update({
1522                    'serial'           : self.serial,
1523                    'cups-printers'    : self.cups_printers,
1524                    'status-code'      : self.status_code,
1525                    'status-desc'      : status_desc,
1526                    'deviceid'         : self.raw_deviceID,
1527                    'panel'            : 0,
1528                    'panel-line1'      : '',
1529                    'panel-line2'      : '',
1530                    'device-state'     : self.device_state,
1531                    'error-state'      : self.error_state,
1532                    })
1533
1534
1535            log.debug("Fax activity check...")
1536
1537            tx_active, rx_active = status.getFaxStatus(self)
1538
1539            if tx_active:
1540                self.status_code = STATUS_FAX_TX_ACTIVE
1541            elif rx_active:
1542                self.status_code = STATUS_FAX_RX_ACTIVE
1543
1544            self.error_state = STATUS_TO_ERROR_STATE_MAP.get(self.status_code, ERROR_STATE_CLEAR)
1545            self.error_code = self.status_code
1546            self.sendEvent(self.error_code)
1547
1548            try:
1549                self.dq.update({'status-desc' : self.queryString(self.status_code),
1550                                'error-state' : self.error_state,
1551                                })
1552
1553            except (KeyError, Error):
1554                self.dq.update({'status-desc' : '',
1555                                'error-state' : ERROR_STATE_CLEAR,
1556                                })
1557
1558
1559            if self.panel_check:
1560                self.panel_check = bool(self.mq.get('panel-check-type', 0))
1561
1562            status_type = self.mq.get('status-type', STATUS_TYPE_NONE)
1563            if self.panel_check and \
1564                status_type in (STATUS_TYPE_LJ, STATUS_TYPE_S, STATUS_TYPE_VSTATUS) and \
1565                io_mode != IO_MODE_UNI:
1566
1567                log.debug("Panel check...")
1568                try:
1569                    self.panel_check, line1, line2 = status.PanelCheck(self)
1570                finally:
1571                    self.closePML()
1572
1573                self.dq.update({'panel': int(self.panel_check),
1574                                  'panel-line1': line1,
1575                                  'panel-line2': line2,})
1576
1577            if not quick and reread_cups_printers:
1578                self.updateCUPSPrinters()
1579
1580        for d in self.dq:
1581            self.__dict__[d.replace('-','_')] = self.dq[d]
1582
1583        self.last_event = Event(self.device_uri, '', self.status_code, prop.username, 0, '', time.time())
1584
1585        log.debug(self.dq)
1586
1587
1588
1589    def updateCUPSPrinters(self):
1590        self.cups_printers = []
1591        log.debug("Re-reading CUPS printer queue information.")
1592        printers = cups.getPrinters()
1593        for p in printers:
1594            if self.device_uri == p.device_uri:
1595                self.cups_printers.append(p.name)
1596                self.state = p.state # ?
1597
1598                if self.io_state == IO_STATE_NON_HP:
1599                    self.model = p.makemodel.split(',')[0]
1600
1601        self.dq.update({'cups-printers' : self.cups_printers})
1602
1603        try:
1604            self.first_cups_printer = self.cups_printers[0]
1605        except IndexError:
1606            self.first_cups_printer = ''
1607
1608
1609
1610
1611    def queryDevice(self, quick=False, reread_cups_printers=False):
1612        if not self.supported:
1613            self.dq = {}
1614
1615            self.last_event = Event(self.device_uri, '', STATUS_DEVICE_UNSUPPORTED,
1616                prop.username, 0, '', time.time())
1617
1618            return
1619
1620        if self.device_type == DEVICE_TYPE_FAX:
1621            return self.__queryFax(quick, reread_cups_printers)
1622
1623        r_type = self.mq.get('r-type', 0)
1624        tech_type = self.mq.get('tech-type', TECH_TYPE_NONE)
1625        status_type = self.mq.get('status-type', STATUS_TYPE_NONE)
1626        battery_check = self.mq.get('status-battery-check', STATUS_BATTERY_CHECK_NONE)
1627        dynamic_counters = self.mq.get('status-dynamic-counters', STATUS_DYNAMIC_COUNTERS_NONE)
1628        io_mode = self.mq.get('io-mode', IO_MODE_UNI)
1629        io_mfp_mode = self.mq.get('io-mfp-mode', IO_MODE_UNI)
1630        status_code = STATUS_UNKNOWN
1631
1632        # Turn off status if local connection and bi-di not avail.
1633        #if io_mode  == IO_MODE_UNI and self.back_end != 'net':
1634        #    status_type = STATUS_TYPE_NONE
1635
1636        agents = []
1637
1638        if self.device_state != DEVICE_STATE_NOT_FOUND:
1639            if self.tech_type in (TECH_TYPE_MONO_INK, TECH_TYPE_COLOR_INK):
1640                try:
1641                    self.getDeviceID()
1642                except Error as e:
1643                    log.error("Error getting device ID.")
1644                    self.last_event = Event(self.device_uri, '', ERROR_DEVICE_IO_ERROR,
1645                        prop.username, 0, '', time.time())
1646
1647                    raise Error(ERROR_DEVICE_IO_ERROR)
1648
1649            status_desc = self.queryString(self.status_code)
1650
1651            self.dq.update({
1652                'serial'           : self.serial,
1653                'cups-printers'    : self.cups_printers,
1654                'status-code'      : self.status_code,
1655                'status-desc'      : status_desc,
1656                'deviceid'         : self.raw_deviceID,
1657                'panel'            : 0,
1658                'panel-line1'      : '',
1659                'panel-line2'      : '',
1660                'device-state'     : self.device_state,
1661                'error-state'      : self.error_state,
1662                })
1663
1664            status_block = {}
1665
1666            if status_type == STATUS_TYPE_NONE:
1667                log.warn("No status available for device.")
1668                status_block = {'status-code' : STATUS_UNKNOWN}
1669
1670            elif status_type in (STATUS_TYPE_VSTATUS, STATUS_TYPE_S):
1671                log.debug("Type 1/2 (S: or VSTATUS:) status")
1672                status_block = status.parseStatus(self.deviceID)
1673
1674            elif status_type in (STATUS_TYPE_LJ, STATUS_TYPE_PML_AND_PJL):
1675                log.debug("Type 3/9 LaserJet PML(+PJL) status")
1676                status_block = status.StatusType3(self, self.deviceID)
1677
1678            elif status_type == STATUS_TYPE_LJ_XML:
1679                log.debug("Type 6: LJ XML")
1680                status_block = status.StatusType6(self)
1681
1682            elif status_type == STATUS_TYPE_PJL:
1683                log.debug("Type 8: LJ PJL")
1684                status_block = status.StatusType8(self)
1685
1686            elif status_type == STATUS_TYPE_LEDM:
1687                log.debug("Type 10: LEDM")
1688                status_block = status.StatusType10(self.getEWSUrl_LEDM)
1689
1690            elif status_type == STATUS_TYPE_LEDM_FF_CC_0:
1691                log.debug("Type 11: LEDM_FF_CC_0")
1692                status_block = status.StatusType10(self.getUrl_LEDM)
1693
1694            elif status_type == STATUS_TYPE_IPP:
1695                log.debug("Type 12: IPP")
1696                status_block = status.StatusTypeIPP(self.device_uri)
1697
1698            else:
1699                log.error("Unimplemented status type: %d" % status_type)
1700
1701            if battery_check and \
1702                io_mode != IO_MODE_UNI:
1703
1704                log.debug("Battery check...")
1705                status.BatteryCheck(self, status_block, battery_check)
1706
1707            if status_block:
1708                log.debug(status_block)
1709                self.dq.update(status_block)
1710                try:
1711                    status_block['agents']
1712                except KeyError:
1713                    pass
1714                else:
1715                    agents = status_block['agents']
1716                    del self.dq['agents']
1717
1718
1719            status_code = self.dq.get('status-code', STATUS_UNKNOWN)
1720
1721            self.error_state = STATUS_TO_ERROR_STATE_MAP.get(status_code, ERROR_STATE_CLEAR)
1722            self.error_code = status_code
1723            self.sendEvent(self.error_code)
1724
1725            try:
1726                self.dq.update({'status-desc' : self.queryString(status_code),
1727                                'error-state' : self.error_state,
1728                                })
1729
1730            except (KeyError, Error):
1731                self.dq.update({'status-desc' : '',
1732                                'error-state' : ERROR_STATE_CLEAR,
1733                                })
1734
1735            r_value = 0
1736
1737            if not quick and status_type != STATUS_TYPE_NONE:
1738                if self.panel_check:
1739                    self.panel_check = bool(self.mq.get('panel-check-type', 0))
1740
1741                if self.panel_check and \
1742                    status_type in (STATUS_TYPE_LJ, STATUS_TYPE_S, STATUS_TYPE_VSTATUS) and \
1743                    io_mode != IO_MODE_UNI:
1744
1745                    log.debug("Panel check...")
1746                    try:
1747                        self.panel_check, line1, line2 = status.PanelCheck(self)
1748                    finally:
1749                        self.closePML()
1750
1751                    self.dq.update({'panel': int(self.panel_check),
1752                                      'panel-line1': line1,
1753                                      'panel-line2': line2,})
1754
1755
1756                if dynamic_counters != STATUS_DYNAMIC_COUNTERS_NONE and \
1757                    io_mode != IO_MODE_UNI:
1758
1759                    r_value, r_value_str, rg, rr = self.getRValues(r_type, status_type, dynamic_counters)
1760                else:
1761                    r_value, r_value_str, rg, rr = 0, '000000000', '000', '000000'
1762
1763                self.dq.update({'r'  : r_value,
1764                                'rs' : r_value_str,
1765                                'rg' : rg,
1766                                'rr' : rr,
1767                              })
1768
1769            if not quick and reread_cups_printers:
1770                self.updateCUPSPrinters()
1771
1772            if not quick:
1773                # Make sure there is some valid agent data for this r_value
1774                # If not, fall back to r_value == 0
1775                if r_value > 0 and self.mq.get('r%d-agent1-kind' % r_value, 0) == 0:
1776                    r_value = 0
1777                    self.dq.update({'r'  : r_value,
1778                                    'rs' : r_value_str,
1779                                    'rg' : rg,
1780                                    'rr' : rr,
1781                                  })
1782
1783                #Check if device itself is sending the supplies info. If so, then in that case we need not check model.dat static data and
1784                #compare with region, kind and type values.
1785                dynamic_sku_data = False
1786                for agent in agents:
1787                    try:
1788                        if agent['agent-sku'] != '':
1789                            dynamic_sku_data = True
1790                            break
1791                    except:
1792                        pass
1793
1794                a, aa = 1, 1
1795                while True:
1796                    if dynamic_sku_data:
1797                        if a > len(agents):
1798                            break
1799                        agent = agents[a-1]
1800                        mq_agent_sku = agent['agent-sku']
1801                        agent_kind = agent['kind']
1802                        agent_type = agent['type']
1803                        found = True
1804                    else:
1805                        mq_agent_kind = self.mq.get('r%d-agent%d-kind' % (r_value, a), -1)
1806                        if mq_agent_kind == -1:
1807                            break
1808                        mq_agent_type = self.mq.get('r%d-agent%d-type' % (r_value, a), 0)
1809                        mq_agent_sku = self.mq.get('r%d-agent%d-sku' % (r_value, a), '')
1810                        found = False
1811
1812                        log.debug("Looking for kind=%d, type=%d..." % (mq_agent_kind, mq_agent_type))
1813                        for agent in agents:
1814                            agent_kind = agent['kind']
1815                            agent_type = agent['type']
1816
1817                            if agent_kind == mq_agent_kind and \
1818                               agent_type == mq_agent_type:
1819                                   found = True
1820                                   break
1821
1822                    if found:
1823                        log.debug("found: r%d-kind%d-type%d" % (r_value, agent_kind, agent_type))
1824
1825                        agent_health = agent.get('health', AGENT_HEALTH_OK)
1826                        agent_level = agent.get('level', 100)
1827                        agent_level_trigger = agent.get('level-trigger',
1828                            AGENT_LEVEL_TRIGGER_SUFFICIENT_0)
1829
1830                        log.debug("health=%d, level=%d, level_trigger=%d, status_code=%d" %
1831                            (agent_health, agent_level, agent_level_trigger, status_code))
1832
1833                        query = 'agent_%s_%s' % (AGENT_types.get(agent_type, 'unknown'),
1834                                                 AGENT_kinds.get(agent_kind, 'unknown'))
1835
1836                        agent_desc = self.queryString(query)
1837                        query = 'agent_health_ok'
1838
1839                        # If printer is not in an error state, and
1840                        # if agent health is OK, check for low supplies. If low, use
1841                        # the agent level trigger description for the agent description.
1842                        # Otherwise, report the agent health.
1843                        if (status_code == STATUS_PRINTER_POWER_SAVE or status_code == STATUS_PRINTER_IDLE or status_code == STATUS_PRINTER_OUT_OF_INK) and \
1844                            (agent_health == AGENT_HEALTH_OK or
1845                             (agent_health == AGENT_HEALTH_FAIR_MODERATE and agent_kind == AGENT_KIND_HEAD)) and \
1846                            agent_level_trigger >= AGENT_LEVEL_TRIGGER_MAY_BE_LOW:
1847
1848                            query = 'agent_level_%s' % AGENT_levels.get(agent_level_trigger, 'unknown')
1849
1850                            if tech_type in (TECH_TYPE_MONO_INK, TECH_TYPE_COLOR_INK):
1851                                code = agent_type + STATUS_PRINTER_LOW_INK_BASE
1852                            else:
1853                                code = agent_type + STATUS_PRINTER_LOW_TONER_BASE
1854
1855                            self.dq['status-code'] = code
1856                            self.dq['status-desc'] = self.queryString(code)
1857
1858                            self.dq['error-state'] = STATUS_TO_ERROR_STATE_MAP.get(code, ERROR_STATE_LOW_SUPPLIES)
1859                            self.error_code = code
1860                            self.sendEvent(self.error_code)
1861
1862                            if agent_level_trigger in \
1863                                (AGENT_LEVEL_TRIGGER_PROBABLY_OUT, AGENT_LEVEL_TRIGGER_ALMOST_DEFINITELY_OUT):
1864
1865                                query = 'agent_level_out'
1866                            else:
1867                                query = 'agent_level_low'
1868
1869                            agent_health_desc = self.queryString(query)
1870
1871                            self.dq.update(
1872                            {
1873                                'agent%d-kind' % aa :          agent_kind,
1874                                'agent%d-type' % aa :          agent_type,
1875                                'agent%d-known' % aa :         agent.get('known', False),
1876                                'agent%d-sku' % aa :           mq_agent_sku,
1877                                'agent%d-level' % aa :         agent_level,
1878                                'agent%d-level-trigger' % aa : agent_level_trigger,
1879                                'agent%d-ack' % aa :           agent.get('ack', False),
1880                                'agent%d-hp-ink' % aa :        agent.get('hp-ink', False),
1881                                'agent%d-health' % aa :        agent_health,
1882                                'agent%d-dvc' % aa :           agent.get('dvc', 0),
1883                                'agent%d-virgin' % aa :        agent.get('virgin', False),
1884                                'agent%d-desc' % aa :          agent_desc,
1885                                'agent%d-id' % aa :            agent.get('id', 0),
1886                                'agent%d-health-desc' % aa :   agent_health_desc,
1887                            })
1888
1889                        else:
1890                            query = 'agent_health_%s' % AGENT_healths.get(agent_health, AGENT_HEALTH_OK)
1891                            agent_health_desc = self.queryString(query)
1892
1893                            self.dq.update(
1894                            {
1895                                'agent%d-kind' % aa :          agent_kind,
1896                                'agent%d-type' % aa :          agent_type,
1897                                'agent%d-known' % aa :         False,
1898                                'agent%d-sku' % aa :           mq_agent_sku,
1899                                'agent%d-level' % aa :         agent_level,
1900                                'agent%d-level-trigger' % aa : agent_level_trigger,
1901                                'agent%d-ack' % aa :           False,
1902                                'agent%d-hp-ink' % aa :        False,
1903                                'agent%d-health' % aa :        agent_health,
1904                                'agent%d-dvc' % aa :           0,
1905                                'agent%d-virgin' % aa :        False,
1906                                'agent%d-desc' % aa :          agent_desc,
1907                                'agent%d-id' % aa :            0,
1908                                'agent%d-health-desc' % aa :   agent_health_desc,
1909                            })
1910
1911                        aa += 1
1912
1913                    else:
1914                        log.debug("Not found: %d" % a)
1915
1916                    a += 1
1917
1918        else: # Create agent keys for not-found devices
1919
1920            r_value = 0
1921            if r_type > 0 and self.r_values is not None:
1922                r_value = self.r_values[0]
1923
1924            # Make sure there is some valid agent data for this r_value
1925            # If not, fall back to r_value == 0
1926            if r_value > 0 and self.mq.get('r%d-agent1-kind', 0) == 0:
1927                r_value = 0
1928
1929            a = 1
1930            while True:
1931                mq_agent_kind = self.mq.get('r%d-agent%d-kind' % (r_value, a), 0)
1932
1933                if mq_agent_kind == 0:
1934                    break
1935
1936                mq_agent_type = self.mq.get('r%d-agent%d-type' % (r_value, a), 0)
1937                mq_agent_sku = self.mq.get('r%d-agent%d-sku' % (r_value, a), '')
1938                query = 'agent_%s_%s' % (AGENT_types.get(mq_agent_type, 'unknown'),
1939                                         AGENT_kinds.get(mq_agent_kind, 'unknown'))
1940
1941                agent_desc = self.queryString(query)
1942
1943                self.dq.update(
1944                {
1945                    'agent%d-kind' % a :          mq_agent_kind,
1946                    'agent%d-type' % a :          mq_agent_type,
1947                    'agent%d-known' % a :         False,
1948                    'agent%d-sku' % a :           mq_agent_sku,
1949                    'agent%d-level' % a :         0,
1950                    'agent%d-level-trigger' % a : AGENT_LEVEL_TRIGGER_ALMOST_DEFINITELY_OUT,
1951                    'agent%d-ack' % a :           False,
1952                    'agent%d-hp-ink' % a :        False,
1953                    'agent%d-health' % a :        AGENT_HEALTH_MISINSTALLED,
1954                    'agent%d-dvc' % a :           0,
1955                    'agent%d-virgin' % a :        False,
1956                    'agent%d-health-desc' % a :   self.queryString('agent_health_unknown'),
1957                    'agent%d-desc' % a :          agent_desc,
1958                    'agent%d-id' % a :            0,
1959                })
1960
1961                a += 1
1962
1963        for d in self.dq:
1964            self.__dict__[d.replace('-','_')] = self.dq[d]
1965
1966        self.last_event = Event(self.device_uri, '', status_code, prop.username, 0, '', time.time())
1967        log.debug(self.dq)
1968
1969
1970    def isBusyOrInErrorState(self):
1971        try:
1972            self.queryDevice(quick=True)
1973        except Error:
1974            return True
1975        return self.error_state in (ERROR_STATE_ERROR, ERROR_STATE_BUSY)
1976
1977
1978    def isIdleAndNoError(self):
1979        try:
1980            self.queryDevice(quick=True)
1981        except Error:
1982            return False
1983        return self.error_state not in (ERROR_STATE_ERROR, ERROR_STATE_BUSY)
1984
1985
1986    def getPML(self, oid, desired_int_size=pml.INT_SIZE_INT): # oid => ( 'dotted oid value', pml type )
1987        channel_id = self.openPML()
1988        result_code, data, typ, pml_result_code = \
1989            hpmudext.get_pml(self.device_id, channel_id, pml.PMLToSNMP(oid[0]), oid[1])
1990        if pml_result_code > pml.ERROR_MAX_OK:
1991            log.debug("PML/SNMP GET %s failed (result code = 0x%x)" % (oid[0], pml_result_code))
1992            return pml_result_code, None
1993
1994        converted_data = pml.ConvertFromPMLDataFormat(data, oid[1], desired_int_size)
1995
1996        if log.is_debug():
1997            if oid[1] in (pml.TYPE_STRING, pml.TYPE_BINARY):
1998
1999                log.debug("PML/SNMP GET %s (result code = 0x%x) returned:" %
2000                    (oid[0], pml_result_code))
2001                log.log_data(data)
2002            else:
2003                log.debug("PML/SNMP GET %s (result code = 0x%x) returned: %s" %
2004                    (oid[0], pml_result_code, repr(converted_data)))
2005        return pml_result_code, converted_data
2006
2007
2008    def setPML(self, oid, value): # oid => ( 'dotted oid value', pml type )
2009        channel_id = self.openPML()
2010        value = pml.ConvertToPMLDataFormat(value, oid[1])
2011        result_code, pml_result_code = \
2012            hpmudext.set_pml(self.device_id, channel_id, pml.PMLToSNMP(oid[0]), oid[1], value)
2013
2014        if log.is_debug():
2015            if oid[1] in (pml.TYPE_STRING, pml.TYPE_BINARY):
2016
2017                log.debug("PML/SNMP SET %s (result code = 0x%x) to:" %
2018                    (oid[0], pml_result_code))
2019            else:
2020                log.debug("PML/SNMP SET %s (result code = 0x%x) to: %s" %
2021                    (oid[0], pml_result_code, repr(value.decode('utf-8'))))
2022
2023        return pml_result_code
2024
2025
2026    def getDynamicCounter(self, counter, convert_to_int=True):
2027        dynamic_counters = self.mq.get('status-dynamic-counters', STATUS_DYNAMIC_COUNTERS_NONE)
2028        log.debug("Dynamic counters: %d" % dynamic_counters)
2029        if dynamic_counters != STATUS_DYNAMIC_COUNTERS_NONE:
2030
2031            if dynamic_counters == STATUS_DYNAMIC_COUNTERS_LIDIL_0_5_4:
2032                self.printData(ldl.buildResetPacket(), direct=True)
2033                self.printData(ldl.buildDynamicCountersPacket(counter), direct=True)
2034            else:
2035                self.printData(pcl.buildDynamicCounter(counter), direct=True)
2036
2037            value, tries, times_seen, sleepy_time, max_tries = 0, 0, 0, 0.1, 5
2038            time.sleep(0.1)
2039
2040            while True:
2041
2042                if self.callback:
2043                    self.callback()
2044
2045                sleepy_time += 0.1
2046                tries += 1
2047
2048                time.sleep(sleepy_time)
2049
2050                self.getDeviceID()
2051
2052                if 'CTR' in self.deviceID and \
2053                    pat_dynamic_ctr.search(self.raw_deviceID) is not None:
2054                    dev_counter, value = parseDynamicCounter(self.deviceID['CTR'], convert_to_int)
2055
2056                    if counter == dev_counter:
2057                        self.printData(pcl.buildDynamicCounter(0), direct=True)
2058                        # protect the value as a string during msg handling
2059                        if not convert_to_int:
2060                            value = '#' + value
2061                        return value
2062
2063                if tries > max_tries:
2064                    if dynamic_counters == STATUS_DYNAMIC_COUNTERS_LIDIL_0_5_4:
2065                        self.printData(ldl.buildResetPacket())
2066                        self.printData(ldl.buildDynamicCountersPacket(counter), direct=True)
2067                    else:
2068                        self.printData(pcl.buildDynamicCounter(0), direct=True)
2069
2070                    return None
2071
2072                if dynamic_counters == STATUS_DYNAMIC_COUNTERS_LIDIL_0_5_4:
2073                    self.printData(ldl.buildResetPacket())
2074                    self.printData(ldl.buildDynamicCountersPacket(counter), direct=True)
2075                else:
2076                    self.printData(pcl.buildDynamicCounter(counter), direct=True)
2077
2078        else:
2079            raise Error(ERROR_DEVICE_DOES_NOT_SUPPORT_OPERATION)
2080
2081
2082    def readPrint(self, bytes_to_read, stream=None, timeout=prop.read_timeout, allow_short_read=False):
2083        return self.__readChannel(self.openPrint, bytes_to_read, stream, timeout, allow_short_read)
2084
2085    def readPCard(self, bytes_to_read, stream=None, timeout=prop.read_timeout, allow_short_read=False):
2086        return self.__readChannel(self.openPCard, bytes_to_read, stream, timeout, allow_short_read)
2087
2088    def readFax(self, bytes_to_read, stream=None, timeout=prop.read_timeout, allow_short_read=False):
2089        return self.__readChannel(self.openFax, bytes_to_read, stream, timeout, allow_short_read)
2090
2091    def readCfgUpload(self, bytes_to_read, stream=None, timeout=prop.read_timeout, allow_short_read=False):
2092        return self.__readChannel(self.openCfgUpload, bytes_to_read, stream, timeout, allow_short_read)
2093
2094    def readEWS(self, bytes_to_read, stream=None, timeout=prop.read_timeout, allow_short_read=True):
2095        return self.__readChannel(self.openEWS, bytes_to_read, stream, timeout, allow_short_read)
2096
2097    def readEWS_LEDM(self, bytes_to_read, stream=None, timeout=prop.read_timeout, allow_short_read=True):
2098        return self.__readChannel(self.openEWS_LEDM, bytes_to_read, stream, timeout, allow_short_read)
2099
2100    def readLEDM(self, bytes_to_read, stream=None, timeout=prop.read_timeout, allow_short_read=True):
2101        return self.__readChannel(self.openLEDM, bytes_to_read, stream, timeout, allow_short_read)
2102
2103    def readMarvell_EWS(self, bytes_to_read, stream=None, timeout=prop.read_timeout, allow_short_read=True):
2104        return self.__readChannel(self.openMarvell_EWS, bytes_to_read, stream, timeout, allow_short_read)
2105
2106    def readSoapFax(self, bytes_to_read, stream=None, timeout=prop.read_timeout, allow_short_read=True):
2107        return self.__readChannel(self.openSoapFax, bytes_to_read, stream, timeout, allow_short_read)
2108
2109    def readMarvellFax(self, bytes_to_read, stream=None, timeout=prop.read_timeout, allow_short_read=True):
2110        return self.__readChannel(self.openMarvellFax, bytes_to_read, stream, timeout, allow_short_read)
2111
2112    def readWifiConfig(self, bytes_to_read, stream=None, timeout=prop.read_timeout, allow_short_read=True):
2113        return self.__readChannel(self.openWifiConfig, bytes_to_read, stream, timeout, allow_short_read)
2114
2115#Common handling of reading chunked or unchunked data from LEDM devices
2116    def readLEDMData(dev, func, reply, timeout=6):
2117
2118        END_OF_DATA=to_bytes_utf8("0\r\n\r\n")
2119        bytes_requested = 1024
2120        bytes_remaining = 0
2121        chunkedFlag = True
2122
2123        bytes_read = func(bytes_requested, reply, timeout)
2124
2125        for line in reply.getvalue().splitlines():
2126            if line.lower().find(to_bytes_utf8("content-length")) != -1:
2127                 bytes_remaining = int(line.split(to_bytes_utf8(":"))[1])
2128                 chunkedFlag = False
2129                 break
2130
2131        xml_data_start = reply.getvalue().find(to_bytes_utf8("<?xml"))
2132        if (xml_data_start != -1):
2133            bytes_remaining = bytes_remaining - (len(reply.getvalue())  - xml_data_start)
2134
2135        while bytes_read > 0:
2136            temp_buf = xStringIO()
2137            bytes_read = func(bytes_requested, temp_buf, timeout)
2138
2139            reply.write(temp_buf.getvalue())
2140
2141            if not chunkedFlag:     # Unchunked data
2142                bytes_remaining = bytes_remaining - bytes_read
2143                if bytes_remaining <= 0:
2144                    break
2145            elif END_OF_DATA == temp_buf.getvalue():   # Chunked data end
2146                    break
2147
2148
2149
2150    def __readChannel(self, opener, bytes_to_read, stream=None,
2151                      timeout=prop.read_timeout, allow_short_read=False):
2152
2153        channel_id = opener()
2154
2155        log.debug("Reading channel %d (device-id=%d, bytes_to_read=%d, allow_short=%s, timeout=%d)..." %
2156            (channel_id, self.device_id, bytes_to_read, allow_short_read, timeout))
2157
2158        num_bytes = 0
2159
2160        if stream is None:
2161            buffer = to_bytes_utf8('')
2162
2163        while True:
2164            result_code, data = \
2165                hpmudext.read_channel(self.device_id, channel_id, bytes_to_read, timeout)
2166
2167            log.debug("Result code=%d" % result_code)
2168
2169            l = len(data)
2170
2171            if result_code == hpmudext.HPMUD_R_IO_TIMEOUT:
2172                log.debug("I/O timeout")
2173                break
2174
2175            if result_code != hpmudext.HPMUD_R_OK:
2176                log.error("Channel read error")
2177                raise Error(ERROR_DEVICE_IO_ERROR)
2178
2179            if not l:
2180                log.debug("End of data")
2181                break
2182
2183            if stream is None:
2184                buffer = to_bytes_utf8('').join([buffer, data])
2185            else:
2186                stream.write(data)
2187
2188            num_bytes += l
2189
2190            if self.callback is not None:
2191                self.callback()
2192
2193            if num_bytes == bytes_to_read:
2194                log.debug("Full read complete.")
2195                break
2196
2197            if allow_short_read and num_bytes < bytes_to_read:
2198                log.debug("Allowed short read of %d of %d bytes complete." % (num_bytes, bytes_to_read))
2199                break
2200
2201        if stream is None:
2202            log.debug("Returned %d total bytes in buffer." % num_bytes)
2203            return buffer
2204        else:
2205            log.debug("Saved %d total bytes to stream." % num_bytes)
2206            return num_bytes
2207
2208
2209    def writePrint(self, data):
2210        return self.__writeChannel(self.openPrint, data)
2211
2212    def writePCard(self, data):
2213        return self.__writeChannel(self.openPCard, data)
2214
2215    def writeFax(self, data):
2216        return self.__writeChannel(self.openFax, data)
2217
2218    def writeEWS(self, data):
2219        return self.__writeChannel(self.openEWS, data)
2220
2221    def writeEWS_LEDM(self, data):
2222        return self.__writeChannel(self.openEWS_LEDM, data)
2223
2224    def writeLEDM(self, data):
2225        return self.__writeChannel(self.openLEDM, data)
2226
2227    def writeMarvell_EWS(self, data):
2228        return self.__writeChannel(self.openMarvell_EWS, data)
2229
2230    def writeCfgDownload(self, data):
2231        return self.__writeChannel(self.openCfgDownload, data)
2232
2233    def writeSoapFax(self, data):
2234        return self.__writeChannel(self.openSoapFax, data)
2235
2236    def writeMarvellFax(self, data):
2237        if not isinstance(data, bytes) and hasattr(data, 'tobytes'):   # hasattr function used for supporting 2.6
2238            data = data.tobytes()
2239        return self.__writeChannel(self.openMarvellFax, data)
2240
2241    def writeWifiConfig(self, data):
2242        return self.__writeChannel(self.openWifiConfig, data)
2243
2244    def __writeChannel(self, opener, data):
2245        channel_id = opener()
2246        buffer, bytes_out, total_bytes_to_write = data, 0, len(data)
2247        log.debug("Writing %d bytes to channel %d (device-id=%d)..." % (total_bytes_to_write, channel_id, self.device_id))
2248
2249        while len(buffer) > 0:
2250            result_code, bytes_written = \
2251                hpmudext.write_channel(self.device_id, channel_id,
2252                    buffer[:prop.max_message_len])
2253
2254            log.debug("Result code=%d" % result_code)
2255
2256            if result_code != hpmudext.HPMUD_R_OK:
2257                log.error("Channel write error")
2258                raise Error(ERROR_DEVICE_IO_ERROR)
2259
2260            buffer = buffer[prop.max_message_len:]
2261            bytes_out += bytes_written
2262
2263            if self.callback is not None:
2264                self.callback()
2265
2266        if total_bytes_to_write != bytes_out:
2267            raise Error(ERROR_DEVICE_IO_ERROR)
2268
2269        return bytes_out
2270
2271
2272    def writeEmbeddedPML(self, oid, value, style=1, direct=True):
2273        if style == 1:
2274            func = pcl.buildEmbeddedPML2
2275        else:
2276            func = pcl.buildEmbeddedPML
2277
2278        data = func(pcl.buildPCLCmd('&', 'b', 'W',
2279                     pml.buildEmbeddedPMLSetPacket(oid[0],
2280                                                    value,
2281                                                    oid[1])))
2282
2283        #log.log_data(data)
2284
2285        self.printData(data, direct=direct, raw=True)
2286
2287    def post(self, url, post):
2288        status_type = self.mq.get('status-type', STATUS_TYPE_NONE)
2289        data = """POST %s HTTP/1.1\r
2290Connection: Keep-alive\r
2291User-agent: hplip/2.0\r
2292Host: %s\r
2293Content-type: text/xml\r
2294Content-length: %d\r
2295\r
2296%s""" % (url, self.http_host, len(post), post)
2297        log.log_data(data)
2298        if status_type == STATUS_TYPE_LEDM:
2299            log.debug("status-type: %d" % status_type)
2300            self.writeEWS_LEDM(data)
2301            response = BytesIO()
2302
2303            self.readLEDMData(self.readEWS_LEDM, response)
2304
2305            response = response.getvalue()
2306            log.log_data(response)
2307            self.closeEWS_LEDM()
2308
2309        elif status_type == STATUS_TYPE_LEDM_FF_CC_0:
2310            log.debug("status-type: %d" % status_type)
2311            self.writeLEDM(data)
2312            response = BytesIO()
2313
2314            self.readLEDMData(self.readLEDM, response)
2315
2316            response = response.getvalue()
2317            log.log_data(response)
2318            self.closeLEDM()
2319
2320        else:
2321            log.error("Not an LEDM status-type: %d" % status_type)
2322
2323        match = http_result_pat.match(to_string_utf8(response))
2324        if match is None: return HTTP_OK
2325        try:
2326            code = int(match.group(1))
2327        except (ValueError, TypeError):
2328            code = HTTP_ERROR
2329
2330        return code == HTTP_OK
2331
2332    def printGzipFile(self, file_name, printer_name=None, direct=False, raw=True, remove=False):
2333        return self.printFile(file_name, printer_name, direct, raw, remove)
2334
2335    def printParsedGzipPostscript(self, print_file, printer_name=None):
2336        # always: direct=False, raw=False, remove=True
2337        try:
2338            os.stat(print_file)
2339        except OSError:
2340            log.error("File not found: %s" % print_file)
2341            return
2342
2343        temp_file_fd, temp_file_name = utils.make_temp_file()
2344        f = gzip.open(print_file, 'r')
2345
2346        x = f.readline()
2347        while not x.startswith(to_bytes_utf8('%PY_BEGIN')):
2348            os.write(temp_file_fd, x)
2349            x = f.readline()
2350
2351        sub_lines = []
2352        x = f.readline()
2353        while not x.startswith(to_bytes_utf8('%PY_END')):
2354            sub_lines.append(x)
2355            x = f.readline()
2356
2357        SUBS = {'VERSION' : prop.version,
2358                 'MODEL'   : self.model_ui,
2359                 'URI'     : self.device_uri,
2360                 'BUS'     : self.bus,
2361                 'SERIAL'  : self.serial,
2362                 'IP'      : self.host,
2363                 'PORT'    : self.port,
2364                 'DEVNODE' : self.dev_file,
2365                 }
2366
2367        if self.bus == 'net' :
2368            SUBS['DEVNODE'] = 'n/a'
2369        else:
2370            SUBS['IP']= 'n/a'
2371            SUBS['PORT'] = 'n/a'
2372
2373        if PY3:
2374            sub_lines = [s.decode('utf-8') for s in sub_lines]
2375
2376
2377        for s in sub_lines:
2378            os.write(temp_file_fd, to_bytes_utf8((s % SUBS)))
2379
2380
2381        os.write(temp_file_fd, f.read())
2382        f.close()
2383        os.close(temp_file_fd)
2384
2385        self.printFile(temp_file_name, printer_name, direct=False, raw=False, remove=True)
2386
2387    def printFile(self, file_name, printer_name=None, direct=False, raw=True, remove=False):
2388        is_gzip = os.path.splitext(file_name)[-1].lower() == '.gz'
2389
2390        if printer_name is None:
2391            printer_name = self.first_cups_printer
2392
2393            if not printer_name:
2394                raise Error(ERROR_NO_CUPS_QUEUE_FOUND_FOR_DEVICE)
2395
2396        log.debug("Printing file '%s' to queue '%s' (gzip=%s, direct=%s, raw=%s, remove=%s)" %
2397                   (file_name, printer_name, is_gzip, direct, raw, remove))
2398
2399        if direct: # implies raw==True
2400            if is_gzip:
2401                self.writePrint(gzip.open(file_name, 'r').read())
2402            else:
2403                self.writePrint(open(file_name, 'r').read())
2404
2405        else:
2406            if not utils.which('lpr'):
2407                lp_opt = ''
2408
2409                if raw:
2410                    lp_opt = '-oraw'
2411
2412                if is_gzip:
2413                    c = 'gunzip -c %s | lp -c -d%s %s' % (file_name, printer_name, lp_opt)
2414                else:
2415                    c = 'lp -c -d%s %s %s' % (printer_name, lp_opt, file_name)
2416
2417                exit_code = os_utils.execute(c)
2418
2419                if exit_code != 0:
2420                    log.error("Print command failed with exit code %d!" % exit_code)
2421
2422                if remove:
2423                    os.remove(file_name)
2424
2425            else:
2426                raw_str, rem_str = '', ''
2427                if raw: raw_str = '-o raw'
2428                if remove: rem_str = '-r'
2429
2430                if is_gzip:
2431                    c = 'gunzip -c %s | lpr %s %s -P%s' % (file_name, raw_str, rem_str, printer_name)
2432                else:
2433                    c = 'lpr -P%s %s %s %s' % (printer_name, raw_str, rem_str, file_name)
2434
2435                exit_code = os_utils.execute(c)
2436
2437                if exit_code != 0:
2438                    log.error("Print command failed with exit code %d!" % exit_code)
2439
2440
2441    def printTestPage(self, printer_name=None):
2442        return self.printParsedGzipPostscript(os.path.join( prop.home_dir, 'data',
2443                                              'ps', 'testpage.ps.gz' ), printer_name)
2444
2445
2446    def printData(self, data, printer_name=None, direct=True, raw=True):
2447        if direct:
2448            self.writePrint(data)
2449        else:
2450            temp_file_fd, temp_file_name = utils.make_temp_file()
2451            os.write(temp_file_fd, data)
2452            os.close(temp_file_fd)
2453
2454            self.printFile(temp_file_name, printer_name, False, raw, remove=True)
2455
2456
2457    def cancelJob(self, jobid):
2458        cups.cancelJob(jobid)
2459        self.error_code = STATUS_PRINTER_CANCELING
2460        self.sendEvent(self.error_code)
2461
2462
2463    def queryHistory(self):
2464        result = []
2465
2466        if self.dbus_avail:
2467            try:
2468                device_uri, history = self.service.GetHistory(self.device_uri)
2469            except dbus.exceptions.DBusException as e:
2470                log.error("dbus call to GetHistory() failed.")
2471                return []
2472
2473            history.reverse()
2474
2475            for h in history:
2476                result.append(Event(*tuple(h)))
2477
2478            try:
2479                self.error_code = result[0].event_code
2480            except IndexError:
2481                self.error_code = STATUS_UNKNOWN
2482
2483            self.error_state = STATUS_TO_ERROR_STATE_MAP.get(self.error_code, ERROR_STATE_CLEAR)
2484
2485        else:
2486            self.error_code = STATUS_UNKNOWN
2487            self.error_state = ERROR_STATE_CLEAR
2488
2489        self.hist = result
2490        return result
2491
2492    def getEWSUrl(self, url, stream):
2493        try:
2494            if self.is_local:
2495                url2 = "%s&loc=%s" % (self.device_uri.replace('hpfax:', 'hp:'), url)
2496                data = self
2497            else:
2498                url2 = "http://%s%s" % (self.host, url)
2499                if self.zc:
2500                    status, ip = hpmudext.get_zc_ip_address(self.zc)
2501                    if status == hpmudext.HPMUD_R_OK:
2502                        url2 = "http://%s%s" % (ip, url)
2503                data = None
2504
2505            log.debug("Opening: %s" % url2)
2506            opener = LocalOpener({})
2507            try:
2508                f = opener.open(url2, data)
2509
2510            except Error:
2511                log.error("Status read failed: %s" % url2)
2512                stream.seek(0)
2513                stream.truncate()
2514            else:
2515                try:
2516                    stream.write(f.fp.read())
2517                    #stream.write(f)
2518                finally:
2519                    f.close()
2520
2521        finally:
2522            self.closeEWS()
2523
2524    def getEWSUrl_LEDM(self, url, stream, footer=""):
2525        try:
2526            url2 = "%s&loc=%s" % (self.device_uri.replace('hpfax:', 'hp:'), url)
2527            data = self
2528            opener = LocalOpenerEWS_LEDM({})
2529            try:
2530                if footer:
2531                    return opener.open_hp(url2, data, footer)
2532                else:
2533                    return opener.open_hp(url2, data)
2534            except Error:
2535                log.debug("Status read failed: %s" % url2)
2536        finally:
2537            self.closeEWS_LEDM()
2538
2539    def getUrl_LEDM(self, url, stream, footer=""):
2540        try:
2541            url2 = "%s&loc=%s" % (self.device_uri.replace('hpfax:', 'hp:'), url)
2542            data = self
2543            opener = LocalOpener_LEDM({})
2544            try:
2545                if footer:
2546                    return opener.open_hp(url2, data, footer)
2547                else:
2548                    return opener.open_hp(url2, data)
2549            except Error:
2550                log.debug("Status read failed: %s" % url2)
2551
2552        finally:
2553            self.closeLEDM()
2554
2555    def FetchLEDMUrl(self, url, footer=""):
2556        data_fp = BytesIO()
2557        if footer:
2558            data = self.getUrl_LEDM(url, data_fp, footer)
2559        else:
2560            data = self.getUrl_LEDM(url, data_fp)
2561        if data:
2562            data = data.split(to_bytes_utf8('\r\n\r\n'), 1)[1]
2563            if data:
2564                data = status.ExtractXMLData(data)
2565        return data
2566
2567#-------------------------For LEDM SOAP PROTOCOL(FAX) Devices----------------------------------------------------------------------#
2568
2569    def FetchEWS_LEDMUrl(self, url, footer=""):
2570        data_fp = BytesIO()
2571        if footer:
2572            data = self.getEWSUrl_LEDM(url, data_fp, footer)
2573        else:
2574            data = self.getEWSUrl_LEDM(url, data_fp)
2575        if data:
2576            data = data.split(to_bytes_utf8('\r\n\r\n'), 1)[1]
2577            if data:
2578                data = status.ExtractXMLData(data)
2579        return data
2580
2581    def readAttributeFromXml_EWS(self, uri, attribute):
2582        stream = BytesIO()
2583        data = self.FetchEWS_LEDMUrl(uri)
2584        if not data:
2585            log.error("Unable To read the XML data from device")
2586            return ""
2587        xmlDict = utils.XMLToDictParser().parseXML(data)
2588
2589        try:
2590            return xmlDict[attribute]
2591        except:
2592            return str("")
2593
2594#---------------------------------------------------------------------------------------------------#
2595
2596    def readAttributeFromXml(self,uri,attribute):
2597        stream = BytesIO()
2598        data = self.FetchLEDMUrl(uri)
2599        if not data:
2600            log.error("Unable To read the XML data from device")
2601            return ""
2602        xmlDict = utils.XMLToDictParser().parseXML(data )
2603        try:
2604            return xmlDict[attribute]
2605        except:
2606            return str("")
2607
2608
2609    def downloadFirmware(self, usb_bus_id=None, usb_device_id=None): # Note: IDs not currently used
2610        ok = False
2611        filename = os.path.join(prop.data_dir, "firmware", self.model.lower() + '.fw.gz')
2612        log.debug(filename)
2613
2614        if os.path.exists(filename):
2615            log.debug("Downloading firmware file '%s'..." % filename)
2616
2617            # Write to port directly (no MUD) so that HAL can enumerate the printer
2618            if 0: # this currently doesn't work because usblp is loaded...
2619            #if usb_bus_id is not None and usb_device_id is not None:
2620                try:
2621                    p = "/dev/bus/usb/%s/%s" % (usb_bus_id, usb_device_id)
2622                    log.debug("Writing to %s..." % p)
2623                    f = os.open(p, os.O_RDWR)
2624                    x = gzip.open(filename).read()
2625                    os.write(f, x)
2626                    os.close(f)
2627                    ok = True
2628                    log.debug("OK")
2629                except (OSError, IOError) as e:
2630                    log.error("An error occured: %s" % e)
2631            else:
2632                try:
2633                    self.openPrint()
2634                    bytes_written = self.writePrint(gzip.open(filename).read())
2635                    log.debug("%s bytes downloaded." % utils.commafy(bytes_written))
2636                    self.closePrint()
2637                    ok = True
2638                    log.debug("OK")
2639                except Error as e:
2640                    log.error("An error occured: %s" % e.msg)
2641        else:
2642            log.error("Firmware file '%s' not found." % filename)
2643
2644        return ok
2645
2646
2647
2648
2649
2650
2651# URLs: hp:/usb/HP_LaserJet_3050?serial=00XXXXXXXXXX&loc=/hp/device/info_device_status.xml
2652class LocalOpener(urllib_request.URLopener):
2653    def open_hp(self, url, dev):
2654        log.debug("open_hp(%s)" % url)
2655
2656        match_obj = http_pat_url.search(url)
2657        bus = match_obj.group(1) or ''
2658        model = match_obj.group(2) or ''
2659        serial = match_obj.group(3) or ''
2660        device = match_obj.group(4) or ''
2661        loc = match_obj.group(5) or ''
2662
2663        dev.openEWS()
2664        dev.writeEWS("""GET %s HTTP/1.0\nContent-Length:0\nHost:localhost\nUser-Agent:hplip\n\n""" % loc)
2665
2666        reply = xStringIO()
2667        while dev.readEWS(8192, reply, timeout=1):
2668            pass
2669
2670        reply.seek(0)
2671        log.log_data(reply.getvalue())
2672
2673        response = http_client.HTTPResponse(reply)
2674        response.begin()
2675
2676        if response.status != http_client.OK:
2677            raise Error(ERROR_DEVICE_STATUS_NOT_AVAILABLE)
2678        else:
2679            return response#.fp
2680
2681# URLs: hp:/usb/HP_OfficeJet_7500?serial=00XXXXXXXXXX&loc=/hp/device/info_device_status.xml
2682class LocalOpenerEWS_LEDM(urllib_request.URLopener):
2683    def open_hp(self, url, dev, foot=""):
2684        log.debug("open_hp(%s)" % url)
2685
2686        match_obj = http_pat_url.search(url)
2687        loc = url.split("=")[url.count("=")]
2688
2689        dev.openEWS_LEDM()
2690        if foot:
2691            if "PUT" in foot:
2692                dev.writeEWS_LEDM("""%s""" % foot)
2693            else:
2694                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)))
2695                dev.writeEWS_LEDM("""%s""" % foot)
2696        else:
2697            dev.writeEWS_LEDM("""GET %s HTTP/1.1\r\nAccept: text/plain\r\nHost:localhost\r\nUser-Agent:hplip\r\n\r\n""" % loc)
2698
2699        reply = xStringIO()
2700
2701        dev.readLEDMData(dev.readEWS_LEDM,reply)
2702
2703        reply.seek(0)
2704        return reply.getvalue()
2705
2706
2707# URLs: hp:/usb/HP_OfficeJet_7500?serial=00XXXXXXXXXX&loc=/hp/device/info_device_status.xml
2708class LocalOpener_LEDM(urllib_request.URLopener):
2709    def open_hp(self, url, dev, foot=""):
2710        log.debug("open_hp(%s)" % url)
2711
2712        match_obj = http_pat_url.search(url)
2713        loc = url.split("=")[url.count("=")]
2714
2715        dev.openLEDM()
2716        if foot:
2717            if "PUT" in foot:
2718                dev.writeLEDM("""%s""" % foot)
2719            else:
2720                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)))
2721                dev.writeLEDM("""%s""" % foot)
2722        else:
2723            dev.writeLEDM("""GET %s HTTP/1.1\r\nAccept: text/plain\r\nHost:localhost\r\nUser-Agent:hplip\r\n\r\n""" % loc)
2724
2725        reply = xStringIO()
2726
2727
2728        dev.readLEDMData(dev.readLEDM,reply)
2729
2730        reply.seek(0)
2731        return reply.getvalue()
2732