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