Your IP : 216.73.216.48
## system-config-printer
## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Red Hat, Inc.
## Copyright (C) 2006 Florian Festi <ffesti@redhat.com>
## Copyright (C) 2007, 2008, 2009 Tim Waugh <twaugh@redhat.com>
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
import cupshelpers
from debug import *
import errno
import socket, time
import gtk
from timedops import TimedOperation
import subprocess
import threading
import errno
import cups
import gobject
import smburi
try:
import pysmb
PYSMB_AVAILABLE=True
except:
PYSMB_AVAILABLE=False
class pysmb:
class AuthContext:
pass
def wordsep (line):
words = []
escaped = False
quoted = False
in_word = False
word = ''
n = len (line)
for i in range (n):
ch = line[i]
if escaped:
word += ch
escaped = False
continue
if ch == '\\':
in_word = True
escaped = True
continue
if in_word:
if quoted:
if ch == '"':
quoted = False
else:
word += ch
elif ch.isspace ():
words.append (word)
word = ''
in_word = False
elif ch == '"':
quoted = True
else:
word += ch
else:
if ch == '"':
in_word = True
quoted = True
elif not ch.isspace ():
in_word = True
word += ch
if word != '':
words.append (word)
return words
### should be ['network', 'foo bar', ' ofoo', '"', '2 3']
##print wordsep ('network "foo bar" \ ofoo "\\"" 2" "3')
def open_socket(hostname, port):
try:
host, port = hostname.split(":", 1)
except ValueError:
host = hostname
s = None
try:
ai = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
socket.SOCK_STREAM)
except socket.gaierror:
ai = []
for res in ai:
af, socktype, proto, canonname, sa = res
try:
s = socket.socket(af, socktype, proto)
s.settimeout(0.5)
except socket.error, msg:
s = None
continue
try:
s.connect(sa)
except socket.error, msg:
s.close()
s = None
continue
break
return s
class LpdServer:
def __init__(self, hostname):
self.hostname = hostname
self.max_lpt_com = 8
self.stop = False
def probe_queue(self,name, result):
s = open_socket(self.hostname, 515)
if not s:
return None
print name
try:
s.send('\2%s\n' % name) # cmd send job to queue
data = s.recv(1024) # receive status
print repr(data)
except socket.error, msg:
print msg
try:
s.close ()
except:
pass
return False
if len(data)>0 and ord(data[0])==0:
try:
s.send('\1\n') # abort job again
s.close ()
except:
pass
result.append(name)
return True
try:
s.close()
except:
pass
return False
def get_possible_queue_names (self):
candidate = ["PASSTHRU", "ps", "lp", "PORT1", ""]
for nr in range (self.max_lpt_com):
candidate.extend (["LPT%d" % nr,
"LPT%d_PASSTHRU" % nr,
"COM%d" % nr,
"COM%d_PASSTHRU" % nr])
for nr in range (50):
candidate.append ("pr%d" % nr)
return candidate
def destroy(self):
debugprint ("LpdServer exiting: destroy called")
self.stop = True
def probe(self):
result = []
for name in self.get_possible_queue_names ():
while gtk.events_pending ():
gtk.main_iteration ()
if self.stop:
break
found = self.probe_queue(name, result)
if found == None:
# Couldn't even connect.
break
if not found and name.startswith ("pr"):
break
time.sleep(0.1) # avoid DOS and following counter measures
return result
class BackgroundSmbAuthContext(pysmb.AuthContext):
"""An SMB AuthContext class that is only ever run from
a non-GUI thread."""
def __init__ (self, *args, **kwargs):
self._gui_event = threading.Event ()
pysmb.AuthContext.__init__ (self, *args, **kwargs)
def _do_perform_authentication (self):
gtk.gdk.threads_enter ()
result = pysmb.AuthContext.perform_authentication (self)
gtk.gdk.threads_leave ()
self._do_perform_authentication_result = result
self._gui_event.set ()
def perform_authentication (self):
if (self.passes == 0 or
not self.has_failed or
not self.auth_called or
(self.auth_called and not self.tried_guest)):
# Safe to call the base function. It won't try any UI stuff.
return pysmb.AuthContext.perform_authentication (self)
self._gui_event.clear ()
gobject.timeout_add (1, self._do_perform_authentication)
self._gui_event.wait ()
return self._do_perform_authentication_result
class PrinterFinder:
def __init__ (self):
self.quit = False
def find (self, hostname, callback_fn):
self.hostname = hostname
self.callback_fn = callback_fn
self.op = TimedOperation (self._do_find, callback=lambda x, y: None)
def cancel (self):
self.op.cancel ()
self.quit = True
def _do_find (self):
self._cached_attributes = dict()
for fn in [self._probe_jetdirect,
self._probe_ipp,
self._probe_snmp,
self._probe_lpd,
self._probe_hplip,
self._probe_smb]:
if self.quit:
return
try:
fn ()
except Exception, e:
nonfatalException ()
# Signal that we've finished.
if not self.quit:
self.callback_fn (None)
def _new_device (self, uri, info, location = None):
device_dict = { 'device-class': 'network',
'device-info': "%s" % info }
if location:
device_dict['device-location']=location
device_dict.update (self._cached_attributes)
new_device = cupshelpers.Device (uri, **device_dict)
debugprint ("Device found: %s" % uri)
self.callback_fn (new_device)
def _probe_snmp (self):
# Run the CUPS SNMP backend, pointing it at the host.
null = file ("/dev/null", "r+")
try:
debugprint ("snmp: trying")
p = subprocess.Popen (args=["/usr/lib/cups/backend/snmp",
self.hostname],
close_fds=True,
stdin=null,
stdout=subprocess.PIPE,
stderr=null)
except OSError, e:
debugprint ("snmp: no good")
if e == errno.ENOENT:
return
raise
(stdout, stderr) = p.communicate ()
if p.returncode != 0:
debugprint ("snmp: no good (return code %d)" % p.returncode)
return
if self.quit:
debugprint ("snmp: no good")
return
for line in stdout.split ('\n'):
words = wordsep (line)
n = len (words)
if n == 6:
(device_class, uri, make_and_model,
info, device_id, device_location) = words
elif n == 5:
(device_class, uri, make_and_model, info, device_id) = words
elif n == 4:
(device_class, uri, make_and_model, info) = words
else:
continue
device_dict = { 'device-class': device_class,
'device-make-and-model': make_and_model,
'device-info': info }
if n == 5:
debugprint ("snmp: Device ID found:\n%s" %
device_id)
device_dict['device-id'] = device_id
if n == 6:
device_dict['device-location'] = device_location
device = cupshelpers.Device (uri, **device_dict)
debugprint ("Device found: %s" % uri)
self.callback_fn (device)
# Cache the make and model for use by other search methods
# that are not able to determine it.
self._cached_attributes['device-make-and-model'] = make_and_model
self._cached_attributes['device_id'] = device_id
debugprint ("snmp: done")
def _probe_lpd (self):
debugprint ("lpd: trying")
lpd = LpdServer (self.hostname)
for name in lpd.get_possible_queue_names ():
if self.quit:
debugprint ("lpd: no good")
return
found = lpd.probe_queue (name, [])
if found == None:
# Couldn't even connect.
debugprint ("lpd: couldn't connect")
break
if found:
uri = "lpd://%s/%s" % (self.hostname, name)
self._new_device(uri, self.hostname)
if not found and name.startswith ("pr"):
break
time.sleep(0.1) # avoid DOS and following counter measures
debugprint ("lpd: done")
def _probe_hplip (self):
null = file ("/dev/null", "r+")
try:
debugprint ("hplip: trying")
p = subprocess.Popen (args=["hp-makeuri", "-c", self.hostname],
close_fds=True,
stdin=null,
stdout=subprocess.PIPE,
stderr=null)
except OSError, e:
if e == errno.ENOENT:
return
raise
(stdout, stderr) = p.communicate ()
if p.returncode != 0:
debugprint ("hplip: no good (return code %d)" % p.returncode)
return
if self.quit:
debugprint ("hplip: no good")
return
uri = stdout.strip ()
debugprint ("hplip: uri is %s" % uri)
if uri.find (":") != -1:
self._new_device(uri, uri)
debugprint ("hplip: done")
def _probe_smb (self):
if not PYSMB_AVAILABLE:
return
smbc_auth = BackgroundSmbAuthContext ()
debug = 0
if get_debugging ():
debug = 10
ctx = pysmb.smbc.Context (debug=debug,
auth_fn=smbc_auth.callback)
entries = []
uri = "smb://%s/" % self.hostname
debugprint ("smb: trying")
try:
while smbc_auth.perform_authentication () > 0:
if self.quit:
debugprint ("smb: no good")
return
try:
entries = ctx.opendir (uri).getdents ()
except Exception, e:
smbc_auth.failed (e)
except RuntimeError, (e, s):
if e not in [errno.ENOENT, errno.EACCES, errno.EPERM]:
debugprint ("Runtime error: %s" % repr ((e, s)))
except:
nonfatalException ()
if self.quit:
debugprint ("smb: no good")
return
for entry in entries:
if entry.smbc_type == pysmb.smbc.PRINTER_SHARE:
uri = "smb://%s/%s" % (smburi.urlquote (self.hostname),
smburi.urlquote (entry.name))
info = "SMB (%s)" % self.hostname
self._new_device(uri, info)
debugprint ("smb: done")
def _probe_jetdirect (self):
port = 9100 #jetdirect
sock_address = (self.hostname, port)
debugprint ("jetdirect: trying")
s = open_socket(self.hostname, port)
if not s:
debugprint ("jetdirect: %s:%d CLOSED" % sock_address)
else:
# port is open so assume its a JetDirect device
debugprint ("jetdirect %s:%d OPEN" % sock_address)
uri = "socket://%s:%d" % sock_address
info = "JetDirect (%s)" % self.hostname
self._new_device(uri, info)
s.close ()
debugprint ("jetdirect: done")
def _probe_ipp (self):
debugprint ("ipp: trying")
try:
ai = socket.getaddrinfo(self.hostname, 631, socket.AF_UNSPEC,
socket.SOCK_STREAM)
except socket.gaierror:
debugprint ("ipp: can't resolve %s" % self.hostname)
debugprint ("ipp: no good")
return
for res in ai:
af, socktype, proto, canonname, sa = res
if (af == socket.AF_INET and sa[0] == '127.0.0.1' or
af == socket.AF_INET6 and sa[0] == '::1'):
debugprint ("ipp: do not probe local cups server")
debugprint ("ipp: no good")
return
try:
c = cups.Connection (host = self.hostname)
except RuntimeError:
debugprint ("ipp: can't connect to server/printer")
debugprint ("ipp: no good")
return
try:
printers = c.getPrinters ()
except cups.IPPError:
debugprint ("%s is probably not a cups server but IPP printer" %
self.hostname)
uri = "ipp://%s:631/ipp" % (self.hostname)
info = "IPP (%s)" % self.hostname
self._new_device(uri, info)
debugprint ("ipp: done")
return
for name, queue in printers.iteritems ():
uri = queue['printer-uri-supported']
info = queue['printer-info']
location = queue['printer-location']
self._new_device(uri, info, location)
debugprint ("ipp: done")
if __name__ == '__main__':
import sys
if len (sys.argv) < 2:
print "Need printer address"
sys.exit (1)
set_debugging (True)
loop = gobject.MainLoop ()
def display (device):
if device == None:
loop.quit ()
addr = sys.argv[1]
p = PrinterFinder ()
p.find (addr, display)
loop.run ()