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 cookie_filepath = None
533 if os.path.exists(os.path.normpath('%s/.pulse-cookie' % _LOCAL_HOME)):
534 cookie_filepath = os.path.normpath('%s/.pulse-cookie' % _LOCAL_HOME)
535 elif os.path.exists(os.path.normpath('%s/.config/pulse/cookie' % _LOCAL_HOME)):
536 cookie_filepath = os.path.normpath('%s/.config/pulse/cookie' % _LOCAL_HOME)
537 if cookie_filepath is not None:
538
539 cmd_line = "echo 'default-server=127.0.0.1:%s'>%s/.pulse-client.conf;" % (self.session_info.snd_port, self.session_info.remote_container) + \
540 "echo 'cookie-file=%s/.pulse-cookie'>>%s/.pulse-client.conf" % (self.session_info.remote_container, self.session_info.remote_container)
541 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
542
543 self.control_session._x2go_sftp_put(local_path=cookie_filepath, remote_path='%s/.pulse-cookie' % self.session_info.remote_container)
544
545
546 _tunnel = rforward.X2GoRevFwTunnel(server_port=self.session_info.snd_port,
547 remote_host='127.0.0.1',
548 remote_port=self.snd_port,
549 ssh_transport=self.control_session.get_transport(),
550 session_instance=self.session_instance,
551 logger=self.logger
552 )
553 else:
554 if self.client_instance:
555 self.client_instance.HOOK_on_sound_tunnel_failed(profile_name=self.profile_name, session_name=self.session_info.name)
556 elif self.params.snd_system == 'arts':
557
558
559
560 self.logger('the ArtsD sound server (as in KDE3) is obsolete and will not be supported by Python X2Go...', loglevel=log.loglevel_WARNING)
561
562 elif self.params.snd_system == 'esd':
563
564
565
566
567 self.logger('initializing ESD sound support in X2Go session', loglevel=log.loglevel_INFO)
568 self.control_session._x2go_sftp_put(local_path='%s/.esd_auth' % _LOCAL_HOME, remote_path='%s/.esd_auth' % self.control_session._x2go_remote_home)
569
570
571 _tunnel = rforward.X2GoRevFwTunnel(server_port=self.session_info.snd_port,
572 remote_host='127.0.0.1',
573 remote_port=self.snd_port,
574 ssh_transport=self.control_session.get_transport(),
575 session_instance=self.session_instance,
576 logger=self.logger
577 )
578
579
580 if _tunnel is not None:
581 self.reverse_tunnels[self.session_info.name]['snd'] = (self.session_info.snd_port, _tunnel)
582 _tunnel.start()
583 self.active_threads.append(_tunnel)
584
585 else:
586
587 self.reverse_tunnels[self.session_info.name]['snd'][1].resume()
588
590 """\
591 Initialize Paramiko/SSH reverse forwarding tunnel for X2Go folder sharing.
592
593 """
594 if not self.control_session.is_sshfs_available():
595 raise x2go_exceptions.X2GoUserException('Remote user %s is not allowed to share SSHFS resources with the server.' % self.session_info.username)
596
597
598 ssh_transport = self.control_session.get_transport()
599 if self.reverse_tunnels[self.session_info.name]['sshfs'][1] is None:
600
601 _tunnel = sftpserver.X2GoRevFwTunnelToSFTP(server_port=self.session_info.sshfs_port,
602 ssh_transport=ssh_transport,
603 auth_key=self.control_session._x2go_session_auth_rsakey,
604 session_instance=self.session_instance,
605 logger=self.logger
606 )
607
608 if _tunnel is not None:
609 self.reverse_tunnels[self.session_info.name]['sshfs'] = (self.session_info.sshfs_port, _tunnel)
610 _tunnel.start()
611 self.active_threads.append(_tunnel)
612 while not _tunnel.ready:
613 gevent.sleep(.1)
614
615 else:
616
617 self.reverse_tunnels[self.session_info.name]['sshfs'][1].resume()
618
620 """\
621 Pause reverse SSH tunnel of name <name>.
622
623 @param name: tunnel name (either of C{sshfs}, C{snd})
624 @type name: C{str}
625
626 """
627 _tunnel = self.reverse_tunnels[self.session_info.name][name][1]
628 if _tunnel is not None:
629 _tunnel.pause()
630
632 """\
633 Shutdown (pause) Paramiko/SSH reverse forwarding tunnel for X2Go sound.
634
635 """
636 self._x2go_pause_rev_fw_tunnel('snd')
637
639 """\
640 Shutdown (pause) Paramiko/SSH reverse forwarding tunnel for X2Go folder sharing.
641
642 """
643 self._x2go_pause_rev_fw_tunnel('sshfs')
644
646 """\
647 Initialize X2Go print spooling.
648
649 @raise X2GoUserException: if the X2Go printing feature is not available to this user
650
651 """
652 if not self.control_session.is_sshfs_available():
653 raise x2go_exceptions.X2GoUserException('Remote user %s is not allowed to use client-side printing.' % self.session_info.username)
654
655 spool_dir = os.path.join(self.session_info.local_container, 'spool')
656 if not os.path.exists(spool_dir):
657 os.makedirs(spool_dir)
658 self.share_local_folder(local_path=spool_dir, folder_type='spool')
659 self.print_queue = printqueue.X2GoPrintQueue(profile_name=self.profile_name,
660 session_name=self.session_info.name,
661 spool_dir=spool_dir,
662 print_action=self.print_action,
663 print_action_args=self.print_action_args,
664 client_instance=self.client_instance,
665 printing_backend=self.printing_backend,
666 logger=self.logger,
667 )
668 self.print_queue.start()
669 self.active_threads.append(self.print_queue)
670
672 """\
673 Set a print action for the next incoming print jobs.
674
675 This method is a wrapper for L{X2GoPrintQueue}C{.set_print_action()}.
676
677 @param print_action: print action name or object (i.e. an instance of C{X2GoPrintAction*} classes)
678 @type print_action: C{str} or C{X2GoPrintAction*}
679 @param kwargs: print action specific parameters
680 @type kwargs: dict
681
682 """
683 self.print_queue.set_print_action(print_action, logger=self.logger, **kwargs)
684
686 """\
687 Shutdown (pause) the X2Go Print Queue thread.
688
689 """
690 if self.print_queue is not None:
691 self.print_queue.pause()
692
694 """\
695 Return the server-side printing spooldir path.
696
697 @return: the directory for remote print job spooling
698 @rtype: C{str}
699
700 """
701 return '%s/%s' % (self.session_info.remote_container, 'spool')
702
703 - def start_mimebox(self, mimebox_extensions=[], mimebox_action=None):
704 """\
705 Initialize the X2Go MIME box. Open/process incoming files from the server-side locally.
706
707 @param mimebox_extensions: file name extensions that are allowed for local opening/processing
708 @type mimebox_extensions: C{list}
709 @param mimebox_action: MIME box action given as name or object (i.e. an instance of C{X2GoMIMEboxAction*} classes).
710 @type mimebox_action: C{str} or C{obj}
711
712 @raise X2GoUserException: if the X2Go MIME box feature is not available to this user
713
714 """
715 if not self.control_session.is_sshfs_available():
716 raise x2go_exceptions.X2GoUserException('Remote user %s is not allowed to use the MIME box.' % self.session_info.username)
717
718 mimebox_dir = os.path.join(self.session_info.local_container, 'mimebox')
719 if not os.path.exists(mimebox_dir):
720 os.makedirs(mimebox_dir)
721 self.share_local_folder(local_path=mimebox_dir, folder_type='mimebox')
722 self.mimebox_queue = mimebox.X2GoMIMEboxQueue(profile_name=self.profile_name,
723 session_name=self.session_info.name,
724 mimebox_dir=mimebox_dir,
725 mimebox_extensions=mimebox_extensions,
726 mimebox_action=mimebox_action,
727 client_instance=self.client_instance,
728 logger=self.logger,
729 )
730 self.mimebox_queue.start()
731 self.active_threads.append(self.mimebox_queue)
732
734 """\
735 Set a MIME box action for the next incoming MIME jobs.
736
737 This method is a wrapper for L{X2GoMIMEboxQueue}C{set_mimebox_action()}.
738
739 @param mimebox_action: MIME box action name or object (i.e. an instance of C{X2GoMIMEboxAction*} classes)
740 @type mimebox_action: C{str} or C{X2GoMIMEboxAction*}
741 @param kwargs: MIME box action specific parameters
742 @type kwargs: dict
743
744 """
745 self.mimebox_queue.set_mimebox_action(mimebox_action, logger=self.logger, **kwargs)
746
748 """\
749 Shutdown (pause) the X2Go MIME box Queue thread.
750
751 """
752 if self.mimebox_queue is not None:
753 self.mimebox_queue.pause()
754
756 """\
757 Return the server-side MIME box spooldir path.
758
759 @return: the directory where remote MIME box jobs are placed
760 @rtype: C{str}
761
762 """
763 return '%s/%s' % (self.session_info.remote_container, 'mimebox')
764
766 """\
767 Test if this terminal's session info object is write-protected.
768
769 @return: C{True}, if session info object is read-only, C{False} for read-write.
770 @rtype: C{bool}
771
772 """
773 self.session_info.is_protected()
774
776 """\
777 Protect this terminal session's info object against updates.
778
779 """
780 self.session_info.protect()
781
783 """\
784 Allow session info updates from within the list_sessions method of the control session.
785
786 """
787 self.session_info.unprotect()
788
790 """\
791 Share a local folder with the X2Go session.
792
793 @param local_path: the full path to an existing folder on the local
794 file system
795 @type local_path: C{str}
796 @param folder_type: one of 'disk' (a folder on your local hard drive), 'rm' (removeable device),
797 'cdrom' (CD/DVD Rom) or 'spool' (for X2Go print spooling)
798 @type folder_type: C{str}
799
800 @return: returns C{True} if the local folder has been successfully mounted within the X2Go server session
801 @rtype: C{bool}
802
803 @raise X2GoUserException: if local folder sharing is not available to this user
804 @raise Exception: any other exception occuring on the way is passed through by this method
805
806 """
807 if not self.control_session.is_sshfs_available():
808 raise x2go_exceptions.X2GoUserException('Remote user %s is not allowed to share local folders with the server.' % self.session_info.username)
809
810 if local_path is None:
811 self.logger('no folder name given...', log.loglevel_WARN)
812 return False
813
814 if type(local_path) not in (types.StringType, types.UnicodeType):
815 self.logger('folder name needs to be of type StringType...', log.loglevel_WARN)
816 return False
817
818 if not os.path.exists(local_path):
819 self.logger('local folder does not exist: %s' % local_path, log.loglevel_WARN)
820 return False
821
822 local_path = os.path.normpath(local_path)
823 self.logger('sharing local folder: %s' % local_path, log.loglevel_INFO)
824
825 _auth_rsakey = self.control_session._x2go_session_auth_rsakey
826 _host_rsakey = defaults.RSAHostKey
827
828 _tmp_io_object = cStringIO.StringIO()
829 _auth_rsakey.write_private_key(_tmp_io_object)
830 _tmp_io_object.write('----BEGIN RSA IDENTITY----')
831 _tmp_io_object.write('%s %s' % (_host_rsakey.get_name(),_host_rsakey.get_base64(),))
832
833
834 _x2go_key_fname = '%s/%s/%s' % (os.path.dirname(self.session_info.remote_container), 'ssh', 'key.z%s' % self.session_info.agent_pid)
835 _x2go_key_bundle = _tmp_io_object.getvalue()
836
837
838 self._share_local_folder_lock.acquire()
839
840 try:
841 self.control_session._x2go_sftp_write(_x2go_key_fname, _x2go_key_bundle)
842
843 _convert_encoding = self.params.convert_encoding
844 _client_encoding = self.params.client_encoding
845 _server_encoding = self.params.server_encoding
846
847 if _X2GOCLIENT_OS == 'Windows':
848 if local_path.startswith('\\\\'):
849
850 if 'X2GO_MOUNT_UNCPATHS' in self.control_session.get_server_features():
851 local_path = local_path.repalce('\\\\', '/uncpath/')
852 else:
853 local_path = local_path.repalce('\\\\', '/windrive/')
854 local_path = local_path.replace('\\', '/')
855 else:
856 local_path = local_path.replace('\\', '/')
857 local_path = local_path.replace(':', '')
858 local_path = '/windrive/%s' % local_path
859 _convert_encoding = True
860 _client_encoding = 'WINDOWS-1252'
861
862 if _convert_encoding:
863 export_iconv_settings = 'export X2GO_ICONV=modules=iconv,from_code=%s,to_code=%s && ' % (_client_encoding, _server_encoding)
864 else:
865 export_iconv_settings = ''
866
867 if folder_type == 'disk':
868
869 cmd_line = [ '%sexport HOSTNAME &&' % export_iconv_settings,
870 'x2gomountdirs',
871 'dir',
872 str(self.session_info.name),
873 '\'%s\'' % _CURRENT_LOCAL_USER,
874 _x2go_key_fname,
875 '%s__REVERSESSH_PORT__%s; ' % (local_path, self.session_info.sshfs_port),
876 'rm -f %s %s.ident' % (_x2go_key_fname, _x2go_key_fname),
877 ]
878
879 elif folder_type == 'spool':
880
881 cmd_line = [ '%sexport HOSTNAME &&' % export_iconv_settings,
882 'x2gomountdirs',
883 'dir',
884 str(self.session_info.name),
885 '\'%s\'' % _CURRENT_LOCAL_USER,
886 _x2go_key_fname,
887 '%s__PRINT_SPOOL___REVERSESSH_PORT__%s; ' % (local_path, self.session_info.sshfs_port),
888 'rm -f %s %s.ident' % (_x2go_key_fname, _x2go_key_fname),
889 ]
890
891 elif folder_type == 'mimebox':
892
893 cmd_line = [ '%sexport HOSTNAME &&' % export_iconv_settings,
894 'x2gomountdirs',
895 'dir',
896 str(self.session_info.name),
897 '\'%s\'' % _CURRENT_LOCAL_USER,
898 _x2go_key_fname,
899 '%s__MIMEBOX_SPOOL___REVERSESSH_PORT__%s; ' % (local_path, self.session_info.sshfs_port),
900 'rm -f %s %s.ident' % (_x2go_key_fname, _x2go_key_fname),
901 ]
902
903 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
904 _stdout = stdout.read().split('\n')
905 self.logger('x2gomountdirs output is: %s' % _stdout, log.loglevel_NOTICE)
906
907 except:
908 self._share_local_folder_lock.release()
909 raise
910 self._share_local_folder_lock.release()
911
912 if len(_stdout) >= 6 and _stdout[5].endswith('ok'):
913 return True
914 return False
915
917 """\
918 Unshare all local folders mount in the X2Go session.
919
920 @return: returns C{True} if all local folders could be successfully unmounted from the X2Go server session
921 @rtype: C{bool}
922
923 """
924 self.logger('unsharing all local folders from session %s' % self.session_info, log.loglevel_INFO)
925
926 cmd_line = [ 'export HOSTNAME &&',
927 'x2goumount-session',
928 self.session_info.name,
929 ]
930
931 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
932 if not stderr.read():
933 self.logger('x2goumount-session (all mounts) for session %s has been successful' % self.session_info, log.loglevel_NOTICE)
934 return True
935 else:
936 self.logger('x2goumount-session (all mounts) for session %s failed' % self.session_info, log.loglevel_ERROR)
937 return False
938
940 """\
941 Unshare local folder given as <local_path> from X2Go session.
942
943 @return: returns C{True} if the local folder <local_path> could be successfully unmounted from the X2Go server session
944 @rtype: C{bool}
945
946 """
947 self.logger('unsharing local folder from session %s' % self.session_info, log.loglevel_INFO)
948
949 cmd_line = [ 'export HOSTNAME &&',
950 'x2goumount-session',
951 self.session_info.name,
952 "'%s'" % local_path,
953 ]
954
955 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
956 if not stderr.read():
957 self.logger('x2goumount-session (%s) for session %s has been successful' % (local_path, self.session_info, ), log.loglevel_NOTICE)
958 return True
959 else:
960 self.logger('x2goumount-session (%s) for session %s failed' % (local_path, self.session_info, ), log.loglevel_ERROR)
961 return False
962
964 """\
965 Retrieve the session's color depth.
966
967 @return: the session's color depth
968 @rtype: C{int}
969
970 """
971 return self.params.depth
972
974 """\
975 Automatically generate an appropriate human-readable session window title.
976
977 The session window title will be provider in the C{session_title} property of
978 this method.
979
980 @param dont_set: generate the session window title, but do not actually set it
981 @type dont_set: C{bool}
982
983 """
984 _generic_title = 'X2GO-%s' % self.session_info.name
985
986
987 self.session_title = self.session_title.strip()
988
989 if self.params.session_type == 'D':
990 if self.set_session_title:
991
992 if not self.session_title:
993 self.session_title = '%s for %s@%s' % (self.params.cmd, self.control_session.remote_username(), self.control_session.get_hostname())
994
995 else:
996
997 self.session_title = _generic_title
998
999 elif self.params.session_type == 'S':
1000 if self.set_session_title:
1001
1002 shared_user = _generic_title.split('XSHAD')[1]
1003 shared_display = _generic_title.split('XSHAD')[2].replace('PP', ':').split("_")[0]
1004
1005 self.session_title = 'Desktop %s@%s shared with %s@%s' % (shared_user, shared_display, self.control_session.remote_username(), self.control_session.get_hostname())
1006
1007 else:
1008
1009 self.session_title = _generic_title
1010
1011 else:
1012
1013 self.session_title = _generic_title
1014
1015 if self.session_title != _generic_title and not dont_set:
1016 self.set_session_window_title(title=self.session_title)
1017
1019 """\
1020 Try for <timeout> seconds to find the X2Go session window of this
1021 terminal session.
1022
1023 A background thread will get spawned for this operation.
1024
1025 @param timeout: try for <timeout> seconds to find the session window
1026 @type timeout: C{int}
1027
1028 """
1029 gevent.spawn(self._find_session_window, timeout=timeout)
1030
1032 """\
1033 Try for <timeout> seconds to find the X2Go session window of this
1034 terminal session.
1035
1036 @param timeout: try for <timeout> seconds to find the session window
1037 @type timeout: C{int}
1038
1039 """
1040 self.session_window = None
1041
1042
1043
1044 timeout += 1
1045 while timeout:
1046
1047 timeout -= 1
1048
1049 window = utils.find_session_window(self.session_info.name)
1050
1051 if window is not None:
1052 self.logger('Session window ID for session %s is: %s' % (self.session_info.name, window.id), loglevel=log.loglevel_DEBUG)
1053 self.session_window = window
1054 break
1055
1056 gevent.sleep(1)
1057
1059 """\
1060 Modify the session window title.
1061
1062 A background thread will get spawned for this operation.
1063
1064 @param title: new title for the terminal session's session window
1065 @type title: C{str}
1066 @param timeout: try for <timeout> seconds to find the session window
1067 @type timeout: C{int}
1068
1069 """
1070 gevent.spawn(self._set_session_window_title, title=title.strip(), timeout=timeout)
1071
1073 """\
1074 Modify the session window title.
1075
1076 @param title: new title for the terminal session's session window
1077 @type title: C{str}
1078 @param timeout: try for <timeout> seconds to find the session window
1079 @type timeout: C{int}
1080
1081 """
1082 self.session_title = title
1083
1084 if not self.session_title:
1085 self.auto_session_title(dont_set=True)
1086
1087 timeout += 1
1088 while timeout:
1089
1090 timeout -= 1
1091
1092 if self.session_window is not None:
1093 self.logger('Setting session window title for session %s is: %s' % (self.session_info.name, self.session_title), loglevel=log.loglevel_DEBUG)
1094 utils.set_session_window_title(self.session_window, self.session_title)
1095 break
1096
1097 gevent.sleep(1)
1098
1100 """\
1101 Try for <timeout> seconds to raise the X2Go session window of this
1102 terminal session to the top and bring it to focus.
1103
1104 A background thread will get spawned for this operation.
1105
1106 @param timeout: try for <timeout> seconds to raise the session window
1107 @type timeout: C{int}
1108
1109 """
1110 gevent.spawn(self._raise_session_window, timeout=timeout)
1111
1113 """
1114 Try for <timeout> seconds to raise the X2Go session window of this
1115 terminal session to the top and bring it to focus.
1116
1117 @param timeout: try for <timeout> seconds to raise the session window
1118 @type timeout: C{int}
1119
1120 """
1121 timeout += 1
1122 while timeout:
1123
1124 timeout -= 1
1125
1126 if self.session_window is not None:
1127
1128 utils.raise_session_window(self.session_window)
1129 break
1130
1131 gevent.sleep(1)
1132
1134 """\
1135 ,,Guess'' if the command C{<cmd>} exists on the X2Go server and is executable.
1136 The expected result is not 100% safe, however, it comes with a high probability to
1137 be correct.
1138
1139 @param cmd: session command
1140 @type cmd: C{str}
1141
1142 @return: C{True} if this method reckons that the command is executable on the remote X2Go server
1143 @rtype: C{bool}
1144
1145 """
1146 test_cmd = None;
1147
1148 cmd = cmd.strip('"').strip('"')
1149 if cmd.find('RDP') != -1:
1150 cmd = 'rdesktop'
1151
1152 if cmd in _X2GO_GENERIC_APPLICATIONS:
1153 return True
1154 if cmd in _X2GO_DESKTOPSESSIONS.keys():
1155 return True
1156 elif 'XSHAD' in cmd:
1157 return True
1158 elif 'PUBLISHED' in cmd and 'X2GO_PUBLISHED_APPLICATIONS' in self.control_session.get_server_features():
1159 return True
1160 elif cmd and cmd.startswith('/'):
1161
1162 test_cmd = 'test -x %s && which %s && echo OK' % (cmd, os.path.basename(cmd.split()[0]))
1163 elif cmd and '/' not in cmd.split()[0]:
1164
1165 test_cmd = 'which %s && echo OK' % os.path.basename(cmd.split()[0])
1166
1167 if test_cmd:
1168 (stdin, stdout, stderr) = self.control_session._x2go_exec_command([test_cmd])
1169 _stdout = stdout.read()
1170 return _stdout.find('OK') != -1
1171 else:
1172 return False
1173
1175 """\
1176 Run a command in this session.
1177
1178 After L{X2GoTerminalSessionSTDOUT.start()} has been called
1179 one or more commands can be executed with L{X2GoTerminalSessionSTDOUT.run_command()}
1180 within the current X2Go session.
1181
1182 @param cmd: Command to be run
1183 @type cmd: C{str}
1184 @param env: add server-side environment variables
1185 @type env: C{dict}
1186
1187 @return: stdout.read() and stderr.read() as returned by the run command
1188 on the X2Go server
1189 @rtype: C{tuple} of C{str}
1190
1191 """
1192 if not self.has_command(_rewrite_cmd(str(self.params.cmd), params=self.params)):
1193 if self.client_instance:
1194 self.client_instance.HOOK_no_such_command(profile_name=self.profile_name, session_name=self.session_info.name, cmd=self.params.cmd)
1195 return False
1196
1197 if cmd in ("", None):
1198 if self.params.cmd is None:
1199 cmd = 'TERMINAL'
1200 else:
1201 cmd = self.params.cmd
1202
1203 if cmd == 'XDMCP':
1204
1205 return None
1206
1207 if 'XSHAD' in cmd:
1208
1209 return None
1210
1211 self.params.update(cmd=cmd)
1212
1213
1214 if '/' in cmd:
1215 cmd = os.path.basename(cmd)
1216
1217 cmd_line = [ "setsid x2goruncommand",
1218 str(self.session_info.display),
1219 str(self.session_info.agent_pid),
1220 str(self.session_info.name),
1221 str(self.session_info.snd_port),
1222 _rewrite_blanks(_rewrite_cmd(cmd, params=self.params)),
1223 str(self.params.snd_system),
1224 str(self.params.session_type),
1225 "1>/dev/null 2>/dev/null & exit",
1226 ]
1227
1228 if self.params.snd_system == 'pulse':
1229 cmd_line = [ 'PULSE_CLIENTCONFIG=%s/.pulse-client.conf' % self.session_info.remote_container ] + cmd_line
1230
1231 if env:
1232 for env_var in env.keys():
1233 cmd_line = [ '%s=%s' % (env_var, env[env_var]) ] + cmd_line
1234
1235 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
1236
1237 if self.params.kbtype not in ('null/null', 'auto') and (self.params.kblayout not in ('null', '') or self.params.kbvariant not in ('null', '')):
1238 self.set_keyboard(layout=self.params.kblayout, variant=self.params.kbvariant)
1239
1240 return stdout.read(), stderr.read()
1241
1243 """\
1244 Is this (terminal) session a desktop session?
1245
1246 @return: Returns C{True} is this session is a desktop session.
1247 @rtype: C{bool}
1248
1249 """
1250 if self.session_info:
1251 return self.session_info.is_desktop_session()
1252 return False
1253
1255 """\
1256 Is this (terminal) session a published applications provider?
1257
1258 @return: Returns C{True} is this session is a provider session for published applications.
1259 @rtype: C{bool}
1260
1261 """
1262 if self.session_info and self.is_running():
1263 return self.session_info.is_published_applications_provider()
1264 return False
1265
1267 """\
1268 Set the keyboard layout and variant for this (running) session.
1269
1270 @param layout: keyboard layout to be set
1271 @type layout: C{str}
1272 @param variant: keyboard variant to be set
1273 @type variant: C{str}
1274
1275 @return: returns C{True} if the {setxkbmap} command could be executed successfully.
1276 @rtype: C{bool}
1277
1278 """
1279 if not self.is_running():
1280 return False
1281
1282 cmd_line = [ 'export DISPLAY=:%s && ' % str(self.session_info.display),
1283 'setxkbmap '
1284 ]
1285
1286 if layout != 'null':
1287 self.logger('setting keyboad layout ,,%s\'\' for session %s' % (layout, self.session_info), log.loglevel_INFO)
1288 cmd_line.append('-layout %s' % layout)
1289 if variant != 'null':
1290 self.logger('setting keyboad variant ,,%s\'\' for session %s' % (variant, self.session_info), log.loglevel_INFO)
1291 cmd_line.append('-variant %s' % variant)
1292
1293 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
1294 _stderr = stderr.read()
1295 if not _stderr:
1296 self.logger('setting keyboard layout ,,%s\'\' and variant ,,%s\'\' for session %s has been successful' % (layout, variant, self.session_info), log.loglevel_NOTICE)
1297 return True
1298 else:
1299 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)
1300 return False
1301
1303 """\
1304 Executed a published application.
1305
1306 @param exec_name: application to be executed
1307 @type exec_name: C{str}
1308 @param timeout: execution timeout
1309 @type timeout: C{int}
1310 @param env: session environment dictionary
1311 @type env: C{dict}
1312
1313 """
1314 cmd_line = [
1315 "export DISPLAY=:%s && " % str(self.session_info.display),
1316 "export X2GO_SESSION=%s && " % str(self.get_session_name()),
1317 ]
1318
1319 if self.params.snd_system == 'pulse':
1320 cmd_line.append("export PULSE_CLIENTCONFIG=%s/.pulse-client.conf && " % self.session_info.remote_container)
1321
1322 if env:
1323 for env_var in env.keys():
1324 cmd_line = [ 'export %s=%s && ' % (env_var, env[env_var]) ] + cmd_line
1325
1326 cmd_line.extend(
1327 [
1328 "setsid %s" % exec_name,
1329 "1>/dev/null 2>/dev/null & exit",
1330 ]
1331 )
1332
1333 self.logger('executing published application %s for %s with command line: %s' % (exec_name, self.profile_name, cmd_line), loglevel=log.loglevel_DEBUG)
1334 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line, timeout=timeout)
1335
1337 """\
1338 X2Go session OK?
1339
1340 @return: Returns C{True} if this X2Go (terminal) session is up and running,
1341 C{False} otherwise.
1342 @rtype: C{bool}
1343
1344 """
1345 _ok = bool(self.session_info.name and self.proxy.ok())
1346 return _ok
1347
1349 """\
1350 X2Go session running?
1351
1352 @return: Returns C{True} if this X2Go (terminal) session is in running state,
1353 C{False} otherwise.
1354 @rtype: C{bool}
1355
1356 """
1357 return self.session_info.is_running()
1358
1360 """\
1361 X2Go session suspended?
1362
1363 @return: Returns C{True} if this X2Go (terminal) session is in suspended state,
1364 C{False} otherwise.
1365 @rtype: C{bool}
1366
1367 """
1368 return self.session_info.is_suspended()
1369
1371 """\
1372 X2Go session connected?
1373
1374 @return: Returns C{True} if this X2Go session's Paramiko/SSH transport is
1375 connected/authenticated, C{False} else.
1376 @rtype: C{bool}
1377
1378 """
1379 return self.control_session.is_connected()
1380
1382 """\
1383 Start a new X2Go session.
1384
1385 @return: C{True} if session startup has been successful and the X2Go proxy is up-and-running
1386 @rtype: C{bool}
1387
1388 @raise X2GoTerminalSessionException: if the session startup failed
1389 @raise X2GoDesktopSharingDenied: if desktop sharing fails because of denial by the user running the desktop to be shared
1390
1391 """
1392 self.params.rewrite_session_type()
1393
1394 if not self.has_command(_rewrite_cmd(self.params.cmd, params=self.params)):
1395 if self.client_instance:
1396 self.client_instance.HOOK_no_such_command(profile_name=self.profile_name, session_name=self.session_info.name, cmd=self.params.cmd)
1397 return False
1398
1399 setkbd = "0"
1400 if self.params.kbtype != "null/null":
1401 setkbd = "1"
1402
1403 if '/' in self.params.cmd:
1404 self.params.cmd = os.path.basename(self.params.cmd)
1405
1406 self.params.rewrite_session_type()
1407
1408 if self.params.geometry == 'maximize':
1409 _geometry = utils.get_workarea_geometry()
1410 if _geometry is None or len(_geometry) != 2:
1411 _geometry = utils.get_desktop_geometry()
1412 if _geometry and len(_geometry) == 2:
1413 self.params.geometry = "%sx%s" % _geometry
1414 else:
1415 self.logger('failed to detect best maxmimized geometry of your client-side desktop', loglevel=log.loglevel_WARN)
1416 self.params.geometry = "1024x768"
1417
1418 cmd_line = [ "x2gostartagent",
1419 str(self.params.geometry),
1420 str(self.params.link),
1421 str(self.params.pack),
1422 str(self.params.cache_type+'-depth_'+self.params.depth),
1423 str(self.params.kblayout),
1424 str(self.params.kbtype),
1425 str(setkbd),
1426 str(self.params.session_type),
1427 str(self.params.cmd),
1428 ]
1429
1430 if self.params.cmd == 'XDMCP' and self.params.xdmcp_server:
1431 cmd_line = ['X2GOXDMCP=%s' % self.params.xdmcp_server] + cmd_line
1432
1433 if self.params.dpi:
1434 cmd_line = ['X2GODPI=%s' % self.params.dpi] + cmd_line
1435
1436 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
1437
1438 _stdout = stdout.read()
1439 _stderr = stderr.read()
1440
1441
1442
1443 if "ACCESS DENIED" in _stderr and "XSHAD" in _stderr:
1444 raise x2go_exceptions.X2GoDesktopSharingDenied('X2Go desktop sharing has been denied by the remote user')
1445
1446 try:
1447 self.session_info.initialize(_stdout,
1448 username=self.control_session.remote_username(),
1449 hostname=self.control_session.remote_peername(),
1450 )
1451 except ValueError:
1452 raise x2go_exceptions.X2GoTerminalSessionException("failed to start X2Go session")
1453 except IndexError:
1454 raise x2go_exceptions.X2GoTerminalSessionException("failed to start X2Go session")
1455
1456
1457 self.session_info.local_container = os.path.join(self.params.rootdir, 'S-%s' % self.session_info.name)
1458
1459 self.session_info.remote_container = '%s/.x2go/C-%s' % (self.control_session._x2go_remote_home,
1460 self.session_info.name,
1461 )
1462
1463
1464 self.proxy = self.proxy_backend(session_info=self.session_info,
1465 ssh_transport=self.control_session.get_transport(),
1466 sessions_rootdir=self.sessions_rootdir,
1467 session_instance=self.session_instance,
1468 proxy_options=self.proxy_options,
1469 logger=self.logger)
1470 self.proxy_subprocess, proxy_ok = self.proxy.start_proxy()
1471
1472 if proxy_ok:
1473 self.active_threads.append(self.proxy)
1474
1475 if self.params.session_type in ('D', 'S'):
1476 self.find_session_window()
1477 self.auto_session_window_title()
1478 self.raise_session_window()
1479
1480 if self.params.published_applications:
1481 self.control_session.get_published_applications()
1482
1483 else:
1484 raise x2go_exceptions.X2GoTerminalSessionException("failed to start X2Go session")
1485
1486 return proxy_ok
1487
1489 """\
1490 Resume a running/suspended X2Go session.
1491
1492 @return: C{True} if the session could successfully be resumed
1493 @rtype: C{bool}
1494
1495 @raise X2GoTerminalSessionException: if the terminal session failed to update server-side reported port changes
1496
1497 """
1498 setkbd = "0"
1499 if self.params.kbtype != "null/null":
1500 setkbd = "1"
1501
1502 if self.params.geometry == 'maximize':
1503 _geometry = utils.get_workarea_geometry()
1504 if _geometry is None or len(_geometry) != 2:
1505 _geometry = utils.get_desktop_geometry()
1506 if _geometry and len(_geometry) == 2:
1507 self.params.geometry = "%sx%s" % _geometry
1508 else:
1509 self.logger('failed to detect best maxmimized geometry of your client-side desktop, using 1024x768 instead', loglevel=log.loglevel_WARN)
1510 self.params.geometry = "1024x768"
1511
1512 cmd_line = [ "x2goresume-session", self.session_info.name,
1513 self.params.geometry,
1514 self.params.link,
1515 self.params.pack,
1516 self.params.kblayout,
1517 self.params.kbtype,
1518 setkbd,
1519 ]
1520
1521 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
1522
1523
1524 for stdout_line in stdout.read():
1525 try:
1526 _new_value = stdout_line.split("=")[1].strip()
1527 if 'gr_port=' in stdout_line and _new_value != str(self.session_info.graphics_port):
1528 try:
1529 self.session_info.graphics_port = int(_new_value)
1530 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)
1531 except TypeError:
1532
1533 raise x2go_exceptions.X2GoTerminalSessionException('Failed to retrieve new graphics port from server. X2Go Session cannot be resumed.')
1534 elif 'sound_port=' in stdout_line and _new_value != str(self.session_info.snd_port):
1535 try:
1536 self.session_info.snd_port = int(_new_value)
1537 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)
1538 except TypeError:
1539 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)
1540 elif 'fs_port=' in stdout_line and _new_value != str(self.session_info.sshfs_port):
1541 try:
1542 self.session_info.sshfs_port = int(_new_value)
1543 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)
1544 except TypeError:
1545 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)
1546 except IndexError:
1547 continue
1548
1549
1550 self.session_info.local_container = os.path.join(self.params.rootdir, 'S-%s' % self.session_info.name)
1551
1552 self.session_info.remote_container = '%s/.x2go/C-%s' % (self.control_session._x2go_remote_home,
1553 self.session_info.name,
1554 )
1555 self.proxy = self.proxy_backend(session_info=self.session_info,
1556 ssh_transport=self.control_session.get_transport(),
1557 sessions_rootdir=self.sessions_rootdir,
1558 session_instance=self.session_instance,
1559 logger=self.logger
1560 )
1561 self.proxy_subprocess, proxy_ok = self.proxy.start_proxy()
1562
1563 if proxy_ok:
1564 self.params.depth = self.session_info.name.split('_')[2][2:]
1565
1566
1567 self.session_info.username = self.control_session.remote_username()
1568
1569 if self.params.kbtype not in ('null/null', 'auto') and (self.params.kblayout not in ('null', '') or self.params.kbvariant not in ('null', '')):
1570 self.set_keyboard(layout=self.params.kblayout, variant=self.params.kbvariant)
1571
1572 if self.params.session_type in ('D', 'S'):
1573 self.find_session_window()
1574 self.auto_session_window_title()
1575 self.raise_session_window()
1576
1577 if self.is_published_applications_provider():
1578 self.control_session.get_published_applications()
1579 self.published_applications = True
1580 else:
1581 raise x2go_exceptions.X2GoTerminalSessionException("failed to start X2Go session")
1582
1583 return proxy_ok
1584
1586 """\
1587 Suspend this X2Go (terminal) session.
1588
1589 @return: C{True} if the session terminal could be successfully suspended
1590 @rtype: C{bool}
1591
1592 """
1593 self.control_session.suspend(session_name=self.session_info.name)
1594 self.release_proxy()
1595
1596
1597 _ret = True
1598
1599 return _ret
1600
1602 """\
1603 Terminate this X2Go (terminal) session.
1604
1605 @return: C{True} if the session could be successfully terminated
1606 @rtype: C{bool}
1607
1608 """
1609 self.control_session.terminate(session_name=self.session_info.name, destroy_terminals=False)
1610 self.release_proxy()
1611 self.post_terminate_cleanup()
1612 self.__del__()
1613
1614
1615 _ret = True
1616
1617 return _ret
1618
1620 """\
1621 Let the X2Go proxy command cleanly die away... (by calling its destructor).
1622
1623 """
1624 if self.proxy is not None:
1625 self.proxy.__del__()
1626
1628 """\
1629 Do some cleanup after this session has terminated.
1630
1631 """
1632
1633
1634
1635 if not self._cleaned_up and self.session_info.name:
1636
1637
1638 self.logger('cleaning up session %s after termination' % self.session_info, loglevel=log.loglevel_NOTICE)
1639
1640
1641 if self.logger.get_loglevel() & log.loglevel_DEBUG != log.loglevel_DEBUG:
1642
1643 self._rm_session_dirtree()
1644 self._rm_desktop_dirtree()
1645
1646 self._cleaned_up = True
1647
1649 """\
1650 Test if this terminal session is a rootless session.
1651
1652 @return: C{True} if this session is of session type rootless ('R').
1653 @rtype: C{bool}
1654
1655 """
1656 self.params.rewrite_session_type()
1657 return self.params.session_type == 'R'
1658
1660 """\
1661 Test if this terminal session is a desktop sharing (aka shadow) session.
1662
1663 @return: C{True} if this session is of session type shadow ('S').
1664 @rtype: C{bool}
1665
1666 """
1667 self.params.rewrite_session_type()
1668 return self.params.session_type == 'S'
1669
1671 """\
1672 Test if this terminal session is a published applications session.
1673
1674 @return: C{True} if this session is of session type published applications ('P').
1675 @rtype: C{bool}
1676
1677 """
1678 self.params.rewrite_session_type()
1679 return self.params.session_type == 'P'
1680