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 len(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':
220 # Store date olny if it is more recent
221 # This can happen in multiple call records
225 if i
['Value'] > date
:
227 if i
['Type'] == 'Text_Company':
229 if i
['Type'] == 'Number_General':
230 number_result
= i
['Value']
231 elif i
['Type'][:7] == 'Number_':
237 format
= config
.Read('/Wammu/NameFormat')
239 if format
== 'custom':
240 name_result
= config
.Read('/Wammu/NameFormatString') % {
244 'NickName' : nickname
,
245 'FormalName' : formalname
,
253 if format
== 'auto-first-last':
254 name_result
= '%s %s' % (first
, last
)
256 name_result
= '%s, %s' % (last
, first
)
262 name_result
= nickname
263 elif formalname
!= '':
264 name_result
= formalname
268 if name_result
== '':
270 name_result
= company
273 name_result
= '%s (%s)' % (name_result
, company
)
275 if number_result
== '':
276 number_result
= number
278 entry
['Number'] = number_result
279 entry
['Name'] = name_result
280 entry
['Synced'] = False
285 def ParseTodo(entry
):
289 for i
in entry
['Entries']:
290 if i
['Type'] == 'END_DATETIME':
292 elif i
['Type'] == 'TEXT':
294 elif i
['Type'] == 'COMPLETED':
299 entry
['Completed'] = completed
302 entry
['Synced'] = False
305 def ParseCalendar(entry
):
313 for i
in entry
['Entries']:
314 if i
['Type'] == 'END_DATETIME':
315 end
= str(i
['Value'])
316 elif i
['Type'] == 'START_DATETIME':
317 start
= str(i
['Value'])
318 elif i
['Type'] == 'TONE_ALARM_DATETIME':
319 tone_alarm
= _('enabled (tone)')
320 elif i
['Type'] == 'SILENT_ALARM_DATETIME':
321 silent_alarm
= _('enabled (silent)')
322 elif i
['Type'] == 'TEXT':
324 elif i
['Type'] == 'DESCRIPTION':
325 description
= i
['Value']
326 elif i
['Type'] == 'REPEAT_MONTH':
327 recurrence
= _('yearly')
328 elif i
['Type'] == 'REPEAT_DAY':
329 recurrence
= _('monthly')
330 elif i
['Type'] == 'REPEAT_FREQUENCY':
332 recurrence
= _('daily')
333 elif i
['Value'] == 2:
334 recurrence
= _('biweekly')
335 elif (i
['Type'] == 'REPEAT_DAYOFWEEK'):
337 recurrence
= _('weekly on monday')
338 elif i
['Value'] == 2:
339 recurrence
= _('weekly on tuesday')
340 elif i
['Value'] == 3:
341 recurrence
= _('weekly on wednesday')
342 elif i
['Value'] == 4:
343 recurrence
= _('weekly on thursday')
344 elif i
['Value'] == 5:
345 recurrence
= _('weekly on friday')
346 elif i
['Value'] == 6:
347 recurrence
= _('weekly on saturday')
348 elif i
['Value'] == 7:
349 recurrence
= _('weekly on sunday')
351 if tone_alarm
is not None:
352 entry
['Alarm'] = tone_alarm
353 elif silent_alarm
is not None:
354 entry
['Alarm'] = silent_alarm
356 entry
['Alarm'] = _('disabled')
358 if recurrence
is None:
359 entry
['Recurrence'] = _('nonrecurring')
361 entry
['Recurrence'] = recurrence
364 entry
['Text'] = description
365 elif description
== '':
368 entry
['Text'] = '%s (%s)' % (text
, description
)
370 entry
['Start'] = start
372 entry
['Synced'] = False
375 def ParseMessage(msg
, parseinfo
= False):
378 msg
['Folder'] = msg
['SMS'][0]['Folder']
379 msg
['State'] = msg
['SMS'][0]['State']
380 msg
['Number'] = msg
['SMS'][0]['Number']
381 msg
['Name'] = msg
['SMS'][0]['Name']
382 msg
['DateTime'] = msg
['SMS'][0]['DateTime']
384 for i
in msg
['SMSInfo']['Entries']:
385 if i
['Buffer'] != None:
386 txt
= txt
+ i
['Buffer']
389 txt
= txt
+ i
['Text']
393 loc
= loc
+ str(i
['Location'])
400 if x
in string
.printable
:
403 msg
['Location'] = loc
404 msg
['Synced'] = False
407 def ProcessMessages(list, synced
):
412 data
= gammu
.LinkSMS(list)
416 v
= gammu
.DecodeSMS(x
)
420 ParseMessage(i
, (v
!= None))
422 if i
['State'] == 'Read':
424 elif i
['State'] == 'UnRead':
426 elif i
['State'] == 'Sent':
428 elif i
['State'] == 'UnSent':
431 return {'read':read
, 'unread':unread
, 'sent':sent
, 'unsent':unsent
}
433 def FormatError(txt
, info
):
434 if info
['Code'] == gammu
.Errors
['ERR_NOTSUPPORTED']:
435 message
= _('Your phone doesn\'t support this function.')
436 elif info
['Code'] == gammu
.Errors
['ERR_NOTIMPLEMENTED']:
437 message
= _('This function is not implemented for your phone. If you want help with implementation please contact authors.')
438 elif info
['Code'] == gammu
.Errors
['ERR_SECURITYERROR']:
439 message
= _('Your phone asks for PIN.')
440 elif info
['Code'] == gammu
.Errors
['ERR_FULL']:
441 message
= _('Memory is full, try deleting some entries.')
442 elif info
['Code'] == gammu
.Errors
['ERR_CANCELED']:
443 message
= _('Communication canceled by phone, did you press cancel on phone?')
444 elif info
['Code'] == gammu
.Errors
['ERR_EMPTY']:
445 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.')
446 elif info
['Code'] == gammu
.Errors
['ERR_INSIDEPHONEMENU']:
447 message
= _('Please close opened menu in phone and retry, data can not be accessed while you have opened them.')
448 elif info
['Code'] == gammu
.Errors
['ERR_TIMEOUT']:
449 message
= _('Timeout while trying to communicate with phone. Maybe phone is not connected (for cable) or out of range (for bluetooth or IrDA).')
450 elif info
['Code'] == gammu
.Errors
['ERR_DEVICENOTEXIST']:
451 message
= _('Device for communication with phone does not exist. Maybe you don\'t have phone plugged or your configuration is wrong.')
452 elif info
['Code'] == gammu
.Errors
['ERR_DEVICENOPERMISSION']:
453 message
= _('Can not access device for communication with phone.')
454 if sys
.platform
== 'linux2':
455 message
+= ' ' + _('Maybe you need to be member of some group to have acces to device.')
457 message
= '%s %s\n%s %s\n%s %d' % (_('Description:'), StrConv(info
['Text']), _('Function:'), info
['Where'], _('Error code:'), info
['Code'])
458 return StrConv(txt
+ '\n\n' + message
)
460 def FixupMaskedEdit(edit
):
461 # XXX: this is not clean way of reseting to system colour, but I don't know better.
462 bgc
= wx
.SystemSettings
.GetColour(wx
.SYS_COLOUR_LISTBOX
)
463 fgc
= wx
.SystemSettings
.GetColour(wx
.SYS_COLOUR_WINDOWTEXT
)
464 setattr(edit
, '_validBackgroundColour', bgc
)
465 setattr(edit
, '_foregroundColour', fgc
)
467 def GetWebsiteLang():
468 (loc
, charset
) = locale
.getdefaultlocale()
470 if loc
[:2].lower() == 'cs':
476 def DBUSServiceAvailable(bus
, interface
, try_start_service
=False):
481 if try_start_service
:
483 bus
.start_service_by_name(interface
)
484 except dbus
.exceptions
.DBusException
:
485 print 'Failed to start DBus service %s' % interface
486 obj
= bus
.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
487 dbus_iface
= dbus
.Interface(obj
, 'org.freedesktop.DBus')
488 avail
= dbus_iface
.ListNames()
489 return interface
in avail
492 def CheckDeviceNode(curdev
):
494 Checks whether it makes sense to perform searching on this device and
495 possibly warns user about misconfigurations.
497 Returns tuple of 4 members:
498 - error code (0 = ok, -1 = device does not exits, -2 = no permissions)
503 if sys
.platform
== 'win32':
506 if curdev
[:3] == 'COM':
508 win32file
.QueryDosDevice(curdev
)
509 return (0, '', '', '')
512 _('Device %s does not exist!') % curdev
,
513 _('Error opening device'),
514 _('Device %s does not exist!') % curdev
517 return (0, '', '', '')
518 if not os
.path
.exists(curdev
):
520 _('Device %s does not exist!') % curdev
,
521 _('Error opening device'),
522 _('Device %s does not exist!') % curdev
524 if not os
.access(curdev
, os
.R_OK
) or not os
.access(curdev
, os
.W_OK
):
525 gid
= os
.stat(curdev
).st_gid
527 group
= grp
.getgrgid(gid
)[0]
531 _('You don\'t have permissions for %s device!') % curdev
,
532 _('Error opening device'),
533 (_('You don\'t have permissions for %s device!') % curdev
) +
535 (_('Maybe you need to be member of %s group.') % group
)
537 return (0, '', '', '')