Coverage for /home/antoine/projects/xpra-git/dist/python3/lib64/python/xpra/server/source/fileprint_mixin.py : 29%
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# -*- coding: utf-8 -*-
2# This file is part of Xpra.
3# Copyright (C) 2010-2020 Antoine Martin <antoine@xpra.org>
4# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
5# later version. See the file COPYING for details.
7import os
9from xpra.util import envbool, typedict
10from xpra.os_util import get_machine_id, bytestostr
11from xpra.net.file_transfer import FileTransferHandler
12from xpra.server.source.stub_source_mixin import StubSourceMixin
13from xpra.log import Logger
15log = Logger("printing")
17ADD_LOCAL_PRINTERS = envbool("XPRA_ADD_LOCAL_PRINTERS", False)
18PRINTER_LOCATION_STRING = os.environ.get("XPRA_PRINTER_LOCATION_STRING", "via xpra")
20def printer_name(name):
21 try:
22 return name.decode("utf8")
23 except Exception:
24 return bytestostr(name)
27class FilePrintMixin(FileTransferHandler, StubSourceMixin):
29 @classmethod
30 def is_needed(cls, caps : typedict) -> bool:
31 return bool(caps.boolget("file-transfer") or caps.boolget("printing"))
34 def init_state(self):
35 self.printers = {}
36 self.printers_added = set()
37 #duplicated from clientinfo mixin
38 self.machine_id = ""
40 def cleanup(self):
41 self.remove_printers()
43 def parse_client_caps(self, c : dict):
44 FileTransferHandler.parse_file_transfer_caps(self, c)
45 self.machine_id = c.strget("machine_id")
47 def get_info(self) -> dict:
48 return {
49 "printers" : self.printers,
50 "file-transfers" : FileTransferHandler.get_info(self),
51 }
53 def init_from(self, _protocol, server):
54 self.init_attributes()
55 #copy attributes
56 for x in ("file_transfer", "file_transfer_ask", "file_size_limit", "file_chunks",
57 "printing", "printing_ask", "open_files", "open_files_ask",
58 "open_url", "open_url_ask",
59 "file_ask_timeout", "open_command"):
60 setattr(self, x, getattr(server.file_transfer, x))
62 ######################################################################
63 # printing:
64 def set_printers(self, printers, password_file, auth, encryption, encryption_keyfile):
65 log("set_printers(%s, %s, %s, %s, %s) for %s",
66 printers, password_file, auth, encryption, encryption_keyfile, self)
67 if self.machine_id==get_machine_id() and not ADD_LOCAL_PRINTERS:
68 self.printers = printers
69 log("local client with identical machine id,")
70 log(" not configuring local printers")
71 return
72 if not self.uuid:
73 log.warn("Warning: client did not supply a UUID,")
74 log.warn(" printer forwarding cannot be enabled")
75 return
76 #remove the printers no longer defined
77 #or those whose definition has changed (and we will re-add them):
78 for k in tuple(self.printers.keys()):
79 cpd = self.printers.get(k)
80 npd = printers.get(k)
81 if cpd==npd:
82 #unchanged: make sure we don't try adding it again:
83 printers.pop(k, None)
84 continue
85 if npd is None:
86 log("printer %s no longer exists", k)
87 else:
88 log("printer %s has been modified:", k)
89 log(" was %s", cpd)
90 log(" now %s", npd)
91 #remove it:
92 self.printers.pop(k, None)
93 self.remove_printer(k)
94 #expand it here so the xpraforwarder doesn't need to import anything xpra:
95 attributes = {"display" : os.environ.get("DISPLAY"),
96 "source" : self.uuid}
97 def makeabs(filename):
98 #convert to an absolute path since the backend may run as a different user:
99 return os.path.abspath(os.path.expanduser(filename))
100 if auth:
101 auth_password_file = None
102 try:
103 name, _, authclass, authoptions = auth
104 auth_password_file = authoptions.get("file")
105 log("file for %s / %s: '%s'", name, authclass, password_file)
106 except Exception as e:
107 log.error("Error: cannot forward authentication attributes to printer backend:")
108 log.error(" %s", e)
109 if auth_password_file or password_file:
110 attributes["password-file"] = makeabs(auth_password_file or password_file)
111 if encryption:
112 if not encryption_keyfile:
113 log.error("Error: no encryption keyfile found for printing")
114 else:
115 attributes["encryption"] = encryption
116 attributes["encryption-keyfile"] = makeabs(encryption_keyfile)
117 #if we can, tell it exactly where to connect:
118 if self.unix_socket_paths:
119 #prefer sockets in public paths:
120 attributes["socket-path"] = self.choose_socket_path()
121 log("printer attributes: %s", attributes)
122 for name,props in printers.items():
123 printer = printer_name(name)
124 if printer not in self.printers:
125 self.setup_printer(printer, props, attributes)
127 def choose_socket_path(self) -> str:
128 assert self.unix_socket_paths
129 for x in self.unix_socket_paths:
130 if x.startswith("/tmp") or x.startswith("/var") or x.startswith("/run"):
131 return x
132 return self.unix_socket_paths[0]
135 def setup_printer(self, printer, props, attributes):
136 from xpra.platform.pycups_printing import add_printer
137 props = typedict(props)
138 info = props.strget("printer-info", "")
139 attrs = attributes.copy()
140 attrs["remote-printer"] = printer
141 attrs["remote-device-uri"] = props.strget("device-uri")
142 location = PRINTER_LOCATION_STRING
143 if self.hostname:
144 location = "on %s"
145 if PRINTER_LOCATION_STRING:
146 #ie: on FOO (via xpra)
147 location = "on %s (%s)" % (self.hostname, PRINTER_LOCATION_STRING)
148 try:
149 def printer_added():
150 #once the printer has been added, register it in the list
151 #(so it will be removed on exit)
152 log.info("the remote printer '%s' has been configured", printer)
153 self.printers[printer] = props
154 self.printers_added.add(printer)
155 add_printer(printer, props, info, location, attrs, success_cb=printer_added)
156 except Exception as e:
157 log.warn("Warning: failed to add virtual printer '%s'", printer)
158 log.warn(" %s", e)
159 log("setup_printer(%s, %s, %s)", printer, props, attributes, exc_info=True)
161 def remove_printers(self):
162 if self.machine_id==get_machine_id() and not ADD_LOCAL_PRINTERS:
163 return
164 self.printers = {}
165 for k in tuple(self.printers_added):
166 self.remove_printer(k)
168 def remove_printer(self, name):
169 printer = printer_name(name)
170 try:
171 self.printers_added.remove(printer)
172 except KeyError:
173 log("not removing printer '%s' - since we didn't add it", name)
174 else:
175 try:
176 from xpra.platform.pycups_printing import remove_printer
177 remove_printer(printer)
178 log.info("removed remote printer '%s'", printer)
179 except Exception as e:
180 log("remove_printer(%s)", printer, exc_info=True)
181 log.error("Error: failed to remove printer '%s':", name)
182 log.error(" %s", e)