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