1 # -*- coding: UTF-8 -*-
2 # vim: expandtab sw=4 ts=4 sts=4:
5 Misc functions like charset conversion, entries parsers,..
7 __author__
= 'Michal Čihař'
8 __email__
= 'michal@cihar.com'
10 Copyright © 2003 - 2008 Michal Čihař
12 This program is free software; you can redistribute it and/or modify it
13 under the terms of the GNU General Public License version 2 as published by
14 the Free Software Foundation.
16 This program is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
21 You should have received a copy of the GNU General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
39 from Wammu
.Locales
import StrConv
42 if Wammu
.gammu_error
== None:
49 elif txt
[-8:] == 'DATETIME' or txt
== 'Date' or txt
== 'LastModified' or txt
== 'LAST_MODIFIED':
51 elif txt
[-4:] == 'DATE':
53 elif txt
in ['TEXT', 'DESCRIPTION', 'LOCATION', 'LUID'] or txt
[:4] == 'Text':
55 elif txt
== 'PHONE' or txt
[:6] == 'Number':
57 elif txt
== 'CONTACTID':
59 elif txt
== 'PRIVATE' or txt
== 'Private' or txt
== 'COMPLETED':
61 elif txt
== 'Category' or txt
== 'CATEGORY':
63 elif txt
== 'PictureID' or txt
== 'RingtoneID' or txt
== 'RingtoneFileSystemID':
68 def SearchLocation(lst
, loc
, second
= None):
70 for i
in range(len(lst
)):
72 if not lst
[i
][second
[0]] == second
[1]:
74 if type(lst
[i
]['Location']) == type(loc
):
75 if loc
== lst
[i
]['Location']:
79 if str(loc
) in lst
[i
]['Location'].split(', '):
84 def MatchesText(item
, match
, num
):
85 testkeys
= ['Value', 'Text', 'Number']
87 if type(item
) == dict:
91 if type(val
) in (str, unicode):
92 if match
.search(val
) != None:
94 elif num
is not None and type(val
) == int and num
== val
:
96 elif type(val
) == list:
97 for i
in range(len(val
)):
101 if type(val2
) in (str, unicode):
102 if match
.search(val2
) != None:
104 elif num
is not None and type(val2
) == int and num
== val2
:
107 # Ignore not found keys
112 def SearchItem(lst
, item
):
113 for i
in range(len(lst
)):
118 def GrabNumberPrefix(number
, prefixes
):
120 if l
== 0 or number
[0] != '+':
123 while not number
[:i
] in prefixes
:
129 '''Prefix for making international numbers'''
132 NumberStrip
= re
.compile('^([#*]\d+[#*])?(\\+?.*)$')
134 def NormalizeNumber(number
):
136 Attempts to create international number from anything it receives.
137 It does strip any network prefixes and attempts to properly add
138 international prefix. However this is a bit tricky, as there are
139 many ways which can break this.
141 # Strip magic prefixes (like no CLIR)
142 nbmatch
= NumberStrip
.match(number
)
143 resnumber
= nbmatch
.group(2)
144 # If we stripped whole number, return original
145 if len(resnumber
) == 0:
147 # Handle 00 prefix same as +
148 if resnumber
[0:2] == '00':
149 resnumber
= '+' + resnumber
[2:]
150 # Detect numbers with international prefix and without +
151 # This can be national number in some countries (eg. US)
152 if NumberPrefix
[0] == '+' and resnumber
[:len(NumberPrefix
) - 1] == NumberPrefix
[1:]:
153 resnumber
= '+' + resnumber
154 # Add international prefix
155 if resnumber
[0] != '+':
156 resnumber
= NumberPrefix
+ resnumber
159 def SearchNumber(lst
, number
):
160 for i
in range(len(lst
)):
161 for x
in lst
[i
]['Entries']:
162 if GetItemType(x
['Type']) == 'phone' and NormalizeNumber(number
) == NormalizeNumber(x
['Value']):
166 def GetContactLink(lst
, i
, txt
):
167 return StrConv('<a href="memory://%s/%d">%s</a> (%s)' % (lst
[i
]['MemoryType'], lst
[i
]['Location'], lst
[i
]['Name'], txt
))
169 def GetNumberLink(lst
, number
):
170 i
= SearchNumber(lst
, number
)
172 return StrConv(number
)
173 return GetContactLink(lst
, i
, number
)
175 def GetTypeString(type, value
, values
, linkphone
= True):
177 Returns string for entry in data dictionary. Formats it according to
178 knowledge of format types.
180 t
= GetItemType(type)
182 i
= SearchLocation(values
['contact']['ME'], value
)
186 return GetContactLink([] + values
['contact']['ME'], i
, str(value
))
187 elif linkphone
and t
== 'phone':
188 return StrConv(GetNumberLink([] + values
['contact']['ME'] + values
['contact']['SM'], value
))
195 return StrConv(value
)
197 def ParseMemoryEntry(entry
, config
= None):
208 for i
in entry
['Entries']:
209 if i
['Type'] == 'Text_Name':
211 elif i
['Type'] == 'Text_FirstName':
213 if i
['Type'] == 'Text_LastName':
215 if i
['Type'] == 'Text_NickName':
216 nickname
= i
['Value']
217 if i
['Type'] == 'Text_FormalName':
218 formalname
= i
['Value']
219 if i
['Type'] == 'Date':
221 if i
['Type'] == 'Text_Company':
223 if i
['Type'] == 'Number_General':
224 number_result
= i
['Value']
225 elif i
['Type'][:7] == 'Number_':
231 format
= config
.Read('/Wammu/NameFormat')
233 if format
== 'custom':
234 name_result
= config
.Read('/Wammu/NameFormatString') % {
238 'NickName' : nickname
,
239 'FormalName' : formalname
,
247 if format
== 'auto-first-last':
248 name_result
= '%s %s' % (first
, last
)
250 name_result
= '%s, %s' % (last
, first
)
256 name_result
= nickname
257 elif formalname
!= '':
258 name_result
= formalname
262 if name_result
== '':
264 name_result
= company
267 name_result
= '%s (%s)' % (name_result
, company
)
269 if number_result
== '':
270 number_result
= number
272 entry
['Number'] = number_result
273 entry
['Name'] = name_result
274 entry
['Synced'] = False
279 def ParseTodo(entry
):
283 for i
in entry
['Entries']:
284 if i
['Type'] == 'END_DATETIME':
286 elif i
['Type'] == 'TEXT':
288 elif i
['Type'] == 'COMPLETED':
293 entry
['Completed'] = completed
296 entry
['Synced'] = False
299 def ParseCalendar(entry
):
307 for i
in entry
['Entries']:
308 if i
['Type'] == 'END_DATETIME':
309 end
= str(i
['Value'])
310 elif i
['Type'] == 'START_DATETIME':
311 start
= str(i
['Value'])
312 elif i
['Type'] == 'TONE_ALARM_DATETIME':
313 tone_alarm
= _('enabled (tone)')
314 elif i
['Type'] == 'SILENT_ALARM_DATETIME':
315 silent_alarm
= _('enabled (silent)')
316 elif i
['Type'] == 'TEXT':
318 elif i
['Type'] == 'DESCRIPTION':
319 description
= i
['Value']
320 elif i
['Type'] == 'REPEAT_MONTH':
321 recurrence
= _('yearly')
322 elif i
['Type'] == 'REPEAT_DAY':
323 recurrence
= _('monthly')
324 elif i
['Type'] == 'REPEAT_FREQUENCY':
326 recurrence
= _('daily')
327 elif i
['Value'] == 2:
328 recurrence
= _('biweekly')
329 elif (i
['Type'] == 'REPEAT_DAYOFWEEK'):
331 recurrence
= _('weekly on monday')
332 elif i
['Value'] == 2:
333 recurrence
= _('weekly on tuesday')
334 elif i
['Value'] == 3:
335 recurrence
= _('weekly on wednesday')
336 elif i
['Value'] == 4:
337 recurrence
= _('weekly on thursday')
338 elif i
['Value'] == 5:
339 recurrence
= _('weekly on friday')
340 elif i
['Value'] == 6:
341 recurrence
= _('weekly on saturday')
342 elif i
['Value'] == 7:
343 recurrence
= _('weekly on sunday')
345 if tone_alarm
is not None:
346 entry
['Alarm'] = tone_alarm
347 elif silent_alarm
is not None:
348 entry
['Alarm'] = silent_alarm
350 entry
['Alarm'] = _('disabled')
352 if recurrence
is None:
353 entry
['Recurrence'] = _('nonrecurring')
355 entry
['Recurrence'] = recurrence
358 entry
['Text'] = description
359 elif description
== '':
362 entry
['Text'] = '%s (%s)' % (text
, description
)
364 entry
['Start'] = start
366 entry
['Synced'] = False
369 def ParseMessage(msg
, parseinfo
= False):
372 msg
['Folder'] = msg
['SMS'][0]['Folder']
373 msg
['State'] = msg
['SMS'][0]['State']
374 msg
['Number'] = msg
['SMS'][0]['Number']
375 msg
['Name'] = msg
['SMS'][0]['Name']
376 msg
['DateTime'] = msg
['SMS'][0]['DateTime']
378 for i
in msg
['SMSInfo']['Entries']:
379 if i
['Buffer'] != None:
380 txt
= txt
+ i
['Buffer']
383 txt
= txt
+ i
['Text']
387 loc
= loc
+ str(i
['Location'])
394 if x
in string
.printable
:
397 msg
['Location'] = loc
398 msg
['Synced'] = False
401 def ProcessMessages(list, synced
):
406 data
= gammu
.LinkSMS(list)
410 v
= gammu
.DecodeSMS(x
)
414 ParseMessage(i
, (v
!= None))
416 if i
['State'] == 'Read':
418 elif i
['State'] == 'UnRead':
420 elif i
['State'] == 'Sent':
422 elif i
['State'] == 'UnSent':
425 return {'read':read
, 'unread':unread
, 'sent':sent
, 'unsent':unsent
}
427 def FormatError(txt
, info
):
428 if info
['Code'] == gammu
.Errors
['ERR_NOTSUPPORTED']:
429 message
= _('Your phone doesn\'t support this function.')
430 elif info
['Code'] == gammu
.Errors
['ERR_NOTIMPLEMENTED']:
431 message
= _('This function is not implemented for your phone. If you want help with implementation please contact authors.')
432 elif info
['Code'] == gammu
.Errors
['ERR_SECURITYERROR']:
433 message
= _('Your phone asks for PIN.')
434 elif info
['Code'] == gammu
.Errors
['ERR_FULL']:
435 message
= _('Memory is full, try deleting some entries.')
436 elif info
['Code'] == gammu
.Errors
['ERR_CANCELED']:
437 message
= _('Communication canceled by phone, did you press cancel on phone?')
438 elif info
['Code'] == gammu
.Errors
['ERR_EMPTY']:
439 message
= _('Empty entry received. This usually should not happen and most likely is caused by bug in phone firmware or in Gammu/Wammu.\n\nIf you miss some entry, please contact Gammu/Wammu authors.')
440 elif info
['Code'] == gammu
.Errors
['ERR_INSIDEPHONEMENU']:
441 message
= _('Please close opened menu in phone and retry, data can not be accessed while you have opened them.')
442 elif info
['Code'] == gammu
.Errors
['ERR_TIMEOUT']:
443 message
= _('Timeout while trying to communicate with phone. Maybe phone is not connected (for cable) or out of range (for bluetooth or IrDA).')
444 elif info
['Code'] == gammu
.Errors
['ERR_DEVICENOTEXIST']:
445 message
= _('Device for communication with phone does not exist. Maybe you don\'t have phone plugged or your configuration is wrong.')
446 elif info
['Code'] == gammu
.Errors
['ERR_DEVICENOPERMISSION']:
447 message
= _('Can not access device for communication with phone.')
448 if sys
.platform
== 'linux2':
449 message
+= ' ' + _('Maybe you need to be member of some group to have acces to device.')
451 message
= '%s %s\n%s %s\n%s %d' % (_('Description:'), StrConv(info
['Text']), _('Function:'), info
['Where'], _('Error code:'), info
['Code'])
452 return StrConv(txt
+ '\n\n' + message
)
454 def FixupMaskedEdit(edit
):
455 # XXX: this is not clean way of reseting to system colour, but I don't know better.
456 bgc
= wx
.SystemSettings
.GetColour(wx
.SYS_COLOUR_LISTBOX
)
457 fgc
= wx
.SystemSettings
.GetColour(wx
.SYS_COLOUR_WINDOWTEXT
)
458 setattr(edit
, '_validBackgroundColour', bgc
)
459 setattr(edit
, '_foregroundColour', fgc
)
461 def GetWebsiteLang():
462 (loc
, charset
) = locale
.getdefaultlocale()
464 if loc
[:2].lower() == 'cs':
470 def DBUSServiceAvailable(bus
, interface
, try_start_service
=False):
475 if try_start_service
:
477 bus
.start_service_by_name(interface
)
478 except dbus
.exceptions
.DBusException
:
479 print 'Failed to start DBus service %s' % interface
480 obj
= bus
.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
481 dbus_iface
= dbus
.Interface(obj
, 'org.freedesktop.DBus')
482 avail
= dbus_iface
.ListNames()
483 return interface
in avail
486 def CheckDeviceNode(curdev
):
488 Checks whether it makes sense to perform searching on this device and
489 possibly warns user about misconfigurations.
491 Returns tuple of 4 members:
492 - error code (0 = ok, -1 = device does not exits, -2 = no permissions)
497 if sys
.platform
== 'win32':
500 if curdev
[:3] == 'COM':
502 win32file
.QueryDosDevice(curdev
)
503 return (0, '', '', '')
506 _('Device %s does not exist!') % curdev
,
507 _('Error opening device'),
508 _('Device %s does not exist!') % curdev
511 return (0, '', '', '')
512 if not os
.path
.exists(curdev
):
514 _('Device %s does not exist!') % curdev
,
515 _('Error opening device'),
516 _('Device %s does not exist!') % curdev
518 if not os
.access(curdev
, os
.R_OK
) or not os
.access(curdev
, os
.W_OK
):
519 gid
= os
.stat(curdev
).st_gid
521 group
= grp
.getgrgid(gid
)[0]
525 _('You don\'t have permissions for %s device!') % curdev
,
526 _('Error opening device'),
527 (_('You don\'t have permissions for %s device!') % curdev
) +
529 (_('Maybe you need to be member of %s group.') % group
)
531 return (0, '', '', '')