Hide keyboard shortcuts

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. 

6 

7import os 

8 

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 

14 

15log = Logger("printing") 

16 

17ADD_LOCAL_PRINTERS = envbool("XPRA_ADD_LOCAL_PRINTERS", False) 

18PRINTER_LOCATION_STRING = os.environ.get("XPRA_PRINTER_LOCATION_STRING", "via xpra") 

19 

20def printer_name(name): 

21 try: 

22 return name.decode("utf8") 

23 except Exception: 

24 return bytestostr(name) 

25 

26 

27class FilePrintMixin(FileTransferHandler, StubSourceMixin): 

28 

29 @classmethod 

30 def is_needed(cls, caps : typedict) -> bool: 

31 return bool(caps.boolget("file-transfer") or caps.boolget("printing")) 

32 

33 

34 def init_state(self): 

35 self.printers = {} 

36 self.printers_added = set() 

37 #duplicated from clientinfo mixin 

38 self.machine_id = "" 

39 

40 def cleanup(self): 

41 self.remove_printers() 

42 

43 def parse_client_caps(self, c : dict): 

44 FileTransferHandler.parse_file_transfer_caps(self, c) 

45 self.machine_id = c.strget("machine_id") 

46 

47 def get_info(self) -> dict: 

48 return { 

49 "printers" : self.printers, 

50 "file-transfers" : FileTransferHandler.get_info(self), 

51 } 

52 

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)) 

61 

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) 

126 

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] 

133 

134 

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) 

160 

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) 

167 

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)