Package x2go :: Module utils
[frames] | no frames]

Source Code for Module x2go.utils

  1  # -*- coding: utf-8 -*- 
  2   
  3  # Copyright (C) 2010-2014 by Mike Gabriel <mike.gabriel@das-netzwerkteam.de> 
  4  # 
  5  # Python X2Go is free software; you can redistribute it and/or modify 
  6  # it under the terms of the GNU Affero General Public License as published by 
  7  # the Free Software Foundation; either version 3 of the License, or 
  8  # (at your option) any later version. 
  9  # 
 10  # Python X2Go is distributed in the hope that it will be useful, 
 11  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 13  # GNU Affero General Public License for more details. 
 14  # 
 15  # You should have received a copy of the GNU Affero General Public License 
 16  # along with this program; if not, write to the 
 17  # Free Software Foundation, Inc., 
 18  # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 
 19   
 20  """\ 
 21  Python X2Go helper functions, constants etc. 
 22   
 23  """ 
 24  __NAME__ = 'x2goutils-pylib' 
 25   
 26  import sys 
 27  import os 
 28  import locale 
 29  import re 
 30  import types 
 31  import copy 
 32  import socket 
 33  import gevent 
 34  import string 
 35  import subprocess 
 36  import distutils.version 
 37  import paramiko 
 38   
 39  # Python X2Go modules 
 40  from defaults import X2GOCLIENT_OS as _X2GOCLIENT_OS 
 41  from defaults import X2GO_SESSIONPROFILE_DEFAULTS as _X2GO_SESSIONPROFILE_DEFAULTS 
 42  from defaults import X2GO_MIMEBOX_ACTIONS as _X2GO_MIMEBOX_ACTIONS 
 43  from defaults import pack_methods_nx3 
 44   
 45  from defaults import BACKENDS as _BACKENDS 
 46   
 47  import x2go_exceptions 
 48   
 49  if _X2GOCLIENT_OS != 'Windows': 
 50      import Xlib 
 51      from defaults import X_DISPLAY as _X_DISPLAY 
 52   
 53  if _X2GOCLIENT_OS == 'Windows': 
 54      import win32gui 
 55      import win32print 
 56      import win32con 
 57   
58 -def is_in_nx3packmethods(method):
59 60 """\ 61 Test if a given compression method is valid for NX3 Proxy. 62 63 @return: C{True} if C{method} is in the hard-coded list of NX3 compression methods. 64 @rtype: C{bool} 65 66 """ 67 return method in pack_methods_nx3
68 69
70 -def find_session_line_in_x2golistsessions(session_name, stdout):
71 """\ 72 Return the X2Go session meta information as returned by the 73 C{x2golistsessions} server command for session C{session_name}. 74 75 @param session_name: name of a session 76 @type session_name: C{str} 77 @param stdout: raw output from the ,,x2golistsessions'' command, as list of strings 78 @type stdout: C{list} 79 80 @return: the output line that contains C{<session_name>} 81 @rtype: C{str} or C{None} 82 83 """ 84 sessions = stdout.read().split("\n") 85 for line in sessions: 86 # skip empty lines 87 if not line: 88 continue 89 if session_name == line.split("|")[1]: 90 return line 91 return None
92 93
94 -def slugify(value):
95 """\ 96 Normalizes string, converts to lowercase, removes non-alpha characters, 97 converts spaces to hyphens and replaces round brackets by pointed brackets. 98 99 @param value: a string that shall be sluggified 100 @type value: C{str} 101 102 @return: the sluggified string 103 @rtype: C{str} 104 105 """ 106 import unicodedata 107 value = unicodedata.normalize('NFKD', unicode(value)).encode('ascii', 'ignore') 108 value = re.sub('[^\w\s-]', '', value).strip().lower() 109 value = re.sub('[(]', '<', value).strip().lower() 110 value = re.sub('[)]', '>', value).strip().lower() 111 return value
112
113 -def _genSessionProfileId():
114 """\ 115 Generate a session profile ID as used in x2goclient's sessions config file. 116 117 @return: profile ID 118 @rtype: C{str} 119 120 """ 121 import datetime 122 return datetime.datetime.utcnow().strftime('%Y%m%d%H%m%S%f')
123 124
125 -def _checkIniFileDefaults(data_structure):
126 """\ 127 Check an ini file data structure passed on by a user app or class. 128 129 @param data_structure: an ini file date structure 130 @type data_structure: C{dict} of C{dict}s 131 132 @return: C{True} if C{data_structure} matches that of an ini file data structure 133 @rtype: C{bool} 134 135 """ 136 if data_structure is None: 137 return False 138 if type(data_structure) is not types.DictType: 139 return False 140 for sub_dict in data_structure.values(): 141 if type(sub_dict) is not types.DictType: 142 return False 143 return True
144 145
146 -def _checkSessionProfileDefaults(data_structure):
147 """\ 148 Check the data structure of a default session profile passed by a user app. 149 150 @param data_structure: an ini file date structure 151 @type data_structure: C{dict} of C{dict}s 152 153 @return: C{True} if C{data_structure} matches that of an ini file data structure 154 @rtype: C{bool} 155 156 """ 157 if data_structure is None: 158 return False 159 if type(data_structure) is not types.DictType: 160 return False 161 return True
162 163
164 -def _convert_SessionProfileOptions_2_SessionParams(options):
165 """\ 166 Convert session profile options as used in x2goclient's sessions file to 167 Python X2Go session parameters. 168 169 @param options: a dictionary of options, parameter names as in the X2Go ,,sessions'' file 170 @type options: C{dict} 171 172 @return: session options as used in C{X2GoSession} instances 173 @rtype: C{dict} 174 175 """ 176 _params = copy.deepcopy(options) 177 178 # get rid of unknown session profile options 179 _known_options = _X2GO_SESSIONPROFILE_DEFAULTS.keys() 180 for p in _params.keys(): 181 if p not in _known_options: 182 del _params[p] 183 184 _rename_dict = { 185 'host': 'server', 186 'user': 'username', 187 'soundsystem': 'snd_system', 188 'sndport': 'snd_port', 189 'type': 'kbtype', 190 'layout': 'kblayout', 191 'variant': 'kbvariant', 192 'speed': 'link', 193 'sshport': 'port', 194 'useexports': 'allow_share_local_folders', 195 'restoreexports': 'restore_shared_local_folders', 196 'usemimebox': 'allow_mimebox', 197 'mimeboxextensions': 'mimebox_extensions', 198 'mimeboxaction': 'mimebox_action', 199 'print': 'printing', 200 'name': 'profile_name', 201 'key': 'key_filename', 202 'command': 'cmd', 203 'rdpserver': 'rdp_server', 204 'rdpoptions': 'rdp_options', 205 'xdmcpserver': 'xdmcp_server', 206 'useiconv': 'convert_encoding', 207 'iconvto': 'server_encoding', 208 'iconvfrom': 'client_encoding', 209 'usesshproxy': 'use_sshproxy', 210 'sshproxyhost': 'sshproxy_host', 211 'sshproxyport': 'sshproxy_port', 212 'sshproxyuser': 'sshproxy_user', 213 'sshproxykeyfile': 'sshproxy_key_filename', 214 'sessiontitle': 'session_title', 215 'setsessiontitle': 'set_session_title', 216 'published': 'published_applications', 217 'autostart': 'auto_start_or_resume', 218 'autoconnect': 'auto_connect', 219 'forwardsshagent': 'forward_sshagent', 220 'autologin': 'look_for_keys', 221 'sshproxyautologin': 'sshproxy_look_for_keys', 222 'uniquehostkeyaliases': 'unique_hostkey_aliases', 223 } 224 _speed_dict = { 225 '0': 'modem', 226 '1': 'isdn', 227 '2': 'adsl', 228 '3': 'wan', 229 '4': 'lan', 230 } 231 232 for opt, val in options.iteritems(): 233 234 # rename options if necessary 235 if opt in _rename_dict.keys(): 236 del _params[opt] 237 opt = _rename_dict[opt] 238 if opt in _known_options: 239 _type = type(_known_options[opt]) 240 _params[opt] = _type(val) 241 else: 242 _params[opt] = val 243 244 # translate integer values for connection speed to readable strings 245 if opt == 'link': 246 val = str(val).lower() 247 if val in _speed_dict.keys(): 248 val = _speed_dict[val] 249 val = val.lower() 250 _params['link'] = val 251 252 # share_local_folders is a list 253 if opt in ('share_local_folders', 'mimebox_extensions'): 254 if type(val) is types.StringType: 255 if val: 256 _params[opt] = val.split(',') 257 else: 258 _params[opt] = [] 259 260 if _params['cmd'] == "XFCE4": _params['cmd'] = "XFCE" 261 if _params['look_for_keys']: 262 _params['allow_agent'] = True 263 if _params['sshproxy_look_for_keys']: 264 _params['sshproxy_allow_agent'] = True 265 266 # append value for quality to value for pack method 267 if _params['quality']: 268 _params['pack'] = '%s-%s' % (_params['pack'], _params['quality']) 269 # delete quality in any case... 270 del _params['quality'] 271 272 del _params['fstunnel'] 273 274 if _params.has_key('export'): 275 276 _export = _params['export'] 277 del _params['export'] 278 if type(_export) is types.DictType: 279 280 # since Python X2Go 0.5.0.0 281 _params['share_local_folders'] = _export.keys() 282 283 else: 284 285 # before Python X2Go 0.5.0.0 (analysing the INI file's format for the export field) 286 287 # fix for wrong export field usage in PyHoca-GUI/CLI and python-x2go before 20110923 288 _export = _export.replace(",", ";") 289 290 _export = _export.strip().strip('"').strip().strip(';').strip() 291 _export_list = [ f for f in _export.split(';') if f ] 292 293 _params['share_local_folders'] = [] 294 for _shared_folder in _export_list: 295 # fix for wrong export field usage in PyHoca-GUI/CLI and python-x2go before 20110923 296 if not ":" in _shared_folder: _shared_folder = "%s:1" % _shared_folder 297 if _shared_folder.split(":")[-1] == "1": 298 _params['share_local_folders'].append(":".join(_shared_folder.split(":")[:-1])) 299 300 if options['fullscreen']: 301 _params['geometry'] = 'fullscreen' 302 elif options['maxdim']: 303 _params['geometry'] = 'maximize' 304 else: 305 _params['geometry'] = '%sx%s' % (options['width'], options['height']) 306 del _params['width'] 307 del _params['height'] 308 del _params['fullscreen'] 309 del _params['maxdim'] 310 311 if not options['sound']: 312 _params['snd_system'] = 'none' 313 del _params['sound'] 314 315 if not options['rootless']: 316 _params['session_type'] = 'desktop' 317 else: 318 _params['session_type'] = 'application' 319 del _params['rootless'] 320 321 if _params['mimebox_action'] not in _X2GO_MIMEBOX_ACTIONS.keys(): 322 _params['mimebox_action'] = 'OPEN' 323 324 if not options['usekbd']: 325 _params['kbtype'] = 'null/null' 326 _params['kblayout'] = 'null' 327 _params['kbvariant'] = 'null' 328 del _params['usekbd'] 329 330 if not _params['kbtype'].strip(): _params['kbtype'] = 'null/null' 331 if not _params['kblayout'].strip(): _params['kblayout'] = 'null' 332 if not _params['kbvariant'].strip(): _params['kbvariant'] = 'null' 333 334 if not options['setdpi']: 335 del _params['dpi'] 336 del _params['setdpi'] 337 338 if options['sshproxysameuser']: 339 _params['sshproxy_user'] = _params['username'] 340 del _params['sshproxysameuser'] 341 if options['sshproxysamepass']: 342 _params['sshproxy_reuse_authinfo'] = True 343 _params['sshproxy_key_filename'] = _params['key_filename'] 344 del _params['sshproxysamepass'] 345 346 if _params['use_sshproxy']: 347 348 # compat code for Python X2Go 0.2.1.0 -> 0.2.2.0 349 if options.has_key('sshproxytunnel'): 350 if not options['sshproxytunnel'].startswith('DEPRECATED'): 351 _params['server'] = options['sshproxytunnel'].split(":")[-2] 352 _params['port'] = options['sshproxytunnel'].split(":")[-1] 353 try: del _params['sshproxytunnel'] 354 except KeyError: pass 355 356 357 # currently known but ignored in Python X2Go 358 _ignored_options = [ 359 'startsoundsystem', 360 'soundtunnel', 361 'defsndport', 362 'icon', 363 'xinerama', 364 'multidisp', 365 'display', 366 'krblogin', 367 'directrdp', 368 'directrdpsettings', 369 'rdpclient', 370 'rdpport', 371 'sshproxytype', 372 ] 373 for i in _ignored_options: 374 del _params[i] 375 376 return _params
377 378
379 -def session_names_by_timestamp(session_infos):
380 """\ 381 Sorts session profile names by their timestamp (as used in the file format's section name). 382 383 @param session_infos: a dictionary of session infos as reported by L{X2GoClient.list_sessions()} 384 @type session_infos: C{dict} 385 386 @return: a timestamp-sorted list of session names found in C{session_infos} 387 @rtype: C{list} 388 389 """ 390 session_names = session_infos.keys() 391 sortable_session_names = [ '%s|%s' % (session_name.split('-')[-1].split('_')[0], session_name) for session_name in session_names ] 392 sortable_session_names.sort() 393 return [ session_name.split('|')[1] for session_name in sortable_session_names ]
394 395
396 -def touch_file(filename, mode='a'):
397 """\ 398 Imitates the behaviour of the GNU/touch command. 399 400 @param filename: name of the file to touch 401 @type filename: C{str} 402 @param mode: the file mode (as used for Python file objects) 403 @type mode: C{str} 404 405 """ 406 if not os.path.isdir(os.path.dirname(filename)): 407 os.makedirs(os.path.dirname(filename), mode=00700) 408 f = open(filename, mode=mode) 409 f.close()
410 411
412 -def unique(seq):
413 """\ 414 Imitates the behaviour of the GNU/uniq command. 415 416 @param seq: a list/sequence containing consecutive duplicates. 417 @type seq: C{list} 418 419 @return: list that has been clean up from the consecutive duplicates 420 @rtype: C{list} 421 422 """ 423 # order preserving 424 noDupes = [] 425 [noDupes.append(i) for i in seq if not noDupes.count(i)] 426 return noDupes
427 428
429 -def known_encodings():
430 """\ 431 Render a list of all-known-to-Python character encodings (including 432 all known aliases) 433 434 """ 435 from encodings.aliases import aliases 436 _raw_encname_list = [] 437 _raw_encname_list.extend(aliases.keys()) 438 _raw_encname_list.extend(aliases.values()) 439 _raw_encname_list.sort() 440 _encname_list = [] 441 for _raw_encname in _raw_encname_list: 442 _encname = _raw_encname.upper() 443 _encname = _encname.replace('_', '-') 444 _encname_list.append(_encname) 445 _encname_list.sort() 446 _encname_list = unique(_encname_list) 447 return _encname_list
448 449
450 -def patiently_remove_file(dirname, filename):
451 """\ 452 Try to remove a file, wait for unlocking, remove it once removing is possible... 453 454 @param dirname: directory name the file is in 455 @type dirname: C{str} 456 @param filename: name of the file to be removed 457 @type filename: C{str} 458 459 """ 460 _not_removed = True 461 while _not_removed: 462 try: 463 os.remove(os.path.join(dirname, filename)) 464 _not_removed = False 465 except: 466 # file is probably locked 467 gevent.sleep(5)
468 469
470 -def detect_unused_port(bind_address='127.0.0.1', preferred_port=None):
471 """\ 472 Detect an unused IP socket. 473 474 @param bind_address: IP address to bind to 475 @type bind_address: C{str} 476 @param preferred_port: IP socket port that shall be tried first for availability 477 @type preferred_port: C{str} 478 479 @return: free local IP socket port that can be used for binding 480 @rtype: C{str} 481 482 """ 483 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) 484 try: 485 if preferred_port: 486 sock.bind((bind_address, preferred_port)) 487 ipaddr, port = sock.getsockname() 488 else: 489 raise 490 except: 491 sock.bind(('', 0)) 492 ipaddr, port = sock.getsockname() 493 return port
494 495
496 -def get_encoding():
497 """\ 498 Detect systems default character encoding. 499 500 @return: The system's local character encoding. 501 @rtype: C{str} 502 503 """ 504 try: 505 encoding = locale.getdefaultlocale()[1] 506 if encoding is None: 507 raise BaseException 508 except: 509 try: 510 encoding = sys.getdefaultencoding() 511 except: 512 encoding = 'ascii' 513 return encoding
514 515
516 -def is_abs_path(path):
517 """\ 518 Test if a given path is an absolute path name. 519 520 @param path: test this path for absolutism... 521 @type path: C{str} 522 523 @return: Returns C{True} if path is an absolute path name 524 @rtype: C{bool} 525 526 """ 527 return bool((path.startswith('/') or re.match('^[%s]\:\\\\' % string.ascii_letters, path)))
528 529
530 -def xkb_rules_names():
531 """\ 532 Wrapper for: xprop -root _XKB_RULES_NAMES 533 534 @return: A Python dictionary that contains the current X11 keyboard rules. 535 @rtype: C{dict} 536 537 """ 538 p = subprocess.Popen(['xprop', '-root', '_XKB_RULES_NAMES',], stdout=subprocess.PIPE, ) 539 _rn_list = p.stdout.read().split('"') 540 _rn_dict = { 541 'rules': _rn_list[1], 542 'model': _rn_list[3], 543 'layout': _rn_list[5], 544 'variant': _rn_list[7], 545 'options': _rn_list[9], 546 } 547 return _rn_dict
548
549 -def local_color_depth():
550 """\ 551 Detect the current local screen's color depth. 552 553 @return: the local color depth in bits 554 @rtype: C{int} 555 556 """ 557 if _X2GOCLIENT_OS != 'Windows': 558 try: 559 p = subprocess.Popen(['xwininfo', '-root',], stdout=subprocess.PIPE, ) 560 _depth_line = [ _info.strip() for _info in p.stdout.read().split('\n') if 'Depth:' in _info ][0] 561 _depth = _depth_line.split(' ')[1] 562 return int(_depth) 563 except IndexError: 564 # a sensible default value 565 return 24 566 except OSError: 567 # for building your package... 568 return 24 569 570 else: 571 # This gets the color depth of the primary monitor. All monitors need not have the same color depth. 572 dc = win32gui.GetDC(None) 573 _depth = win32print.GetDeviceCaps(dc, win32con.BITSPIXEL) * win32print.GetDeviceCaps(dc, win32con.PLANES) 574 win32gui.ReleaseDC(None, dc) 575 return _depth
576
577 -def is_color_depth_ok(depth_session, depth_local):
578 """\ 579 Test if color depth of this session is compatible with the 580 local screen's color depth. 581 582 @param depth_session: color depth of the session 583 @type depth_session: C{int} 584 @param depth_local: color depth of local screen 585 @type depth_local: C{int} 586 587 @return: Does the session color depth work with the local display? 588 @rtype: C{bool} 589 590 """ 591 if depth_session == 0: 592 return True 593 if depth_session == depth_local: 594 return True 595 if ( ( depth_session == 24 or depth_session == 32 ) and ( depth_local == 24 or depth_local == 32 ) ): 596 return True; 597 if ( ( depth_session == 16 or depth_session == 17 ) and ( depth_local == 16 or depth_local == 17 ) ): 598 return True; 599 return False
600 601
602 -def find_session_window(session_name):
603 """\ 604 Find a session window by its X2GO session ID. 605 606 @param session_name: session name/ID of an X2Go session window 607 @type session_name: C{str} 608 609 @return: the window object (or ID) of the searched for session window 610 @rtype: C{obj} on Unix, C{int} on Windows 611 612 """ 613 if _X2GOCLIENT_OS != 'Windows': 614 # establish connection to the win API in use... 615 display = _X_DISPLAY 616 if display: 617 root = display.screen().root 618 619 success = False 620 windowIDs_obj = root.get_full_property(display.intern_atom('_NET_CLIENT_LIST'), Xlib.X.AnyPropertyType) 621 622 if windowIDs_obj is None: 623 # works with i3 session manager... 624 windowIDs_obj = root.get_full_property(display.intern_atom('_NET_CLIENT_LIST_STACKING'), Xlib.X.AnyPropertyType) 625 626 if windowIDs_obj is not None: 627 windowIDs = windowIDs_obj.value 628 629 for windowID in windowIDs: 630 window = display.create_resource_object('window', windowID) 631 try: 632 name = window.get_wm_name() 633 except Xlib.error.BadWindow: 634 continue 635 if name is not None and "X2GO-{session_name}".format(session_name=session_name) == name: 636 success = True 637 break 638 639 if success: 640 return window 641 642 else: 643 644 windows = [] 645 window = None 646 647 def _callback(hwnd, extra): 648 if win32gui.GetWindowText(hwnd) == "X2GO-%s" % session_name: 649 windows.append(hwnd)
650 651 win32gui.EnumWindows(_callback, None) 652 if len(windows): window = windows[0] 653 654 return window 655 656
657 -def get_desktop_geometry():
658 """\ 659 Get the geometry of the current screen's desktop. 660 661 @return: a (<width>, <height>) tuple will be returned 662 @rtype: C{tuple} 663 664 """ 665 if _X2GOCLIENT_OS != 'Windows': 666 display = _X_DISPLAY 667 if display: 668 root = display.screen().root 669 return (root.get_geometry().width, root.get_geometry().height) 670 671 return None
672
673 -def get_workarea_geometry():
674 """\ 675 Get the geometry of the current screen's work area by 676 wrapping around:: 677 678 xprop -root '_NET_WORKAREA' 679 680 @return: a (<width>, <height>) tuple will be returned 681 @rtype: C{tuple} 682 683 """ 684 if _X2GOCLIENT_OS != 'Windows': 685 p = subprocess.Popen(['xprop', '-root', '_NET_WORKAREA',], stdout=subprocess.PIPE, ) 686 _list = p.stdout.read().rstrip('\n').split(',') 687 if len(_list) == 4: 688 return (_list[2].strip(), _list[3].strip()) 689 else: 690 return None 691 else: 692 693 return None
694 695
696 -def set_session_window_title(session_window, session_title):
697 """\ 698 Set title of session window. 699 700 @param session_window: session window instance 701 @type session_window: C{obj} 702 @param session_title: session title to be set for C{session_window} 703 @type session_title: C{str} 704 705 """ 706 if _X2GOCLIENT_OS != 'Windows': 707 try: 708 session_window.set_wm_name(str(session_title)) 709 session_window.set_wm_icon_name(str(session_title)) 710 _X_DISPLAY.sync() 711 except Xlib.error.BadWindow: 712 pass 713 714 else: 715 win32gui.SetWindowText(session_window, session_title)
716 717
718 -def raise_session_window(session_window):
719 """\ 720 Raise session window. Not functional for Unix-like operating systems. 721 722 @param session_window: session window instance 723 @type session_window: C{obj} 724 725 """ 726 if _X2GOCLIENT_OS != 'Windows': 727 pass 728 else: 729 if session_window is not None: 730 win32gui.SetForegroundWindow(session_window)
731 732
733 -def merge_ordered_lists(l1, l2):
734 """\ 735 Merge sort two sorted lists 736 737 @param l1: first sorted list 738 @type l1: C{list} 739 @param l2: second sorted list 740 @type l2: C{list} 741 742 @return: the merge result of both sorted lists 743 @rtype: C{list} 744 745 """ 746 ordered_list = [] 747 748 # Copy both the args to make sure the original lists are not 749 # modified 750 l1 = l1[:] 751 l2 = l2[:] 752 753 while (l1 and l2): 754 if l1[0] not in l2: 755 item = l1.pop(0) 756 elif l2[0] not in l1: 757 item = l2.pop(0) 758 elif l1[0] in l2: 759 item = l1.pop(0) 760 l2.remove(item) 761 if item not in ordered_list: 762 ordered_list.append(item) 763 764 # Add the remaining of the lists 765 ordered_list.extend(l1 if l1 else l2) 766 767 return ordered_list
768
769 -def compare_versions(version_a, op, version_b):
770 """\ 771 Compare <version_a> with <version_b> using operator <op>. 772 In the background C{distutils.version.LooseVersion} is 773 used for the comparison operation. 774 775 @param version_a: a version string 776 @type version_a: C{str} 777 @param op: an operator provide as string (e.g. '<', '>', '==', '>=' etc.) 778 @type op: C{str} 779 @param version_b: another version string that is to be compared with <version_a> 780 @type version_b: C{str} 781 782 """ 783 784 ### FIXME: this comparison is not reliable with beta et al. version strings 785 786 ver_a = distutils.version.LooseVersion(version_a) 787 ver_b = distutils.version.LooseVersion(version_b) 788 789 return eval("ver_a %s ver_b" % op)
790
791 -class ProgressStatus(object):
792 """\ 793 A simple progress status iterator class. 794 795 """
796 - def __init__(self, progress_event, progress_func=range(0, 100, 10)):
797 """\ 798 @param progress_event: a threading.Event() object that gets notified on progress 799 @type progress_event: C{obj} 800 @param progress_func: a function that delivers a value between 0 and 100 (progress percentage value) 801 @type progress_func: C{func} 802 803 """ 804 self.ev = progress_event 805 self.progress_func = progress_func
806
807 - def __iter__(self):
808 """\ 809 Intialize the L{ProgressStatus} iterator object. 810 811 """ 812 self.status = self.progress_func() 813 return self
814
815 - def next(self):
816 """\ 817 On each iteration wait for the progress event to get triggered from an outside 818 part of the application. 819 820 Once the event fires read the progress status from the progress retrieval function 821 and clear the event afterwards (so we wait for the next firing of the event). 822 823 """ 824 if self.status < 100 and self.status != -1: 825 self.ev.wait() 826 self.status = self.progress_func() 827 self.ev.clear() 828 return self.status 829 else: 830 raise StopIteration
831
832 -def _get_backend_class(backend, class_name):
833 # silence pyflakes, the _this_class var will be assigned in an exec() statement below... 834 _this_class = None 835 if type(backend) not in (types.StringType, types.UnicodeType): return backend 836 backend = backend.upper() 837 available_backends = [ k for k in _BACKENDS[class_name].keys() if k != 'default' ] 838 # if for backend is given 'default' use the default backend module 839 if backend == 'default': backend = _BACKENDS[class_name]['default'] 840 if backend in available_backends: 841 exec("from {backend} import {class_name} as _this_class".format(backend=_BACKENDS[class_name][backend], class_name=class_name)) 842 else: 843 raise x2go_exceptions.X2GoBackendException('unknown backend name %s for class %s' % (backend, class_name)) 844 return _this_class
845
846 -def genkeypair(local_username, client_address, key_type='RSA'):
847 """\ 848 Generate an SSH pub/priv key pair without writing the private key to file. 849 850 @param local_username: the key is for this user 851 @type local_username: C{unicode} 852 @param client_address: the key is only valid for this client 853 @type client_address: C{unicode} 854 @param key_type: either of: RSA, DSA 855 @type key_type: C{unicode} 856 857 """ 858 key = None 859 pubkey = None 860 861 # generate key pair 862 if unicode(key_type) == u'RSA': 863 key = paramiko.RSAKey.generate(2048) 864 elif unicode(key_type) == u'DSA': 865 key = paramiko.DSSKey.generate(1024) 866 867 if key: 868 869 # assemble the public key 870 if key_type == "RSA": 871 pubkey_type = 'ssh-rsa' 872 elif key_type == "DSA": 873 pubkey_type = 'ssh-dss' 874 875 # FIXME: the from option does not work properly by some reason. Fix it later 876 #pubkey = "from={client_address},no-X11-forwarding,no-pty,no-user-rc {pubkey_type} {pubkey} {local_username}@{client_address}".format(pubkey=key.get_base64(), pubkey_type=pubkey_type, local_username=local_username, client_address=client_address) 877 pubkey = "no-X11-forwarding,no-pty,no-user-rc {pubkey_type} {pubkey} {local_username}@{client_address}".format(pubkey=key.get_base64(), pubkey_type=pubkey_type, local_username=local_username, client_address=client_address) 878 879 return (pubkey, key)
880 881
882 -def which(basename):
883 """\ 884 Python equivalent to the shell command "which". 885 886 @param basename: the basename of an application to be search for in $PATH 887 @type basename: C{str} 888 889 @return: full path to the application 890 @rtype: C{str} 891 892 """ 893 if _X2GOCLIENT_OS == 'Windows': 894 delim = ";" 895 else: 896 delim = ":" 897 898 for path in os.environ["PATH"].split(delim): 899 if os.path.exists(os.path.join(path, basename)): 900 return os.path.join(path, basename) 901 902 return None
903