1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
|
## common/helpers.py
##
## Contributors for this file:
## - Yann Le Boulanger <asterix@lagaule.org>
## - Nikos Kouremenos <kourem@gmail.com>
##
## Copyright (C) 2003-2005 Gajim Team
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
## by the Free Software Foundation; version 2 only.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
import sre
import os
import urllib
import errno
import sys
import stat
from pysqlite2 import dbapi2 as sqlite
import gajim
import logger
import i18n
from xmpp_stringprep import nodeprep, resourceprep, nameprep
try:
import winsound # windows-only built-in module for playing wav
except:
pass
_ = i18n._
Q_ = i18n.Q_
class InvalidFormat(Exception):
pass
def parse_jid(jidstring):
'''Perform stringprep on all JID fragments from a string
and return the full jid'''
# This function comes from http://svn.twistedmatrix.com/cvs/trunk/twisted/words/protocols/jabber/jid.py
user = None
server = None
resource = None
# Search for delimiters
user_sep = jidstring.find('@')
res_sep = jidstring.find('/')
if user_sep == -1:
if res_sep == -1:
# host
server = jidstring
else:
# host/resource
server = jidstring[0:res_sep]
resource = jidstring[res_sep + 1:] or None
else:
if res_sep == -1:
# user@host
user = jidstring[0:user_sep] or None
server = jidstring[user_sep + 1:]
else:
if user_sep < res_sep:
# user@host/resource
user = jidstring[0:user_sep] or None
server = jidstring[user_sep + 1:user_sep + (res_sep - user_sep)]
resource = jidstring[res_sep + 1:] or None
else:
# server/resource (with an @ in resource)
server = jidstring[0:res_sep]
resource = jidstring[res_sep + 1:] or None
return prep(user, server, resource)
def parse_resource(resource):
'''Perform stringprep on resource and return it'''
if resource:
try:
return resourceprep.prepare(unicode(resource))
except UnicodeError:
raise InvalidFormat, 'Invalid character in resource.'
def prep(user, server, resource):
'''Perform stringprep on all JID fragments and return the full jid'''
# This function comes from
#http://svn.twistedmatrix.com/cvs/trunk/twisted/words/protocols/jabber/jid.py
if user:
try:
user = nodeprep.prepare(unicode(user))
except UnicodeError:
raise InvalidFormat, _('Invalid character in username.')
else:
user = None
if not server:
raise InvalidFormat, _('Server address required.')
else:
try:
server = nameprep.prepare(unicode(server))
except UnicodeError:
raise InvalidFormat, _('Invalid character in hostname.')
if resource:
try:
resource = resourceprep.prepare(unicode(resource))
except UnicodeError:
raise InvalidFormat, _('Invalid character in resource.')
else:
resource = None
if user:
if resource:
return '%s@%s/%s' % (user, server, resource)
else:
return '%s@%s' % (user, server)
else:
if resource:
return '%s/%s' % (server, resource)
else:
return server
def temp_failure_retry(func, *args, **kwargs):
while True:
try:
return func(*args, **kwargs)
except (os.error, IOError), ex:
if ex.errno == errno.EINTR:
continue
else:
raise
def convert_bytes(string):
suffix = ''
# IEC standard says KiB = 1024 bytes KB = 1000 bytes
# but do we use the standard?
use_kib_mib = gajim.config.get('use_kib_mib')
align = 1024.
bytes = float(string)
if bytes >= align:
bytes = round(bytes/align, 1)
if bytes >= align:
bytes = round(bytes/align, 1)
if bytes >= align:
bytes = round(bytes/align, 1)
if use_kib_mib:
#GiB means gibibyte
suffix = _('%s GiB')
else:
#GB means gigabyte
suffix = _('%s GB')
else:
if use_kib_mib:
#MiB means mibibyte
suffix = _('%s MiB')
else:
#MB means megabyte
suffix = _('%s MB')
else:
if use_kib_mib:
#KiB means kibibyte
suffix = _('%s KiB')
else:
#KB means kilo bytes
suffix = _('%s KB')
else:
#B means bytes
suffix = _('%s B')
return suffix % unicode(bytes)
def get_uf_show(show, use_mnemonic = False):
'''returns a userfriendly string for dnd/xa/chat
and makes all strings translatable
if use_mnemonic is True, it adds _ so GUI should call with True
for accessibility issues'''
if show == 'dnd':
if use_mnemonic:
uf_show = _('_Busy')
else:
uf_show = _('Busy')
elif show == 'xa':
if use_mnemonic:
uf_show = _('_Not Available')
else:
uf_show = _('Not Available')
elif show == 'chat':
if use_mnemonic:
uf_show = _('_Free for Chat')
else:
uf_show = _('Free for Chat')
elif show == 'online':
if use_mnemonic:
uf_show = _('_Available')
else:
uf_show = _('Available')
elif show == 'connecting':
uf_show = _('Connecting')
elif show == 'away':
if use_mnemonic:
uf_show = _('A_way')
else:
uf_show = _('Away')
elif show == 'offline':
if use_mnemonic:
uf_show = _('_Offline')
else:
uf_show = _('Offline')
elif show == 'invisible':
if use_mnemonic:
uf_show = _('_Invisible')
else:
uf_show = _('Invisible')
elif show == 'not in the roster':
uf_show = _('Not in the roster')
elif show == 'requested':
uf_show = Q_('?contact has status:Unknown')
else:
uf_show = Q_('?contact has status:Has errors')
return unicode(uf_show)
def get_uf_sub(sub):
if sub == 'none':
uf_sub = Q_('?Subscription we already have:None')
elif sub == 'to':
uf_sub = _('To')
elif sub == 'from':
uf_sub = _('From')
elif sub == 'both':
uf_sub = _('Both')
else:
uf_sub = sub
return unicode(uf_sub)
def get_uf_ask(ask):
if ask is None:
uf_ask = Q_('?Ask (for Subscription):None')
elif ask == 'subscribe':
uf_ask = _('Subscribe')
else:
uf_ask = ask
return unicode(uf_ask)
def get_uf_role(role, plural = False):
''' plural determines if you get Moderators or Moderator'''
if role == 'none':
role_name = Q_('?Group Chat Contact Role:None')
elif role == 'moderator':
if plural:
role_name = _('Moderators')
else:
role_name = _('Moderator')
elif role == 'participant':
if plural:
role_name = _('Participants')
else:
role_name = _('Participant')
elif role == 'visitor':
if plural:
role_name = _('Visitors')
else:
role_name = _('Visitor')
return role_name
def get_sorted_keys(adict):
keys = adict.keys()
keys.sort()
return keys
def to_one_line(msg):
msg = msg.replace('\\', '\\\\')
msg = msg.replace('\n', '\\n')
# s1 = 'test\ntest\\ntest'
# s11 = s1.replace('\\', '\\\\')
# s12 = s11.replace('\n', '\\n')
# s12
# 'test\\ntest\\\\ntest'
return msg
def from_one_line(msg):
# (?<!\\) is a lookbehind assertion which asks anything but '\'
# to match the regexp that follows it
# So here match '\\n' but not if you have a '\' before that
re = sre.compile(r'(?<!\\)\\n')
msg = re.sub('\n', msg)
msg = msg.replace('\\\\', '\\')
# s12 = 'test\\ntest\\\\ntest'
# s13 = re.sub('\n', s12)
# s14 s13.replace('\\\\', '\\')
# s14
# 'test\ntest\\ntest'
return msg
def get_uf_chatstate(chatstate):
'''removes chatstate jargon and returns user friendly messages'''
if chatstate == 'active':
return _('is paying attention to the conversation')
elif chatstate == 'inactive':
return _('is doing something else')
elif chatstate == 'composing':
return _('is composing a message...')
elif chatstate == 'paused':
#paused means he or she was compoing but has stopped for a while
return _('paused composing a message')
elif chatstate == 'gone':
return _('has closed the chat window or tab')
return ''
def is_in_path(name_of_command, return_abs_path = False):
# if return_abs_path is True absolute path will be returned
# for name_of_command
# on failures False is returned
is_in_dir = False
found_in_which_dir = None
path = os.getenv('PATH').split(':')
for path_to_directory in path:
try:
contents = os.listdir(path_to_directory)
except OSError: # user can have something in PATH that is not a dir
pass
else:
is_in_dir = name_of_command in contents
if is_in_dir:
if return_abs_path:
found_in_which_dir = path_to_directory
break
if found_in_which_dir:
abs_path = os.path.join(path_to_directory, name_of_command)
return abs_path
else:
return is_in_dir
def launch_browser_mailer(kind, uri):
#kind = 'url' or 'mail'
if os.name == 'nt':
try:
os.startfile(uri) # if pywin32 is installed we open
except:
pass
else:
if kind == 'url' and not\
(uri.startswith('http://') or uri.startswith('https://')):
uri = 'http://' + uri
elif kind == 'mail' and not uri.startswith('mailto:'):
uri = 'mailto:' + uri
if gajim.config.get('openwith') == 'gnome-open':
command = 'gnome-open'
elif gajim.config.get('openwith') == 'kfmclient exec':
command = 'kfmclient exec'
elif gajim.config.get('openwith') == 'custom':
if kind == 'url':
command = gajim.config.get('custombrowser')
if kind == 'mail':
command = gajim.config.get('custommailapp')
if command == '': # if no app is configured
return
# we add the uri in "" so we have good parsing from shell
uri = uri.replace('"', '\\"') # escape "
command = command + ' "' + uri + '" &'
try: #FIXME: when we require python2.4+ use subprocess module
os.system(command)
except:
pass
def launch_file_manager(path_to_open):
if os.name == 'nt':
try:
os.startfile(path_to_open) # if pywin32 is installed we open
except:
pass
else:
if gajim.config.get('openwith') == 'gnome-open':
command = 'gnome-open'
elif gajim.config.get('openwith') == 'kfmclient exec':
command = 'kfmclient exec'
elif gajim.config.get('openwith') == 'custom':
command = gajim.config.get('custom_file_manager')
if command == '': # if no app is configured
return
# we add the path in "" so we have good parsing from shell
path_to_open = path_to_open.replace('"', '\\"') # escape "
command = command + ' "' + path_to_open + '" &'
try: #FIXME: when we require python2.4+ use subprocess module
os.system(command)
except:
pass
def play_sound(event):
if not gajim.config.get('sounds_on'):
return
path_to_soundfile = gajim.config.get_per('soundevents', event, 'path')
if path_to_soundfile == 'beep':
print '\a' # make a speaker beep
return
if not os.path.exists(path_to_soundfile):
return
if os.name == 'nt':
try:
winsound.PlaySound(path_to_soundfile,
winsound.SND_FILENAME|winsound.SND_ASYNC)
except:
pass
elif os.name == 'posix':
if gajim.config.get('soundplayer') == '':
return
player = gajim.config.get('soundplayer')
# we add the path in "" so we have good parsing from shell
path_to_soundfile = path_to_soundfile.replace('"', '\\"') # escape "
command = player + ' "' + path_to_soundfile + '" &'
#FIXME: when we require 2.4+ use subprocess module
os.system(command)
def get_file_path_from_dnd_dropped_uri(uri):
path = urllib.url2pathname(uri) # escape special chars
path = path.strip('\r\n\x00') # remove \r\n and NULL
# get the path to file
if path.startswith('file:\\\\\\'): # windows
path = path[8:] # 8 is len('file:///')
elif path.startswith('file://'): # nautilus, rox
path = path[7:] # 7 is len('file://')
elif path.startswith('file:'): # xffm
path = path[5:] # 5 is len('file:')
return path
def from_xs_boolean_to_python_boolean(value):
# this is xs:boolean so 'true','false','1','0'
# convert those to True/False (python booleans)
if value in ('1', 'true'):
val = True
else: # '0', 'false' or anything else
val = False
return val
def ensure_unicode_string(s):
# py23 u'abc'.decode('utf-8') raises
# python24 does not. is python23 is ooold we can remove this func
# FIXME: remove this when we abandon py23
if isinstance(s, str):
s = s.decode('utf-8')
return s
def get_xmpp_show(show):
if show in ('online', 'offline'):
return None
return show
def one_account_connected():
'''returns True if at least one account is connected, else False'''
one_connected = False
accounts = gajim.connections.keys()
for acct in accounts:
if gajim.connections[acct].connected > 1:
one_connected = True
break
return one_connected
def get_output_of_command(command):
try:
child_stdin, child_stdout = os.popen2(command)
except ValueError:
return None
output = child_stdout.readlines()
child_stdout.close()
child_stdin.close()
return output
def get_global_show():
maxi = 0
for account in gajim.connections:
if not gajim.config.get_per('accounts', account,
'sync_with_global_status'):
continue
connected = gajim.connections[account].connected
if connected > maxi:
maxi = connected
return gajim.SHOW_LIST[maxi]
def get_icon_name_to_show(contact, account = None):
'''Get the icon name to show in online, away, requested, ...'''
if account and gajim.awaiting_events[account].has_key(contact.jid):
#FIXME: change icon for FT
return 'message'
if contact.jid.find('@') <= 0: # if not '@' or '@' starts the jid ==> agent
return contact.show
if contact.sub in ('both', 'to'):
return contact.show
if contact.ask == 'subscribe':
return 'requested'
transport = gajim.get_transport_name_from_jid(contact.jid)
if transport:
return contact.show
return 'not in the roster'
def decode_string(string):
'''try to decode (to make it Unicode instance) given string'''
# by the time we go to iso15 it better be the one else we show bad characters
encodings = (sys.getfilesystemencoding(), 'utf-8', 'iso-8859-15')
for encoding in encodings:
try:
string = string.decode(encoding)
except UnicodeError:
continue
break
return string
|