1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 """\
21 X2GoTerminalSession class - core functions for handling your individual X2Go sessions.
22
23 This backend handles X2Go server implementations that respond with session infos
24 via server-side STDOUT and use NX3 as graphical proxy.
25
26 """
27 __NAME__ = 'x2goterminalsession-pylib'
28
29
30 import os
31 import types
32 import gevent
33 import cStringIO
34 import copy
35 import shutil
36 import threading
37
38
39 import x2go.rforward as rforward
40 import x2go.sftpserver as sftpserver
41 import x2go.printqueue as printqueue
42 import x2go.mimebox as mimebox
43 import x2go.log as log
44 import x2go.defaults as defaults
45 import x2go.utils as utils
46 import x2go.x2go_exceptions as x2go_exceptions
47
48
49 from x2go.defaults import X2GOCLIENT_OS as _X2GOCLIENT_OS
50 from x2go.defaults import LOCAL_HOME as _LOCAL_HOME
51 from x2go.defaults import CURRENT_LOCAL_USER as _CURRENT_LOCAL_USER
52 from x2go.defaults import X2GO_CLIENT_ROOTDIR as _X2GO_CLIENT_ROOTDIR
53 from x2go.defaults import X2GO_SESSIONS_ROOTDIR as _X2GO_SESSIONS_ROOTDIR
54 from x2go.defaults import X2GO_GENERIC_APPLICATIONS as _X2GO_GENERIC_APPLICATIONS
55 from x2go.defaults import X2GO_DESKTOPSESSIONS as _X2GO_DESKTOPSESSIONS
56
57 from x2go.backends.info import X2GoServerSessionInfo as _X2GoServerSessionInfo
58 from x2go.backends.info import X2GoServerSessionList as _X2GoServerSessionList
59 from x2go.backends.proxy import X2GoProxy as _X2GoProxy
60 from x2go.backends.printing import X2GoClientPrinting as _X2GoClientPrinting
61
62 _local_color_depth = utils.local_color_depth()
63
65 """\
66 Mechansim that rewrites X2Go server commands into something that gets understood by
67 the server-side script C{x2goruncommand}.
68
69 @param cmd: the current command for execution (as found in the session profile parameter C{cmd})
70 @type cmd: C{str}
71 @param params: an session paramter object
72 @type params: L{X2GoSessionParams}
73
74 @return: the rewritten command for server-side execution
75 @rtype: C{str}
76
77 """
78
79 cmd = cmd or ''
80
81
82 if cmd in _X2GO_DESKTOPSESSIONS.keys():
83 cmd = _X2GO_DESKTOPSESSIONS[cmd]
84
85 if (cmd == 'RDP') and (type(params) == X2GoSessionParams):
86 _depth = params.depth
87 if int(_depth) == 17:
88 _depth = 16
89 if params.geometry == 'fullscreen':
90 cmd = 'rdesktop -f -N %s %s -a %s' % (params.rdp_options, params.rdp_server, _depth)
91 else:
92 cmd = 'rdesktop -g %s -N %s %s -a %s' % (params.geometry, params.rdp_options, params.rdp_server, _depth)
93
94
95 if cmd:
96 cmd = '"%s"' % cmd
97
98 if ((type(params) == X2GoSessionParams) and params.published_applications and cmd == ''):
99 cmd = 'PUBLISHED'
100
101 return cmd
102
103
105 """\
106 In command strings X2Go server scripts expect blanks being rewritten to ,,X2GO_SPACE_CHAR''.
107
108 @param cmd: command that has to be rewritten for passing to the server
109 @type cmd: C{str}
110
111 @return: the command with blanks rewritten to ,,X2GO_SPACE_CHAR''
112 @rtype: C{str}
113
114 """
115
116 if cmd:
117 cmd = cmd.replace(" ", "X2GO_SPACE_CHAR")
118 return cmd
119
120
122 """\
123 The L{X2GoSessionParams} class is used to store all parameters that
124 C{X2GoTerminalSession} backend objects are constructed with.
125
126 """
128 """\
129 Rewrite the X2Go session type, so that the X2Go server
130 can understand it (C{desktop} -> C{D}, etc.).
131
132 Also if the object's C{command} property is a known window
133 manager, the session type will be set to 'D'
134 (i.e. desktop).
135
136 @return: 'D' if session should probably a desktop session,
137 'R' for rootless sessions, 'P' for sessions providing published
138 applications, and 'S' for desktop sharing sessions
139 @rtype: C{str}
140
141 """
142 cmd = self.cmd
143 published = self.published_applications
144
145 if published and self.cmd in ('', 'PUBLISHED'):
146 self.session_type = 'P'
147 self.cmd = 'PUBLISHED'
148 else:
149 if cmd == 'RDP' or cmd.startswith('rdesktop') or cmd.startswith('xfreedrp'):
150 if self.geometry == 'fullscreen': self.session_type = 'D'
151 else: self.session_type = 'R'
152 elif cmd == 'XDMCP':
153 self.session_type = 'D'
154 elif cmd in _X2GO_DESKTOPSESSIONS.keys():
155 self.session_type = 'D'
156 elif os.path.basename(cmd) in _X2GO_DESKTOPSESSIONS.values():
157 self.session_type = 'D'
158
159 if self.session_type in ("D", "desktop"):
160 self.session_type = 'D'
161 elif self.session_type in ("S", "shared", "shadow"):
162 self.session_type = 'S'
163 elif self.session_type in ("R", "rootless", "application"):
164 self.session_type = 'R'
165 elif self.session_type in ("P", "published", "published_applications"):
166 self.session_type = 'P'
167
168 return self.session_type
169
170 - def update(self, **properties_to_be_updated):
171 """\
172 Update all properties in the object L{X2GoSessionParams} object from
173 the passed on dictionary.
174
175 @param properties_to_be_updated: a dictionary with L{X2GoSessionParams}
176 property names as keys und their values to be update in
177 L{X2GoSessionParams} object.
178 @type properties_to_be_updated: C{dict}
179
180 """
181 for key in properties_to_be_updated.keys():
182 setattr(self, key, properties_to_be_updated[key] or '')
183 self.rewrite_session_type()
184
185
187 """\
188 Class for managing X2Go terminal sessions on a remote X2Go server via Paramiko/SSH.
189
190 With the L{X2GoTerminalSessionSTDOUT} class you can start new X2Go sessions, resume suspended
191 sessions or suspend resp. terminate currently running sessions on a
192 connected X2Go server.
193
194 An L{X2GoTerminalSessionSTDOUT} object uses two main data structure classes:
195
196 - L{X2GoSessionParams}: stores all parameters that have been passed to the
197 constructor method.
198
199 - C{X2GoServerSessionInfo*} backend class: when starting or resuming a session, an object of this class
200 will be used to store all information retrieved from the X2Go server.
201
202 The terminal session instance works closely together (i.e. depends on) a connected control
203 session instance (e.g. L{X2GoControlSessionSTDOUT}). You never should use either of them as a standalone
204 instance. Both, control session and terminal session(s) get managed/controlled via L{X2GoSession} instances.
205
206 """
207 - def __init__(self, control_session, session_info=None,
208 geometry="800x600", depth=_local_color_depth, link="adsl", pack="16m-jpeg-9", dpi='',
209 cache_type="unix-kde",
210 kbtype='null/null', kblayout='null', kbvariant='null',
211 session_type="application", snd_system='pulse', snd_port=4713, cmd=None,
212 published_applications=False,
213 set_session_title=False, session_title="", applications=[],
214 rdp_server=None, rdp_options=None,
215 xdmcp_server=None,
216 convert_encoding=False, server_encoding='UTF-8', client_encoding='UTF-8',
217 rootdir=None,
218 profile_name='UNKNOWN', profile_id=utils._genSessionProfileId(),
219 print_action=None, print_action_args={},
220 info_backend=_X2GoServerSessionInfo,
221 list_backend=_X2GoServerSessionList,
222 proxy_backend=_X2GoProxy, proxy_options={},
223 printing_backend=_X2GoClientPrinting,
224 client_rootdir=os.path.join(_LOCAL_HOME, _X2GO_CLIENT_ROOTDIR),
225 sessions_rootdir=os.path.join(_LOCAL_HOME, _X2GO_SESSIONS_ROOTDIR),
226 session_instance=None,
227 logger=None, loglevel=log.loglevel_DEFAULT):
228 """\
229 Initialize an X2Go session. With the L{X2GoTerminalSessionSTDOUT} class you can start
230 new X2Go sessions, resume suspended sessions or suspend resp. terminate
231 currently running sessions on a connected X2Go server.
232
233 @param geometry: screen geometry of the X2Go session. Can be either C{<width>x<height>},
234 C{maximize} or C{fullscreen}
235 @type geometry: C{str}
236 @param depth: color depth in bits (common values: C{16}, C{24})
237 @type depth: C{int}
238 @param link: network link quality (either one of C{modem}, C{isdn}, C{adsl}, C{wan} or C{lan})
239 @type link: C{str}
240 @param pack: compression method for NX based session proxying
241 @type pack: C{str}
242 @param dpi: dots-per-inch value for the session screen (has an impact on the font size on screen)
243 @type dpi: C{str}
244 @param cache_type: a dummy parameter that is passed to the L{X2GoProxyBASE}. In NX Proxy
245 (class C{X2GoProxyNX3}) this originally is the session name. With X2Go it
246 defines the name of the NX cache directory. Best is to leave it untouched.
247 @type cache_type: C{str}
248 @param kbtype: keyboard type, e.g. C{pc105/us} (default), C{pc105/de}, ...
249 @type kbtype: C{str}
250 @param kblayout: keyboard layout, e.g. C{us} (default), C{de}, C{fr}, ...
251 @type kblayout: C{str}
252 @param kbvariant: keyboard variant, e.g. C{nodeadkeys} (for C{de} layout), C{intl} (for C{us} layout), etc.
253 @type kbvariant: C{str}
254 @param session_type: either C{desktop}, C{application} (rootless session) or C{shared}
255 @type session_type: C{str}
256 @param snd_system: sound system to be used on server (C{none}, C{pulse} (default),
257 C{arts} (obsolete) or C{esd})
258 @type snd_system: C{str}
259 @param snd_port: local sound port for network capable audio system
260 @type snd_port: C{int}
261 @param cmd: command to be run on X2Go server after session start (only used
262 when L{X2GoTerminalSessionSTDOUT.start()} is called, ignored on resume, suspend etc.
263 @type cmd: C{str}
264 @param published_applications: session is published applications provider
265 @type published_applications: C{bool}
266 @param set_session_title: modify the session title (i.e. the Window title) of desktop or shared desktop sessions
267 @type set_session_title: C{bool}
268 @param session_title: session title for this (desktop or shared desktop) session
269 @type session_title: C{str}
270 @param applications: applications available for rootless application execution
271 @type applications: C{list}
272 @param rdp_server: host name of server-side RDP server
273 @type rdp_server: C{str}
274 @param rdp_options: options for the C{rdesktop} command executed on the X2Go server (RDP proxy mode of X2Go)
275 @type rdp_options: C{str}
276 @param xdmcp_server: XDMCP server to connect to
277 @type xdmcp_server: C{str}
278 @param convert_encoding: convert file system encodings between server and client (for client-side shared folders)
279 @type convert_encoding: C{bool}
280 @param server_encoding: server-side file system / session encoding
281 @type server_encoding: C{str}
282 @param client_encoding: client-side file system encoding (if client-side is MS Windows, this parameter gets overwritten to WINDOWS-1252)
283 @type client_encoding: C{str}
284 @param rootdir: X2Go session directory, normally C{~/.x2go}
285 @type rootdir: C{str}
286 @param profile_name: the session profile name for this terminal session
287 @type profile_name: C{str}
288 @param profile_id: the session profile ID for this terminal session
289 @type profile_id: C{str}
290 @param print_action: either a print action short name (PDFVIEW, PDFSAVE, PRINT, PRINTCMD) or the
291 resp. C{X2GoPrintActionXXX} class (where XXX equals one of the given short names)
292 @type print_action: C{str} or C{class}
293 @param print_action_args: optional arguments for a given print_action (for further info refer to
294 L{X2GoPrintActionPDFVIEW}, L{X2GoPrintActionPDFSAVE}, L{X2GoPrintActionPRINT} and L{X2GoPrintActionPRINTCMD})
295 @type print_action_args: dict
296 @param info_backend: backend for handling storage of server session information
297 @type info_backend: C{X2GoServerSessionInfo*} instance
298 @param list_backend: backend for handling storage of session list information
299 @type list_backend: C{X2GoServerSessionList*} instance
300 @param proxy_backend: backend for handling the X-proxy connections
301 @type proxy_backend: C{X2GoProxy*} instance
302 @param proxy_options: a set of very C{X2GoProxy*} backend specific options; any option that is not known
303 to the C{X2GoProxy*} backend will simply be ignored
304 @type proxy_options: C{dict}
305 @param client_rootdir: client base dir (default: ~/.x2goclient)
306 @type client_rootdir: C{str}
307 @param sessions_rootdir: sessions base dir (default: ~/.x2go)
308 @type sessions_rootdir: C{str}
309 @param session_instance: the L{X2GoSession} instance that is parent to this terminal session
310 @type session_instance: C{obj}
311 @param logger: you can pass an L{X2GoLogger} object to the
312 L{X2GoTerminalSessionSTDOUT} constructor
313 @type logger: L{X2GoLogger} instance
314 @param loglevel: if no L{X2GoLogger} object has been supplied a new one will be
315 constructed with the given loglevel
316 @type loglevel: C{int}
317
318 """
319 self.proxy = None
320 self.proxy_subprocess = None
321 self.proxy_options = proxy_options
322
323 self.active_threads = []
324 self.reverse_tunnels = {}
325
326 self.print_queue = None
327 self.mimebox_queue = None
328
329 if logger is None:
330 self.logger = log.X2GoLogger(loglevel=loglevel)
331 else:
332 self.logger = copy.deepcopy(logger)
333 self.logger.tag = __NAME__
334
335 self.control_session = control_session
336 self.reverse_tunnels = self.control_session.get_transport().reverse_tunnels
337
338 self.client_rootdir = client_rootdir
339 self.sessions_rootdir = sessions_rootdir
340
341 self.params = X2GoSessionParams()
342
343 self.params.geometry = str(geometry)
344 self.params.link = str(link)
345 self.params.pack = str(pack)
346 self.params.dpi = str(dpi)
347 self.params.cache_type = str(cache_type)
348 self.params.session_type = str(session_type)
349 self.params.kbtype = str(kbtype)
350 self.params.kblayout = str(kblayout)
351 self.params.kbvariant = str(kbvariant)
352 self.params.snd_system = str(snd_system)
353 self.params.cmd = str(cmd)
354 self.params.depth = str(depth)
355
356 self.params.published_applications = published_applications
357 self.published_applications = published_applications
358
359 self.params.rdp_server = str(rdp_server)
360 self.params.rdp_options = str(rdp_options)
361 self.params.xdmcp_server = str(xdmcp_server)
362
363 self.params.convert_encoding = convert_encoding
364 self.params.client_encoding = str(client_encoding)
365 self.params.server_encoding = str(server_encoding)
366
367 self.params.rootdir = (type(rootdir) is types.StringType) and rootdir or self.sessions_rootdir
368 self.params.update()
369
370 self.profile_name = profile_name
371 self.set_session_title = set_session_title
372 self.session_title = session_title
373 self.session_window = None
374 self.proxy_backend = proxy_backend
375
376 self.snd_port = snd_port
377 self.print_action = print_action
378 self.print_action_args = print_action_args
379 self.printing_backend = printing_backend
380 self.session_instance = session_instance
381 if self.session_instance:
382 self.client_instance = self.session_instance.client_instance
383 else:
384 self.client_instance = None
385
386 self._share_local_folder_busy = False
387 self._mk_sessions_rootdir(self.params.rootdir)
388
389 self.session_info = session_info
390 if self.session_info is not None:
391 if self.session_info.name:
392 self.session_info.local_container = os.path.join(self.params.rootdir, 'S-%s' % self.session_info.name)
393 else:
394 raise x2go_exceptions.X2GoTerminalSessionException('no valid session info availble')
395 else:
396 self.session_info = info_backend()
397
398 self._share_local_folder_lock = threading.Lock()
399 self._cleaned_up = False
400
402 """\
403 Tidy up if terminal session gets destructed.
404
405 """
406 self._x2go_tidy_up()
407
409 """\
410 Tidy up this terminal session...
411 - shutdown all forwarding and reverse forwarding tunnels
412 - shutdown the print queue (if running)
413 - shutdown the MIME box queue (if running)
414 - clear the session info
415
416 """
417 self.release_proxy()
418
419 try:
420
421 if self.control_session.get_transport() is not None:
422 try:
423 for _tunnel in [ _tun[1] for _tun in self.reverse_tunnels[self.session_info.name].values() ]:
424 if _tunnel is not None:
425 _tunnel.__del__()
426 except KeyError:
427 pass
428
429 if self.print_queue is not None:
430 self.print_queue.__del__()
431
432 if self.mimebox_queue is not None:
433 self.mimebox_queue.__del__()
434
435 except AttributeError:
436 pass
437
438 self.session_info.clear()
439
441 """\
442 Create the server-side session root dir (normally ~/.x2go).
443
444 @param rootdir: server-side session root directory
445 @type rootdir: C{str}
446
447 """
448 try:
449 os.makedirs(rootdir)
450 except OSError, e:
451 if e.errno == 17:
452
453 pass
454 else:
455 raise OSError, e
456
458 """\
459 Purge client-side session dir (session cache directory).
460
461 """
462 if self.session_info.name:
463 shutil.rmtree('%s/S-%s' % (self.params.rootdir, self.session_info), ignore_errors=True)
464
466 """\
467 Purge client-side session dir (C-<display> directory)
468
469 """
470 if self.session_info.display:
471 shutil.rmtree('%s/S-%s' % (self.params.rootdir, self.session_info.display), ignore_errors=True)
472
474 """\
475 Retrieve the X2Go session's name from the session info object.
476
477 @return: the session name
478 @rtype: C{str}
479
480 """
481 return self.session_info.name
482
484 """\
485 Retrieve the X2Go session's session info object.
486
487 @return: the session info object
488 @rtype: C{X2GoServerSessionInfo*}
489
490 """
491 return self.session_info
492
494 """\
495 Retrieve the X2Go session's command as stored in the session parameter object.
496
497 @return: the session command
498 @rtype: C{str}
499
500 """
501 return self.params.cmd
502
504 """\
505 Retrieve the X2Go session's session type as stored in the session parameter object.
506
507 @return: the session type
508 @rtype: C{str}
509
510 """
511 return self.params.session_type
512
514 """\
515 Initialize Paramiko/SSH reverse forwarding tunnel for X2Go sound.
516
517 Currently supported audio protocols:
518
519 - PulseAudio
520 - Esound (not tested very much)
521
522 @raise X2GoControlSessionException: if the control session of this terminal session is not connected
523
524 """
525 _tunnel = None
526 if self.reverse_tunnels[self.session_info.name]['snd'][1] is None:
527 if self.params.snd_system == 'pulse':
528 self.logger('initializing PulseAudio sound support in X2Go session', loglevel=log.loglevel_INFO)
529
530
531
532 if os.path.exists(os.path.normpath('%s/.pulse-cookie' % _LOCAL_HOME)):
533
534 cmd_line = "echo 'default-server=127.0.0.1:%s'>%s/.pulse-client.conf;" % (self.session_info.snd_port, self.session_info.remote_container) + \
535 "echo 'cookie-file=%s/.pulse-cookie'>>%s/.pulse-client.conf" % (self.session_info.remote_container, self.session_info.remote_container)
536 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
537
538 self.control_session._x2go_sftp_put(local_path='%s/.pulse-cookie' % _LOCAL_HOME, remote_path='%s/.pulse-cookie' % self.session_info.remote_container)
539
540
541 _tunnel = rforward.X2GoRevFwTunnel(server_port=self.session_info.snd_port,
542 remote_host='127.0.0.1',
543 remote_port=self.snd_port,
544 ssh_transport=self.control_session.get_transport(),
545 session_instance=self.session_instance,
546 logger=self.logger
547 )
548 else:
549 if self.client_instance:
550 self.client_instance.HOOK_on_sound_tunnel_failed(profile_name=self.profile_name, session_name=self.session_info.name)
551 elif self.params.snd_system == 'arts':
552
553
554
555 self.logger('the ArtsD sound server (as in KDE3) is obsolete and will not be supported by Python X2Go...', loglevel=log.loglevel_WARNING)
556
557 elif self.params.snd_system == 'esd':
558
559
560
561
562 self.logger('initializing ESD sound support in X2Go session', loglevel=log.loglevel_INFO)
563 self.control_session._x2go_sftp_put(local_path='%s/.esd_auth' % _LOCAL_HOME, remote_path='%s/.esd_auth' % self.control_session._x2go_remote_home)
564
565
566 _tunnel = rforward.X2GoRevFwTunnel(server_port=self.session_info.snd_port,
567 remote_host='127.0.0.1',
568 remote_port=self.snd_port,
569 ssh_transport=self.control_session.get_transport(),
570 session_instance=self.session_instance,
571 logger=self.logger
572 )
573
574
575 if _tunnel is not None:
576 self.reverse_tunnels[self.session_info.name]['snd'] = (self.session_info.snd_port, _tunnel)
577 _tunnel.start()
578 self.active_threads.append(_tunnel)
579
580 else:
581
582 self.reverse_tunnels[self.session_info.name]['snd'][1].resume()
583
585 """\
586 Initialize Paramiko/SSH reverse forwarding tunnel for X2Go folder sharing.
587
588 """
589 if not self.control_session.is_sshfs_available():
590 raise x2go_exceptions.X2GoUserException('Remote user %s is not allowed to share SSHFS resources with the server.' % self.session_info.username)
591
592
593 ssh_transport = self.control_session.get_transport()
594 if self.reverse_tunnels[self.session_info.name]['sshfs'][1] is None:
595
596 _tunnel = sftpserver.X2GoRevFwTunnelToSFTP(server_port=self.session_info.sshfs_port,
597 ssh_transport=ssh_transport,
598 auth_key=self.control_session._x2go_session_auth_rsakey,
599 session_instance=self.session_instance,
600 logger=self.logger
601 )
602
603 if _tunnel is not None:
604 self.reverse_tunnels[self.session_info.name]['sshfs'] = (self.session_info.sshfs_port, _tunnel)
605 _tunnel.start()
606 self.active_threads.append(_tunnel)
607 while not _tunnel.ready:
608 gevent.sleep(.1)
609
610 else:
611
612 self.reverse_tunnels[self.session_info.name]['sshfs'][1].resume()
613
615 """\
616 Pause reverse SSH tunnel of name <name>.
617
618 @param name: tunnel name (either of C{sshfs}, C{snd})
619 @type name: C{str}
620
621 """
622 _tunnel = self.reverse_tunnels[self.session_info.name][name][1]
623 if _tunnel is not None:
624 _tunnel.pause()
625
627 """\
628 Shutdown (pause) Paramiko/SSH reverse forwarding tunnel for X2Go sound.
629
630 """
631 self._x2go_pause_rev_fw_tunnel('snd')
632
634 """\
635 Shutdown (pause) Paramiko/SSH reverse forwarding tunnel for X2Go folder sharing.
636
637 """
638 self._x2go_pause_rev_fw_tunnel('sshfs')
639
641 """\
642 Initialize X2Go print spooling.
643
644 @raise X2GoUserException: if the X2Go printing feature is not available to this user
645
646 """
647 if not self.control_session.is_sshfs_available():
648 raise x2go_exceptions.X2GoUserException('Remote user %s is not allowed to use client-side printing.' % self.session_info.username)
649
650 spool_dir = os.path.join(self.session_info.local_container, 'spool')
651 if not os.path.exists(spool_dir):
652 os.makedirs(spool_dir)
653 self.share_local_folder(local_path=spool_dir, folder_type='spool')
654 self.print_queue = printqueue.X2GoPrintQueue(profile_name=self.profile_name,
655 session_name=self.session_info.name,
656 spool_dir=spool_dir,
657 print_action=self.print_action,
658 print_action_args=self.print_action_args,
659 client_instance=self.client_instance,
660 printing_backend=self.printing_backend,
661 logger=self.logger,
662 )
663 self.print_queue.start()
664 self.active_threads.append(self.print_queue)
665
667 """\
668 Set a print action for the next incoming print jobs.
669
670 This method is a wrapper for L{X2GoPrintQueue}C{.set_print_action()}.
671
672 @param print_action: print action name or object (i.e. an instance of C{X2GoPrintAction*} classes)
673 @type print_action: C{str} or C{X2GoPrintAction*}
674 @param kwargs: print action specific parameters
675 @type kwargs: dict
676
677 """
678 self.print_queue.set_print_action(print_action, logger=self.logger, **kwargs)
679
681 """\
682 Shutdown (pause) the X2Go Print Queue thread.
683
684 """
685 if self.print_queue is not None:
686 self.print_queue.pause()
687
689 """\
690 Return the server-side printing spooldir path.
691
692 @return: the directory for remote print job spooling
693 @rtype: C{str}
694
695 """
696 return '%s/%s' % (self.session_info.remote_container, 'spool')
697
698 - def start_mimebox(self, mimebox_extensions=[], mimebox_action=None):
699 """\
700 Initialize the X2Go MIME box. Open/process incoming files from the server-side locally.
701
702 @param mimebox_extensions: file name extensions that are allowed for local opening/processing
703 @type mimebox_extensions: C{list}
704 @param mimebox_action: MIME box action given as name or object (i.e. an instance of C{X2GoMIMEboxAction*} classes).
705 @type mimebox_action: C{str} or C{obj}
706
707 @raise X2GoUserException: if the X2Go MIME box feature is not available to this user
708
709 """
710 if not self.control_session.is_sshfs_available():
711 raise x2go_exceptions.X2GoUserException('Remote user %s is not allowed to use the MIME box.' % self.session_info.username)
712
713 mimebox_dir = os.path.join(self.session_info.local_container, 'mimebox')
714 if not os.path.exists(mimebox_dir):
715 os.makedirs(mimebox_dir)
716 self.share_local_folder(local_path=mimebox_dir, folder_type='mimebox')
717 self.mimebox_queue = mimebox.X2GoMIMEboxQueue(profile_name=self.profile_name,
718 session_name=self.session_info.name,
719 mimebox_dir=mimebox_dir,
720 mimebox_extensions=mimebox_extensions,
721 mimebox_action=mimebox_action,
722 client_instance=self.client_instance,
723 logger=self.logger,
724 )
725 self.mimebox_queue.start()
726 self.active_threads.append(self.mimebox_queue)
727
729 """\
730 Set a MIME box action for the next incoming MIME jobs.
731
732 This method is a wrapper for L{X2GoMIMEboxQueue}C{set_mimebox_action()}.
733
734 @param mimebox_action: MIME box action name or object (i.e. an instance of C{X2GoMIMEboxAction*} classes)
735 @type mimebox_action: C{str} or C{X2GoMIMEboxAction*}
736 @param kwargs: MIME box action specific parameters
737 @type kwargs: dict
738
739 """
740 self.mimebox_queue.set_mimebox_action(mimebox_action, logger=self.logger, **kwargs)
741
743 """\
744 Shutdown (pause) the X2Go MIME box Queue thread.
745
746 """
747 if self.mimebox_queue is not None:
748 self.mimebox_queue.pause()
749
751 """\
752 Return the server-side MIME box spooldir path.
753
754 @return: the directory where remote MIME box jobs are placed
755 @rtype: C{str}
756
757 """
758 return '%s/%s' % (self.session_info.remote_container, 'mimebox')
759
761 """\
762 Test if this terminal's session info object is write-protected.
763
764 @return: C{True}, if session info object is read-only, C{False} for read-write.
765 @rtype: C{bool}
766
767 """
768 self.session_info.is_protected()
769
771 """\
772 Protect this terminal session's info object against updates.
773
774 """
775 self.session_info.protect()
776
778 """\
779 Allow session info updates from within the list_sessions method of the control session.
780
781 """
782 self.session_info.unprotect()
783
785 """\
786 Share a local folder with the X2Go session.
787
788 @param local_path: the full path to an existing folder on the local
789 file system
790 @type local_path: C{str}
791 @param folder_type: one of 'disk' (a folder on your local hard drive), 'rm' (removeable device),
792 'cdrom' (CD/DVD Rom) or 'spool' (for X2Go print spooling)
793 @type folder_type: C{str}
794
795 @return: returns C{True} if the local folder has been successfully mounted within the X2Go server session
796 @rtype: C{bool}
797
798 @raise X2GoUserException: if local folder sharing is not available to this user
799 @raise Exception: any other exception occuring on the way is passed through by this method
800
801 """
802 if not self.control_session.is_sshfs_available():
803 raise x2go_exceptions.X2GoUserException('Remote user %s is not allowed to share local folders with the server.' % self.session_info.username)
804
805 if local_path is None:
806 self.logger('no folder name given...', log.loglevel_WARN)
807 return False
808
809 if type(local_path) not in (types.StringType, types.UnicodeType):
810 self.logger('folder name needs to be of type StringType...', log.loglevel_WARN)
811 return False
812
813 if not os.path.exists(local_path):
814 self.logger('local folder does not exist: %s' % local_path, log.loglevel_WARN)
815 return False
816
817 local_path = os.path.normpath(local_path)
818 self.logger('sharing local folder: %s' % local_path, log.loglevel_INFO)
819
820 _auth_rsakey = self.control_session._x2go_session_auth_rsakey
821 _host_rsakey = defaults.RSAHostKey
822
823 _tmp_io_object = cStringIO.StringIO()
824 _auth_rsakey.write_private_key(_tmp_io_object)
825 _tmp_io_object.write('----BEGIN RSA IDENTITY----')
826 _tmp_io_object.write('%s %s' % (_host_rsakey.get_name(),_host_rsakey.get_base64(),))
827
828
829 _x2go_key_fname = '%s/%s/%s' % (os.path.dirname(self.session_info.remote_container), 'ssh', 'key.z%s' % self.session_info.agent_pid)
830 _x2go_key_bundle = _tmp_io_object.getvalue()
831
832
833 self._share_local_folder_lock.acquire()
834
835 try:
836 self.control_session._x2go_sftp_write(_x2go_key_fname, _x2go_key_bundle)
837
838 _convert_encoding = self.params.convert_encoding
839 _client_encoding = self.params.client_encoding
840 _server_encoding = self.params.server_encoding
841
842 if _X2GOCLIENT_OS == 'Windows':
843 local_path = local_path.replace('\\', '/')
844 local_path = local_path.replace(':', '')
845 local_path = '/windrive/%s' % local_path
846 _convert_encoding = True
847 _client_encoding = 'WINDOWS-1252'
848
849 if _convert_encoding:
850 export_iconv_settings = 'export X2GO_ICONV=modules=iconv,from_code=%s,to_code=%s && ' % (_client_encoding, _server_encoding)
851 else:
852 export_iconv_settings = ''
853
854 if folder_type == 'disk':
855
856 cmd_line = [ '%sexport HOSTNAME &&' % export_iconv_settings,
857 'x2gomountdirs',
858 'dir',
859 str(self.session_info.name),
860 '\'%s\'' % _CURRENT_LOCAL_USER,
861 _x2go_key_fname,
862 '%s__REVERSESSH_PORT__%s; ' % (local_path, self.session_info.sshfs_port),
863 'rm -f %s %s.ident' % (_x2go_key_fname, _x2go_key_fname),
864 ]
865
866 elif folder_type == 'spool':
867
868 cmd_line = [ '%sexport HOSTNAME &&' % export_iconv_settings,
869 'x2gomountdirs',
870 'dir',
871 str(self.session_info.name),
872 '\'%s\'' % _CURRENT_LOCAL_USER,
873 _x2go_key_fname,
874 '%s__PRINT_SPOOL___REVERSESSH_PORT__%s; ' % (local_path, self.session_info.sshfs_port),
875 'rm -f %s %s.ident' % (_x2go_key_fname, _x2go_key_fname),
876 ]
877
878 elif folder_type == 'mimebox':
879
880 cmd_line = [ '%sexport HOSTNAME &&' % export_iconv_settings,
881 'x2gomountdirs',
882 'dir',
883 str(self.session_info.name),
884 '\'%s\'' % _CURRENT_LOCAL_USER,
885 _x2go_key_fname,
886 '%s__MIMEBOX_SPOOL___REVERSESSH_PORT__%s; ' % (local_path, self.session_info.sshfs_port),
887 'rm -f %s %s.ident' % (_x2go_key_fname, _x2go_key_fname),
888 ]
889
890 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
891 _stdout = stdout.read().split('\n')
892 self.logger('x2gomountdirs output is: %s' % _stdout, log.loglevel_NOTICE)
893
894 except:
895 self._share_local_folder_lock.release()
896 raise
897 self._share_local_folder_lock.release()
898
899 if len(_stdout) >= 6 and _stdout[5].endswith('ok'):
900 return True
901 return False
902
904 """\
905 Unshare all local folders mount in the X2Go session.
906
907 @return: returns C{True} if all local folders could be successfully unmounted from the X2Go server session
908 @rtype: C{bool}
909
910 """
911 self.logger('unsharing all local folders from session %s' % self.session_info, log.loglevel_INFO)
912
913 cmd_line = [ 'export HOSTNAME &&',
914 'x2goumount-session',
915 self.session_info.name,
916 ]
917
918 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
919 if not stderr.read():
920 self.logger('x2goumount-session (all mounts) for session %s has been successful' % self.session_info, log.loglevel_NOTICE)
921 return True
922 else:
923 self.logger('x2goumount-session (all mounts) for session %s failed' % self.session_info, log.loglevel_ERROR)
924 return False
925
927 """\
928 Unshare local folder given as <local_path> from X2Go session.
929
930 @return: returns C{True} if the local folder <local_path> could be successfully unmounted from the X2Go server session
931 @rtype: C{bool}
932
933 """
934 self.logger('unsharing local folder from session %s' % self.session_info, log.loglevel_INFO)
935
936 cmd_line = [ 'export HOSTNAME &&',
937 'x2goumount-session',
938 self.session_info.name,
939 "'%s'" % local_path,
940 ]
941
942 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
943 if not stderr.read():
944 self.logger('x2goumount-session (%s) for session %s has been successful' % (local_path, self.session_info, ), log.loglevel_NOTICE)
945 return True
946 else:
947 self.logger('x2goumount-session (%s) for session %s failed' % (local_path, self.session_info, ), log.loglevel_ERROR)
948 return False
949
951 """\
952 Retrieve the session's color depth.
953
954 @return: the session's color depth
955 @rtype: C{int}
956
957 """
958 return self.params.depth
959
961 """\
962 Automatically generate an appropriate human-readable session window title.
963
964 The session window title will be provider in the C{session_title} property of
965 this method.
966
967 @param dont_set: generate the session window title, but do not actually set it
968 @type dont_set: C{bool}
969
970 """
971 _generic_title = 'X2GO-%s' % self.session_info.name
972
973
974 self.session_title = self.session_title.strip()
975
976 if self.params.session_type == 'D':
977 if self.set_session_title:
978
979 if not self.session_title:
980 self.session_title = '%s for %s@%s' % (self.params.cmd, self.control_session.remote_username(), self.control_session.get_hostname())
981
982 else:
983
984 self.session_title = _generic_title
985
986 elif self.params.session_type == 'S':
987 if self.set_session_title:
988
989 shared_user = _generic_title.split('XSHAD')[1]
990 shared_display = _generic_title.split('XSHAD')[2].replace('PP', ':').split("_")[0]
991
992 self.session_title = 'Desktop %s@%s shared with %s@%s' % (shared_user, shared_display, self.control_session.remote_username(), self.control_session.get_hostname())
993
994 else:
995
996 self.session_title = _generic_title
997
998 else:
999
1000 self.session_title = _generic_title
1001
1002 if self.session_title != _generic_title and not dont_set:
1003 self.set_session_window_title(title=self.session_title)
1004
1006 """\
1007 Try for <timeout> seconds to find the X2Go session window of this
1008 terminal session.
1009
1010 A background thread will get spawned for this operation.
1011
1012 @param timeout: try for <timeout> seconds to find the session window
1013 @type timeout: C{int}
1014
1015 """
1016 gevent.spawn(self._find_session_window, timeout=timeout)
1017
1019 """\
1020 Try for <timeout> seconds to find the X2Go session window of this
1021 terminal session.
1022
1023 @param timeout: try for <timeout> seconds to find the session window
1024 @type timeout: C{int}
1025
1026 """
1027 self.session_window = None
1028
1029
1030
1031 timeout += 1
1032 while timeout:
1033
1034 timeout -= 1
1035
1036 window = utils.find_session_window(self.session_info.name)
1037
1038 if window is not None:
1039 self.session_window = window
1040 break
1041
1042 gevent.sleep(1)
1043
1045 """\
1046 Modify the session window title.
1047
1048 A background thread will get spawned for this operation.
1049
1050 @param title: new title for the terminal session's session window
1051 @type title: C{str}
1052 @param timeout: try for <timeout> seconds to find the session window
1053 @type timeout: C{int}
1054
1055 """
1056 gevent.spawn(self._set_session_window_title, title=title.strip(), timeout=timeout)
1057
1059 """\
1060 Modify the session window title.
1061
1062 @param title: new title for the terminal session's session window
1063 @type title: C{str}
1064 @param timeout: try for <timeout> seconds to find the session window
1065 @type timeout: C{int}
1066
1067 """
1068 self.session_title = title
1069
1070 if not self.session_title:
1071 self.auto_session_title(dont_set=True)
1072
1073 timeout += 1
1074 while timeout:
1075
1076 timeout -= 1
1077
1078 if self.session_window is not None:
1079 utils.set_session_window_title(self.session_window, self.session_title)
1080 break
1081
1082 gevent.sleep(1)
1083
1085 """\
1086 Try for <timeout> seconds to raise the X2Go session window of this
1087 terminal session to the top and bring it to focus.
1088
1089 A background thread will get spawned for this operation.
1090
1091 @param timeout: try for <timeout> seconds to raise the session window
1092 @type timeout: C{int}
1093
1094 """
1095 gevent.spawn(self._raise_session_window, timeout=timeout)
1096
1098 """
1099 Try for <timeout> seconds to raise the X2Go session window of this
1100 terminal session to the top and bring it to focus.
1101
1102 @param timeout: try for <timeout> seconds to raise the session window
1103 @type timeout: C{int}
1104
1105 """
1106 timeout += 1
1107 while timeout:
1108
1109 timeout -= 1
1110
1111 if self.session_window is not None:
1112
1113 utils.raise_session_window(self.session_window)
1114 break
1115
1116 gevent.sleep(1)
1117
1119 """\
1120 ,,Guess'' if the command C{<cmd>} exists on the X2Go server and is executable.
1121 The expected result is not 100% safe, however, it comes with a high probability to
1122 be correct.
1123
1124 @param cmd: session command
1125 @type cmd: C{str}
1126
1127 @return: C{True} if this method reckons that the command is executable on the remote X2Go server
1128 @rtype: C{bool}
1129
1130 """
1131 test_cmd = None;
1132
1133 cmd = cmd.strip('"').strip('"')
1134 if cmd.find('RDP') != -1:
1135 cmd = 'rdesktop'
1136
1137 if cmd in _X2GO_GENERIC_APPLICATIONS:
1138 return True
1139 if cmd in _X2GO_DESKTOPSESSIONS.keys():
1140 return True
1141 elif 'XSHAD' in cmd:
1142 return True
1143 elif 'PUBLISHED' in cmd and 'X2GO_PUBLISHED_APPLICATIONS' in self.control_session.get_server_features():
1144 return True
1145 elif cmd and cmd.startswith('/'):
1146
1147 test_cmd = 'test -x %s && which %s && echo OK' % (cmd, os.path.basename(cmd.split()[0]))
1148 elif cmd and '/' not in cmd.split()[0]:
1149
1150 test_cmd = 'which %s && echo OK' % os.path.basename(cmd.split()[0])
1151
1152 if test_cmd:
1153 (stdin, stdout, stderr) = self.control_session._x2go_exec_command([test_cmd])
1154 _stdout = stdout.read()
1155 return _stdout.find('OK') != -1
1156 else:
1157 return False
1158
1160 """\
1161 Run a command in this session.
1162
1163 After L{X2GoTerminalSessionSTDOUT.start()} has been called
1164 one or more commands can be executed with L{X2GoTerminalSessionSTDOUT.run_command()}
1165 within the current X2Go session.
1166
1167 @param cmd: Command to be run
1168 @type cmd: C{str}
1169 @param env: add server-side environment variables
1170 @type env: C{dict}
1171
1172 @return: stdout.read() and stderr.read() as returned by the run command
1173 on the X2Go server
1174 @rtype: C{tuple} of C{str}
1175
1176 """
1177 if not self.has_command(_rewrite_cmd(str(self.params.cmd), params=self.params)):
1178 if self.client_instance:
1179 self.client_instance.HOOK_no_such_command(profile_name=self.profile_name, session_name=self.session_info.name, cmd=self.params.cmd)
1180 return False
1181
1182 if cmd in ("", None):
1183 if self.params.cmd is None:
1184 cmd = 'TERMINAL'
1185 else:
1186 cmd = self.params.cmd
1187
1188 if cmd == 'XDMCP':
1189
1190 return None
1191
1192 if 'XSHAD' in cmd:
1193
1194 return None
1195
1196 self.params.update(cmd=cmd)
1197
1198
1199 if '/' in cmd:
1200 cmd = os.path.basename(cmd)
1201
1202 cmd_line = [ "setsid x2goruncommand",
1203 str(self.session_info.display),
1204 str(self.session_info.agent_pid),
1205 str(self.session_info.name),
1206 str(self.session_info.snd_port),
1207 _rewrite_blanks(_rewrite_cmd(cmd, params=self.params)),
1208 str(self.params.snd_system),
1209 str(self.params.session_type),
1210 "1>/dev/null 2>/dev/null & exit",
1211 ]
1212
1213 if self.params.snd_system == 'pulse':
1214 cmd_line = [ 'PULSE_CLIENTCONFIG=%s/.pulse-client.conf' % self.session_info.remote_container ] + cmd_line
1215
1216 if env:
1217 for env_var in env.keys():
1218 cmd_line = [ '%s=%s' % (env_var, env[env_var]) ] + cmd_line
1219
1220 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
1221
1222 if self.params.kbtype not in ('null/null', 'auto') and (self.params.kblayout not in ('null', '') or self.params.kbvariant not in ('null', '')):
1223 self.set_keyboard(layout=self.params.kblayout, variant=self.params.kbvariant)
1224
1225 return stdout.read(), stderr.read()
1226
1228 """\
1229 Is this (terminal) session a desktop session?
1230
1231 @return: Returns C{True} is this session is a desktop session.
1232 @rtype: C{bool}
1233
1234 """
1235 if self.session_info:
1236 return self.session_info.is_desktop_session()
1237 return False
1238
1240 """\
1241 Is this (terminal) session a published applications provider?
1242
1243 @return: Returns C{True} is this session is a provider session for published applications.
1244 @rtype: C{bool}
1245
1246 """
1247 if self.session_info and self.is_running():
1248 return self.session_info.is_published_applications_provider()
1249 return False
1250
1252 """\
1253 Set the keyboard layout and variant for this (running) session.
1254
1255 @param layout: keyboard layout to be set
1256 @type layout: C{str}
1257 @param variant: keyboard variant to be set
1258 @type variant: C{str}
1259
1260 @return: returns C{True} if the {setxkbmap} command could be executed successfully.
1261 @rtype: C{bool}
1262
1263 """
1264 if not self.is_running():
1265 return False
1266
1267 cmd_line = [ 'export DISPLAY=:%s && ' % str(self.session_info.display),
1268 'setxkbmap '
1269 ]
1270
1271 if layout != 'null':
1272 self.logger('setting keyboad layout ,,%s\'\' for session %s' % (layout, self.session_info), log.loglevel_INFO)
1273 cmd_line.append('-layout %s' % layout)
1274 if variant != 'null':
1275 self.logger('setting keyboad variant ,,%s\'\' for session %s' % (variant, self.session_info), log.loglevel_INFO)
1276 cmd_line.append('-variant %s' % variant)
1277
1278 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
1279 _stderr = stderr.read()
1280 if not _stderr:
1281 self.logger('setting keyboard layout ,,%s\'\' and variant ,,%s\'\' for session %s has been successful' % (layout, variant, self.session_info), log.loglevel_NOTICE)
1282 return True
1283 else:
1284 self.logger('setting keyboard layout ,,%s\'\' and variant ,,%s\'\' for session %s failed: %s' % (layout, variant, self.session_info, _stderr.replace('\n', ' ')), log.loglevel_ERROR)
1285 return False
1286
1288 """\
1289 Executed a published application.
1290
1291 @param exec_name: application to be executed
1292 @type exec_name: C{str}
1293 @param timeout: execution timeout
1294 @type timeout: C{int}
1295 @param env: session environment dictionary
1296 @type env: C{dict}
1297
1298 """
1299 cmd_line = [
1300 "export DISPLAY=:%s && " % str(self.session_info.display),
1301 "export X2GO_SESSION=%s && " % str(self.get_session_name()),
1302 ]
1303
1304 if self.params.snd_system == 'pulse':
1305 cmd_line.append("export PULSE_CLIENTCONFIG=%s/.pulse-client.conf && " % self.session_info.remote_container)
1306
1307 if env:
1308 for env_var in env.keys():
1309 cmd_line = [ 'export %s=%s && ' % (env_var, env[env_var]) ] + cmd_line
1310
1311 cmd_line.extend(
1312 [
1313 "setsid %s" % exec_name,
1314 "1>/dev/null 2>/dev/null & exit",
1315 ]
1316 )
1317
1318 self.logger('executing published application %s for %s with command line: %s' % (exec_name, self.profile_name, cmd_line), loglevel=log.loglevel_DEBUG)
1319 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line, timeout=timeout)
1320
1322 """\
1323 X2Go session OK?
1324
1325 @return: Returns C{True} if this X2Go (terminal) session is up and running,
1326 C{False} otherwise.
1327 @rtype: C{bool}
1328
1329 """
1330 _ok = bool(self.session_info.name and self.proxy.ok())
1331 return _ok
1332
1334 """\
1335 X2Go session running?
1336
1337 @return: Returns C{True} if this X2Go (terminal) session is in running state,
1338 C{False} otherwise.
1339 @rtype: C{bool}
1340
1341 """
1342 return self.session_info.is_running()
1343
1345 """\
1346 X2Go session suspended?
1347
1348 @return: Returns C{True} if this X2Go (terminal) session is in suspended state,
1349 C{False} otherwise.
1350 @rtype: C{bool}
1351
1352 """
1353 return self.session_info.is_suspended()
1354
1356 """\
1357 X2Go session connected?
1358
1359 @return: Returns C{True} if this X2Go session's Paramiko/SSH transport is
1360 connected/authenticated, C{False} else.
1361 @rtype: C{bool}
1362
1363 """
1364 return self.control_session.is_connected()
1365
1367 """\
1368 Start a new X2Go session.
1369
1370 @return: C{True} if session startup has been successful and the X2Go proxy is up-and-running
1371 @rtype: C{bool}
1372
1373 @raise X2GoTerminalSessionException: if the session startup failed
1374
1375 """
1376 self.params.rewrite_session_type()
1377
1378 if not self.has_command(_rewrite_cmd(self.params.cmd, params=self.params)):
1379 if self.client_instance:
1380 self.client_instance.HOOK_no_such_command(profile_name=self.profile_name, session_name=self.session_info.name, cmd=self.params.cmd)
1381 return False
1382
1383 setkbd = "0"
1384 if self.params.kbtype != "null/null":
1385 setkbd = "1"
1386
1387 if '/' in self.params.cmd:
1388 self.params.cmd = os.path.basename(self.params.cmd)
1389
1390 self.params.rewrite_session_type()
1391
1392 if self.params.geometry == 'maximize':
1393 _geometry = utils.get_workarea_geometry()
1394 if _geometry is None or len(_geometry) != 2:
1395 _geometry = utils.get_desktop_geometry()
1396 if _geometry and len(_geometry) == 2:
1397 self.params.geometry = "%sx%s" % _geometry
1398 else:
1399 self.logger('failed to detect best maxmimized geometry of your client-side desktop', loglevel=log.loglevel_WARN)
1400 self.params.geometry = "1024x768"
1401
1402 cmd_line = [ "x2gostartagent",
1403 str(self.params.geometry),
1404 str(self.params.link),
1405 str(self.params.pack),
1406 str(self.params.cache_type+'-depth_'+self.params.depth),
1407 str(self.params.kblayout),
1408 str(self.params.kbtype),
1409 str(setkbd),
1410 str(self.params.session_type),
1411 str(self.params.cmd),
1412 ]
1413
1414 if self.params.cmd == 'XDMCP' and self.params.xdmcp_server:
1415 cmd_line = ['X2GOXDMCP=%s' % self.params.xdmcp_server] + cmd_line
1416
1417 if self.params.dpi:
1418 cmd_line = ['X2GODPI=%s' % self.params.dpi] + cmd_line
1419
1420 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
1421
1422 _stdout = stdout.read()
1423 _stderr = stderr.read()
1424
1425
1426
1427 if "ACCESS DENIED" in _stderr and "XSHAD" in _stderr:
1428 raise x2go_exceptions.X2GoDesktopSharingException('X2Go desktop sharing has been denied by the remote user')
1429
1430 try:
1431 self.session_info.initialize(_stdout,
1432 username=self.control_session.remote_username(),
1433 hostname=self.control_session.remote_peername(),
1434 )
1435 except ValueError:
1436 raise x2go_exceptions.X2GoTerminalSessionException("failed to start X2Go session")
1437 except IndexError:
1438 raise x2go_exceptions.X2GoTerminalSessionException("failed to start X2Go session")
1439
1440
1441 self.session_info.local_container = os.path.join(self.params.rootdir, 'S-%s' % self.session_info.name)
1442
1443 self.session_info.remote_container = '%s/.x2go/C-%s' % (self.control_session._x2go_remote_home,
1444 self.session_info.name,
1445 )
1446
1447
1448 self.proxy = self.proxy_backend(session_info=self.session_info,
1449 ssh_transport=self.control_session.get_transport(),
1450 sessions_rootdir=self.sessions_rootdir,
1451 session_instance=self.session_instance,
1452 proxy_options=self.proxy_options,
1453 logger=self.logger)
1454 self.proxy_subprocess, proxy_ok = self.proxy.start_proxy()
1455
1456 if proxy_ok:
1457 self.active_threads.append(self.proxy)
1458
1459 if self.params.session_type in ('D', 'S'):
1460 self.find_session_window()
1461 self.auto_session_window_title()
1462 self.raise_session_window()
1463
1464 if self.params.published_applications:
1465 self.control_session.get_published_applications()
1466
1467 else:
1468 raise x2go_exceptions.X2GoTerminalSessionException("failed to start X2Go session")
1469
1470 return proxy_ok
1471
1473 """\
1474 Resume a running/suspended X2Go session.
1475
1476 @return: C{True} if the session could successfully be resumed
1477 @rtype: C{bool}
1478
1479 @raise X2GoTerminalSessionException: if the terminal session failed to update server-side reported port changes
1480
1481 """
1482 setkbd = "0"
1483 if self.params.kbtype != "null/null":
1484 setkbd = "1"
1485
1486 if self.params.geometry == 'maximize':
1487 _geometry = utils.get_workarea_geometry()
1488 if _geometry is None or len(_geometry) != 2:
1489 _geometry = utils.get_desktop_geometry()
1490 if _geometry and len(_geometry) == 2:
1491 self.params.geometry = "%sx%s" % _geometry
1492 else:
1493 self.logger('failed to detect best maxmimized geometry of your client-side desktop, using 1024x768 instead', loglevel=log.loglevel_WARN)
1494 self.params.geometry = "1024x768"
1495
1496 cmd_line = [ "x2goresume-session", self.session_info.name,
1497 self.params.geometry,
1498 self.params.link,
1499 self.params.pack,
1500 self.params.kblayout,
1501 self.params.kbtype,
1502 setkbd,
1503 ]
1504
1505 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
1506
1507
1508 for stdout_line in stdout.read():
1509 try:
1510 _new_value = stdout_line.split("=")[1].strip()
1511 if 'gr_port=' in stdout_line and _new_value != str(self.session_info.graphics_port):
1512 try:
1513 self.session_info.graphics_port = int(_new_value)
1514 self.logger('re-allocating graphics port for session %s, old server-side port is in use; new graphics port is %s' % (self.session_info, self.session_info.graphics_port), loglevel=log.loglevel_NOTICE)
1515 except TypeError:
1516
1517 raise x2go_exceptions.X2GoTerminalSessionException('Failed to retrieve new graphics port from server. X2Go Session cannot be resumed.')
1518 elif 'sound_port=' in stdout_line and _new_value != str(self.session_info.snd_port):
1519 try:
1520 self.session_info.snd_port = int(_new_value)
1521 self.logger('re-allocating sound port for session %s, old server-side port is in use; new sound port is %s' % (self.session_info, self.session_info.snd_port), loglevel=log.loglevel_NOTICE)
1522 except TypeError:
1523 self.logger('Failed to retrieve new sound port from server for session %s, session will be without sound.' % self.session_info, loglevel=log.loglevel_WARN)
1524 elif 'fs_port=' in stdout_line and _new_value != str(self.session_info.sshfs_port):
1525 try:
1526 self.session_info.sshfs_port = int(_new_value)
1527 self.logger('re-allocating sshfs port for session %s, old server-side port is in use; new sshfs port is %s' % (self.session_info, self.session_info.sshfs_port), loglevel=log.loglevel_NOTICE)
1528 except TypeError:
1529 self.logger('Failed to retrieve new sshfs port from server for session %s, session will be without client-side folder sharing. Neither will there be X2Go printing nor X2Go MIME box support.' % self.session_info, loglevel=log.loglevel_WARN)
1530 except IndexError:
1531 continue
1532
1533
1534 self.session_info.local_container = os.path.join(self.params.rootdir, 'S-%s' % self.session_info.name)
1535
1536 self.session_info.remote_container = '%s/.x2go/C-%s' % (self.control_session._x2go_remote_home,
1537 self.session_info.name,
1538 )
1539 self.proxy = self.proxy_backend(session_info=self.session_info,
1540 ssh_transport=self.control_session.get_transport(),
1541 sessions_rootdir=self.sessions_rootdir,
1542 session_instance=self.session_instance,
1543 logger=self.logger
1544 )
1545 self.proxy_subprocess, proxy_ok = self.proxy.start_proxy()
1546
1547 if proxy_ok:
1548 self.params.depth = self.session_info.name.split('_')[2][2:]
1549
1550
1551 self.session_info.username = self.control_session.remote_username()
1552
1553 if self.params.kbtype not in ('null/null', 'auto') and (self.params.kblayout not in ('null', '') or self.params.kbvariant not in ('null', '')):
1554 self.set_keyboard(layout=self.params.kblayout, variant=self.params.kbvariant)
1555
1556 if self.params.session_type in ('D', 'S'):
1557 self.find_session_window()
1558 self.auto_session_window_title()
1559 self.raise_session_window()
1560
1561 if self.is_published_applications_provider():
1562 self.control_session.get_published_applications()
1563 self.published_applications = True
1564 else:
1565 raise x2go_exceptions.X2GoTerminalSessionException("failed to start X2Go session")
1566
1567 return proxy_ok
1568
1570 """\
1571 Suspend this X2Go (terminal) session.
1572
1573 @return: C{True} if the session terminal could be successfully suspended
1574 @rtype: C{bool}
1575
1576 """
1577 self.control_session.suspend(session_name=self.session_info.name)
1578 self.release_proxy()
1579
1580
1581 _ret = True
1582
1583 return _ret
1584
1586 """\
1587 Terminate this X2Go (terminal) session.
1588
1589 @return: C{True} if the session could be successfully terminated
1590 @rtype: C{bool}
1591
1592 """
1593 self.control_session.terminate(session_name=self.session_info.name, destroy_terminals=False)
1594 self.release_proxy()
1595 self.post_terminate_cleanup()
1596 self.__del__()
1597
1598
1599 _ret = True
1600
1601 return _ret
1602
1604 """\
1605 Let the X2Go proxy command cleanly die away... (by calling its destructor).
1606
1607 """
1608 if self.proxy is not None:
1609 self.proxy.__del__()
1610
1612 """\
1613 Do some cleanup after this session has terminated.
1614
1615 """
1616
1617
1618
1619 if not self._cleaned_up and self.session_info.name:
1620
1621
1622 self.logger('cleaning up session %s after termination' % self.session_info, loglevel=log.loglevel_NOTICE)
1623
1624
1625 if self.logger.get_loglevel() & log.loglevel_DEBUG != log.loglevel_DEBUG:
1626
1627 self._rm_session_dirtree()
1628 self._rm_desktop_dirtree()
1629
1630 self._cleaned_up = True
1631
1633 """\
1634 Test if this terminal session is a desktop session.
1635
1636 @return: C{True} if this session is of session type desktop ('D').
1637 @rtype: C{bool}
1638
1639 """
1640 self.params.rewrite_session_type()
1641 return self.params.session_type == 'D'
1642
1644 """\
1645 Test if this terminal session is a rootless session.
1646
1647 @return: C{True} if this session is of session type rootless ('R').
1648 @rtype: C{bool}
1649
1650 """
1651 self.params.rewrite_session_type()
1652 return self.params.session_type == 'R'
1653
1655 """\
1656 Test if this terminal session is a desktop sharing (aka shadow) session.
1657
1658 @return: C{True} if this session is of session type shadow ('S').
1659 @rtype: C{bool}
1660
1661 """
1662 self.params.rewrite_session_type()
1663 return self.params.session_type == 'S'
1664
1666 """\
1667 Test if this terminal session is a published applications session.
1668
1669 @return: C{True} if this session is of session type published applications ('P').
1670 @rtype: C{bool}
1671
1672 """
1673 self.params.rewrite_session_type()
1674 return self.params.session_type == 'P'
1675