1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 paramiko
33 import socket
34 import gevent
35 import string
36 import re
37 import subprocess
38
39
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 if _X2GOCLIENT_OS == 'Windows':
46 import win32api
47
49
50 """\
51 Test if a given compression method is valid for NX3 Proxy.
52
53 """
54 return method in _pack_methods_nx3
55
56
58 """\
59 Return the X2go session meta information as returned by the
60 C{x2golistsessions} server command for session C{session_name}.
61
62 """
63 sessions = stdout.read().split("\n")
64 for line in sessions:
65
66 if not line:
67 continue
68 if session_name == line.split("|")[1]:
69 return line
70 return None
71
72
74 """\
75 Normalizes string, converts to lowercase, removes non-alpha characters,
76 converts spaces to hyphens and replaces round brackets by pointed brackets.
77
78 """
79 import unicodedata
80 value = unicodedata.normalize('NFKD', unicode(value)).encode('ascii', 'ignore')
81 value = re.sub('[^\w\s-]', '', value).strip().lower()
82 value = re.sub('[(]', '<', value).strip().lower()
83 value = re.sub('[)]', '>', value).strip().lower()
84 return value
85
87 """\
88 Generate a session profile ID as used in x2goclient's sessions config file.
89
90 """
91 import datetime
92 return datetime.datetime.utcnow().strftime('%Y%m%d%H%m%S%f')
93
94
96 """\
97 Check an ini file data structure passed on by a user app or class.
98
99 """
100 if defaults is None:
101 return False
102 if type(defaults) is not types.DictType:
103 return False
104 for sub_dict in defaults.values():
105 if type(sub_dict) is not types.DictType:
106 return False
107 return True
108
109
111 """\
112 Check the data structure of a default session profile passed by a user app.
113
114 """
115 if defaults is None:
116 return False
117 if type(defaults) is not types.DictType:
118 return False
119 return True
120
121
123 """\
124 Convert session profile options as used in x2goclient's sessions file to
125 Python X2go session parameters.
126
127 """
128
129 _params = copy.deepcopy(_options)
130
131
132 _known_options = _X2GO_SESSIONPROFILE_DEFAULTS.keys()
133 for p in _params.keys():
134 if p not in _known_options:
135 del _params[p]
136
137 _rename_dict = {
138 'host': 'server',
139 'user': 'username',
140 'soundsystem': 'snd_system',
141 'sndport': 'snd_port',
142 'type': 'kbtype',
143 'layout': 'kblayout',
144 'speed': 'link',
145 'sshport': 'port',
146 'useexports': 'allow_share_local_folders',
147 'export': 'share_local_folders',
148 'usemimebox': 'allow_mimebox',
149 'mimeboxextensions': 'mimebox_extensions',
150 'mimeboxaction': 'mimebox_action',
151 'print': 'printing',
152 'name': 'profile_name',
153 'key': 'key_filename',
154 'command': 'cmd',
155 'rdpserver': 'rdp_server',
156 'rdpoptions': 'rdp_options',
157 'xdmcpserver': 'xdmcp_server',
158 'useiconv': 'convert_encoding',
159 'iconvto': 'server_encoding',
160 'iconvfrom': 'client_encoding',
161 'usesshproxy': 'use_sshproxy',
162 'sshproxyhost': 'sshproxy_host',
163 'sshproxyuser': 'sshproxy_user',
164 'sshproxykeyfile': 'sshproxy_key_filename',
165 'sshproxytunnel': 'sshproxy_tunnel',
166 }
167 _speed_dict = {
168 '0': 'modem',
169 '1': 'isdn',
170 '2': 'adsl',
171 '3': 'wan',
172 '4': 'lan',
173 }
174
175 for opt, val in _options.iteritems():
176
177
178 if opt in _rename_dict.keys():
179 del _params[opt]
180 opt = _rename_dict[opt]
181 _params[opt] = val
182
183
184 if opt == 'link':
185 val = str(val).lower()
186 if val in _speed_dict.keys():
187 val = _speed_dict[val]
188 val = val.lower()
189 _params['link'] = val
190
191
192 if opt in ('share_local_folders', 'mimebox_extensions'):
193 if type(val) is types.StringType:
194 if val:
195 _params[opt] = val.split(',')
196 else:
197 _params[opt] = []
198
199
200 if _params['quality']:
201 _params['pack'] = '%s-%s' % (_params['pack'], _params['quality'])
202 del _params['quality']
203
204 del _params['fstunnel']
205
206 if _params.has_key('share_local_folders'):
207 _params['share_local_folders'] = [ f for f in _params['share_local_folders'].split(',') if f ]
208
209 if not _options['fullscreen']:
210 _params['geometry'] = '%sx%s' % (_options['width'], _options['height'])
211 else:
212 _params['geometry'] = 'fullscreen'
213 del _params['width']
214 del _params['height']
215 del _params['fullscreen']
216
217 if not _options['sound']:
218 snd_system = 'none'
219 del _params['sound']
220
221 if _options['rootless']:
222 _params['session_type'] = 'application'
223 else:
224 _params['session_type'] = 'desktop'
225 del _params['rootless']
226
227 if _params['mimebox_action'] not in _X2GO_MIMEBOX_ACTIONS.keys():
228 _params['mimebox_action'] = 'OPEN'
229
230
231 _ignored_options = [
232 'dpi',
233 'setdpi',
234 'usekbd',
235 'startsoundsystem',
236 'soundtunnel',
237 'defsndport',
238 'icon',
239 'applications',
240 ]
241 for i in _ignored_options:
242 del _params[i]
243
244 return _params
245
246
248 """\
249 Sorts session profile names by their timestamp (as used in the file format's section name).
250
251 """
252 session_names = session_infos.keys()
253 sortable_session_names = [ '%s|%s' % (session_name.split('-')[2].split('_')[0], session_name) for session_name in session_names ]
254 sortable_session_names.sort()
255 return [ session_name.split('|')[1] for session_name in sortable_session_names ]
256
257
259 """\
260 Imitates the behaviour of the GNU/touch command.
261
262 @param filename: name of the file to touch
263 @type filename: C{str}
264 @param mode: the file mode (as used for Python file objects)
265 @type mode: C{str}
266 """
267 if not os.path.isdir(os.path.dirname(filename)):
268 os.makedirs(os.path.dirname(filename), mode=00700)
269 f = open(filename, mode=mode)
270 f.close()
271
272
274 """\
275 Imitates the behaviour of the GNU/uniq command.
276
277 @param seq: a list/sequence containing consecutive duplicates.
278 @type seq: C{list}
279
280 @return: list that has been clean up from the consecutive duplicates
281 @rtype: C{list}
282 """
283
284 noDupes = []
285 [noDupes.append(i) for i in seq if not noDupes.count(i)]
286 return noDupes
287
288
290 """\
291 Render a list of all-known-to-Python character encodings (including
292 all known aliases)
293
294 """
295 from encodings.aliases import aliases
296 _raw_encname_list = []
297 _raw_encname_list.extend(aliases.keys())
298 _raw_encname_list.extend(aliases.values())
299 _raw_encname_list.sort()
300 _encname_list = []
301 for _raw_encname in _raw_encname_list:
302 _encname = _raw_encname.upper()
303 _encname = _encname.replace('_', '-')
304 _encname_list.append(_encname)
305 _encname_list.sort()
306 _encname_list = unique(_encname_list)
307 return _encname_list
308
309
311 """\
312 Try to remove a file, wait for unlocking, remove it once removing is possible...
313
314 @param dirname: directory name the file is in
315 @type dirname: C{str}
316 @param filename: name of the file to be removed
317 @type filename: C{str}
318 """
319 _not_removed = True
320 while _not_removed:
321 try:
322 os.remove(os.path.join(dirname, filename))
323 _not_removed = False
324 except:
325
326 gevent.sleep(5)
327
329 """\
330 Detect an unused IP socket.
331
332 @param bind_address: IP address to bind to
333 @type bind_address: C{str}
334 @param preferred_port: IP socket port that shall be tried first for availability
335 @type preferred_port: C{str}
336
337 @return: free local IP socket port that can be used for binding
338 @rtype: C{str}
339 """
340
341 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
342 try:
343 if preferred_port:
344 sock.bind((bind_address, preferred_port))
345 ipaddr, port = sock.getsockname()
346 else:
347 raise
348 except:
349 sock.bind(('', 0))
350 ipaddr, port = sock.getsockname()
351 return port
352
354 """\
355 Detect systems default character encoding.
356
357 @return: The system's local character encoding.
358 @rtype: C{str}
359 """
360 try:
361 encoding = locale.getdefaultlocale()[1]
362 if encoding is None:
363 raise BaseException
364 except:
365 try:
366 encoding = sys.getdefaultencoding()
367 except:
368 encoding = 'ascii'
369 return encoding
370
372 """\
373 Test if a given path is an absolute path name.
374
375 @param path: test this path for absolutism...
376 @type path: C{str}
377
378 @return: Returns C{True} if path is an absolute path name
379 @rtype: C{bool}
380 """
381 return bool((path.startswith('/') or re.match('^[%s]\:\\\\' % string.ascii_letters, path)))
382
384 """\
385 Wrapper for: xprop -root _XKB_RULES_NAMES
386
387 @return: A Python dictionary that contains the current X11 keyboard rules.
388 @rtype: C{dict}
389
390 """
391 p = subprocess.Popen(['xprop', '-root', '_XKB_RULES_NAMES',], stdout=subprocess.PIPE, )
392 _rn_list = p.stdout.read().split('"')
393 _rn_dict = {
394 'rules': _rn_list[1],
395 'model': _rn_list[3],
396 'layout': _rn_list[5],
397 'variant': _rn_list[7],
398 'options': _rn_list[9],
399 }
400 return _rn_dict
401
403 """\
404 Detect the current local screen's color depth.
405
406 """
407 if _X2GOCLIENT_OS != 'Windows':
408 try:
409 p = subprocess.Popen(['xwininfo', '-root',], stdout=subprocess.PIPE, )
410 _depth_line = [ _info.strip() for _info in p.stdout.read().split('\n') if 'Depth:' in _info ][0]
411 _depth = _depth_line.split(' ')[1]
412 return int(_depth)
413 except IndexError:
414
415 return 24
416 except OSError:
417
418 return 24
419
420 else:
421 return win32api.GetSystemMetrics(2)
422
424 """\
425 Test if color depth of this session is compatible with the
426 local screen's color depth.
427
428 @param depth_session: color depth of the session
429 @type depth_session: C{int}
430 @param depth_local: color depth of local screen
431 @type depth_local: C{int}
432
433 @return: Does the session color depth work with the local display?
434 @rtype: C{bool}
435
436 """
437 if depth_session == 0:
438 return True
439 if depth_session == depth_local:
440 return True
441 if ( ( depth_session == 24 or depth_session == 32 ) and ( depth_local == 24 or depth_local == 32 ) ):
442 return true;
443 return False
444