Coverage for /home/antoine/projects/xpra-git/dist/python3/lib64/python/xpra/client/mixins/fileprint_mixin.py : 32%
Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of Xpra.
2# Copyright (C) 2010-2020 Antoine Martin <antoine@xpra.org>
3# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
4# later version. See the file COPYING for details.
6from xpra.util import envbool, csv, typedict
7from xpra.net.file_transfer import FileTransferHandler
8from xpra.client.mixins.stub_client_mixin import StubClientMixin
9from xpra.log import Logger
11printlog = Logger("printing")
12filelog = Logger("file")
14DELETE_PRINTER_FILE = envbool("XPRA_DELETE_PRINTER_FILE", True)
15SKIP_STOPPED_PRINTERS = envbool("XPRA_SKIP_STOPPED_PRINTERS", True)
18class FilePrintMixin(StubClientMixin, FileTransferHandler):
20 def __init__(self):
21 StubClientMixin.__init__(self)
22 FileTransferHandler.__init__(self)
23 self.printer_attributes = []
24 self.send_printers_timer = 0
25 self.exported_printers = None
26 self.remote_request_file = False
28 def init(self, opts):
29 #printing and file transfer:
30 FileTransferHandler.init_opts(self, opts)
32 def init_authenticated_packet_handlers(self):
33 for packet_type, handler in {
34 "open-url" : self._process_open_url,
35 "send-file" : self._process_send_file,
36 "send-data-request" : self._process_send_data_request,
37 "send-data-response": self._process_send_data_response,
38 "ack-file-chunk" : self._process_ack_file_chunk,
39 "send-file-chunk" : self._process_send_file_chunk,
40 }.items():
41 self.add_packet_handler(packet_type, handler, False)
43 def get_caps(self) -> dict:
44 return self.get_file_transfer_features()
46 def cleanup(self):
47 #we must clean printing before FileTransferHandler, which turns the printing flag off!
48 self.cleanup_printing()
49 FileTransferHandler.cleanup(self)
51 def parse_server_capabilities(self, caps : typedict) -> bool:
52 self.parse_printing_capabilities(caps)
53 self.parse_file_transfer_caps(caps)
54 self.remote_request_file = caps.boolget("request-file", False)
55 return True
57 def parse_printing_capabilities(self, caps : typedict):
58 printlog("parse_printing_capabilities() client printing support=%s", self.printing)
59 if self.printing:
60 server_printing = caps.boolget("printing")
61 printlog("parse_printing_capabilities() server printing support=%s", server_printing)
62 if server_printing:
63 self.printer_attributes = caps.strtupleget("printer.attributes",
64 ("printer-info", "device-uri"))
65 self.timeout_add(1000, self.init_printing)
68 def init_printing(self):
69 try:
70 from xpra.platform.printing import init_printing
71 printlog("init_printing=%s", init_printing)
72 init_printing(self.send_printers)
73 except Exception as e:
74 printlog.error("Error initializing printing support:")
75 printlog.error(" %s", e)
76 self.printing = False
77 else:
78 try:
79 self.do_send_printers()
80 except Exception:
81 printlog.error("Error sending the list of printers:", exc_info=True)
82 self.printing = False
83 printlog("init_printing() enabled=%s", self.printing)
85 def cleanup_printing(self):
86 printlog("cleanup_printing() printing=%s", self.printing)
87 if not self.printing:
88 return
89 self.cancel_send_printers_timer()
90 try:
91 from xpra.platform.printing import cleanup_printing
92 printlog("cleanup_printing=%s", cleanup_printing)
93 cleanup_printing()
94 except ImportError as e:
95 printlog("cleanup_printing()", exc_info=True)
96 except Exception as e:
97 printlog("cleanup_printing()", exc_info=True)
98 printlog.warn("Warning: failed to cleanup printing subsystem:")
99 printlog.warn(" %s", e)
101 def send_printers(self, *args):
102 printlog("send_printers%s timer=%s", args, self.send_printers_timer)
103 #dbus can fire dozens of times for a single printer change
104 #so we wait a bit and fire via a timer to try to batch things together:
105 if self.send_printers_timer:
106 return
107 self.send_printers_timer = self.timeout_add(500, self.do_send_printers)
109 def cancel_send_printers_timer(self):
110 spt = self.send_printers_timer
111 printlog("cancel_send_printers_timer() send_printers_timer=%s", spt)
112 if spt:
113 self.send_printers_timer = None
114 self.source_remove(spt)
116 def do_send_printers(self):
117 try:
118 self.send_printers_timer = None
119 from xpra.platform.printing import get_printers, get_mimetypes
120 try:
121 printers = get_printers()
122 except Exception as e:
123 printlog("%s", get_printers, exc_info=True)
124 printlog.error("Error: cannot access the list of printers")
125 printlog.error(" %s", e)
126 return
127 printlog("do_send_printers() found printers=%s", printers)
128 #remove xpra-forwarded printers to avoid loops and multi-forwards,
129 #also ignore stopped printers
130 #and only keep the attributes that the server cares about (self.printer_attributes)
131 exported_printers = {}
132 def used_attrs(d):
133 #filter attributes so that we only compare things that are actually used
134 if not d:
135 return d
136 return dict((k,v) for k,v in d.items() if k in self.printer_attributes)
137 for k,v in printers.items():
138 device_uri = v.get("device-uri", "")
139 if device_uri:
140 #this is cups specific.. oh well
141 printlog("do_send_printers() device-uri(%s)=%s", k, device_uri)
142 if device_uri.startswith("xpraforwarder"):
143 printlog("do_send_printers() skipping xpra forwarded printer=%s", k)
144 continue
145 state = v.get("printer-state")
146 #"3" if the destination is idle,
147 #"4" if the destination is printing a job,
148 #"5" if the destination is stopped.
149 if state==5 and SKIP_STOPPED_PRINTERS:
150 printlog("do_send_printers() skipping stopped printer=%s", k)
151 continue
152 attrs = used_attrs(v)
153 #add mimetypes:
154 attrs["mimetypes"] = get_mimetypes()
155 exported_printers[k.encode("utf8")] = attrs
156 if self.exported_printers is None:
157 #not been sent yet, ensure we can use the dict below:
158 self.exported_printers = {}
159 elif exported_printers==self.exported_printers:
160 printlog("do_send_printers() exported printers unchanged: %s", self.exported_printers)
161 return
162 #show summary of what has changed:
163 added = tuple(k for k in exported_printers if k not in self.exported_printers)
164 if added:
165 printlog("do_send_printers() new printers: %s", added)
166 removed = tuple(k for k in self.exported_printers if k not in exported_printers)
167 if removed:
168 printlog("do_send_printers() printers removed: %s", removed)
169 modified = tuple(k for k,v in exported_printers.items() if
170 self.exported_printers.get(k)!=v and k not in added)
171 if modified:
172 printlog("do_send_printers() printers modified: %s", modified)
173 printlog("do_send_printers() printers=%s", exported_printers.keys())
174 printlog("do_send_printers() exported printers=%s", csv(str(x) for x in exported_printers))
175 self.exported_printers = exported_printers
176 self.send("printers", self.exported_printers)
177 except Exception:
178 printlog.error("do_send_printers()", exc_info=True)