| 1 | ##Â Â Â common/helpers.py |
|---|
| 2 | ## |
|---|
| 3 | ## Copyright (C) 2003-2006 Yann Le Boulanger <asterix@lagaule.org> |
|---|
| 4 | ## Copyright (C) 2005-2006 Nikos Kouremenos <kourem@gmail.com> |
|---|
| 5 | ## Copyright (C) 2005 |
|---|
| 6 | ##Â Â Â Â Â Â Â Â Â Â Dimitur Kirov <dkirov@gmail.com> |
|---|
| 7 | ##Â Â Â Â Â Â Â Â Â Â Travis Shirk <travis@pobox.com> |
|---|
| 8 | ## |
|---|
| 9 | ## This program is free software; you can redistribute it and/or modify |
|---|
| 10 | ## it under the terms of the GNU General Public License as published |
|---|
| 11 | ## by the Free Software Foundation; version 2 only. |
|---|
| 12 | ## |
|---|
| 13 | ## This program is distributed in the hope that it will be useful, |
|---|
| 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 16 | ## GNU General Public License for more details. |
|---|
| 17 | ## |
|---|
| 18 | |
|---|
| 19 | import re |
|---|
| 20 | import locale |
|---|
| 21 | import os |
|---|
| 22 | import subprocess |
|---|
| 23 | import urllib |
|---|
| 24 | import errno |
|---|
| 25 | import select |
|---|
| 26 | import sha |
|---|
| 27 | from encodings.punycode import punycode_encode |
|---|
| 28 | from encodings import idna |
|---|
| 29 | |
|---|
| 30 | import gajim |
|---|
| 31 | from i18n import Q_ |
|---|
| 32 | from i18n import ngettext |
|---|
| 33 | from xmpp_stringprep import nodeprep, resourceprep, nameprep |
|---|
| 34 | |
|---|
| 35 | |
|---|
| 36 | try: |
|---|
| 37 |     import winsound # windows-only built-in module for playing wav |
|---|
| 38 |     import win32api |
|---|
| 39 |     import win32con |
|---|
| 40 | except: |
|---|
| 41 | Â Â Â Â pass |
|---|
| 42 | |
|---|
| 43 | special_groups = (_('Transports'), _('Not in Roster'), _('Observers')) |
|---|
| 44 | |
|---|
| 45 | class InvalidFormat(Exception): |
|---|
| 46 | Â Â Â Â pass |
|---|
| 47 | |
|---|
| 48 | def decompose_jid(jidstring): |
|---|
| 49 | Â Â Â Â user =Â None |
|---|
| 50 | Â Â Â Â server =Â None |
|---|
| 51 | Â Â Â Â resource =Â None |
|---|
| 52 | |
|---|
| 53 | Â Â Â Â # Search for delimiters |
|---|
| 54 | Â Â Â Â user_sep =Â jidstring.find('@') |
|---|
| 55 | Â Â Â Â res_sep =Â jidstring.find('/') |
|---|
| 56 | |
|---|
| 57 |     if user_sep == -1: |
|---|
| 58 |         if res_sep == -1: |
|---|
| 59 | Â Â Â Â Â Â Â Â Â Â Â Â # host |
|---|
| 60 | Â Â Â Â Â Â Â Â Â Â Â Â server =Â jidstring |
|---|
| 61 | Â Â Â Â Â Â Â Â else: |
|---|
| 62 | Â Â Â Â Â Â Â Â Â Â Â Â # host/resource |
|---|
| 63 | Â Â Â Â Â Â Â Â Â Â Â Â server =Â jidstring[0:res_sep] |
|---|
| 64 |             resource = jidstring[res_sep + 1:] or None |
|---|
| 65 | Â Â Â Â else: |
|---|
| 66 |         if res_sep == -1: |
|---|
| 67 | Â Â Â Â Â Â Â Â Â Â Â Â # user@host |
|---|
| 68 |             user = jidstring[0:user_sep] or None |
|---|
| 69 | Â Â Â Â Â Â Â Â Â Â Â Â server =Â jidstring[user_sep +Â 1:] |
|---|
| 70 | Â Â Â Â Â Â Â Â else: |
|---|
| 71 |             if user_sep < res_sep: |
|---|
| 72 | Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â # user@host/resource |
|---|
| 73 |                 user = jidstring[0:user_sep] or None |
|---|
| 74 | Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â server =Â jidstring[user_sep +Â 1:user_sep +Â (res_sep -Â user_sep)] |
|---|
| 75 |                 resource = jidstring[res_sep + 1:] or None |
|---|
| 76 | Â Â Â Â Â Â Â Â Â Â Â Â else: |
|---|
| 77 | Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â # server/resource (with an @ in resource) |
|---|
| 78 | Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â server =Â jidstring[0:res_sep] |
|---|
| 79 |                 resource = jidstring[res_sep + 1:] or None |
|---|
| 80 |     return user, server, resource |
|---|
| 81 | |
|---|
| 82 | def parse_jid(jidstring): |
|---|
| 83 | Â Â Â Â '''Perform stringprep on all JID fragments from a string |
|---|
| 84 | Â Â Â Â and return the full jid''' |
|---|
| 85 | Â Â Â Â # This function comes from http://svn.twistedmatrix.com/cvs/trunk/twisted/words/protocols/jabber/jid.py |
|---|
| 86 | |
|---|
| 87 |     return prep(*decompose_jid(jidstring)) |
|---|
| 88 | |
|---|
| 89 | def idn_to_ascii(host): |
|---|
| 90 | Â Â Â Â '''convert IDN (Internationalized Domain Names) to ACE (ASCII-compatible encoding)''' |
|---|
| 91 | Â Â Â Â labels =Â idna.dots.split(host) |
|---|
| 92 | Â Â Â Â converted_labels =Â [] |
|---|
| 93 |     for label in labels: |
|---|
| 94 | Â Â Â Â Â Â Â Â converted_labels.append(idna.ToASCII(label)) |
|---|
| 95 |     return ".".join(converted_labels) |
|---|
| 96 | |
|---|
| 97 | def parse_resource(resource): |
|---|
| 98 | Â Â Â Â '''Perform stringprep on resource and return it''' |
|---|
| 99 |     if resource: |
|---|
| 100 | Â Â Â Â Â Â Â Â try: |
|---|
| 101 |             return resourceprep.prepare(unicode(resource)) |
|---|
| 102 |         except UnicodeError: |
|---|
| 103 |             raise InvalidFormat, 'Invalid character in resource.' |
|---|
| 104 | |
|---|
| 105 | def prep(user, server, resource): |
|---|
| 106 | Â Â Â Â '''Perform stringprep on all JID fragments and return the full jid''' |
|---|
| 107 | Â Â Â Â # This function comes from |
|---|
| 108 | Â Â Â Â #http://svn.twistedmatrix.com/cvs/trunk/twisted/words/protocols/jabber/jid.py |
|---|
| 109 | |
|---|
| 110 |     if user: |
|---|
| 111 | Â Â Â Â Â Â Â Â try: |
|---|
| 112 | Â Â Â Â Â Â Â Â Â Â Â Â user =Â nodeprep.prepare(unicode(user)) |
|---|
| 113 |         except UnicodeError: |
|---|
| 114 |             raise InvalidFormat, _('Invalid character in username.') |
|---|
| 115 | Â Â Â Â else: |
|---|
| 116 | Â Â Â Â Â Â Â Â user =Â None |
|---|
| 117 | |
|---|
| 118 |     if not server: |
|---|
| 119 |         raise InvalidFormat, _('Server address required.') |
|---|
| 120 | Â Â Â Â else: |
|---|
| 121 | Â Â Â Â Â Â Â Â try: |
|---|
| 122 | Â Â Â Â Â Â Â Â Â Â Â Â server =Â nameprep.prepare(unicode(server)) |
|---|
| 123 |         except UnicodeError: |
|---|
| 124 |             raise InvalidFormat, _('Invalid character in hostname.') |
|---|
| 125 | |
|---|
| 126 |     if resource: |
|---|
| 127 | Â Â Â Â Â Â Â Â try: |
|---|
| 128 | Â Â Â Â Â Â Â Â Â Â Â Â resource =Â resourceprep.prepare(unicode(resource)) |
|---|
| 129 |         except UnicodeError: |
|---|
| 130 |             raise InvalidFormat, _('Invalid character in resource.') |
|---|
| 131 | Â Â Â Â else: |
|---|
| 132 | Â Â Â Â Â Â Â Â resource =Â None |
|---|
| 133 | |
|---|
| 134 |     if user: |
|---|
| 135 |         if resource: |
|---|
| 136 |             return '%s@%s/%s' % (user, server, resource) |
|---|
| 137 | Â Â Â Â Â Â Â Â else: |
|---|
| 138 |             return '%s@%s' % (user, server) |
|---|
| 139 | Â Â Â Â else: |
|---|
| 140 |         if resource: |
|---|
| 141 |             return '%s/%s' % (server, resource) |
|---|
| 142 | Â Â Â Â Â Â Â Â else: |
|---|
| 143 |             return server |
|---|
| 144 | |
|---|
| 145 | def temp_failure_retry(func, *args, **kwargs): |
|---|
| 146 |     while True: |
|---|
| 147 | Â Â Â Â Â Â Â Â try: |
|---|
| 148 |             return func(*args, **kwargs) |
|---|
| 149 |         except (os.error, IOError, select.error), ex: |
|---|
| 150 |             if ex.errno == errno.EINTR: |
|---|
| 151 | Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â continue |
|---|
| 152 | Â Â Â Â Â Â Â Â Â Â Â Â else: |
|---|
| 153 | Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â raise |
|---|
| 154 | |
|---|
| 155 | def convert_bytes(string): |
|---|
| 156 | Â Â Â Â suffix =Â '' |
|---|
| 157 | Â Â Â Â # IEC standard says KiB = 1024 bytes KB = 1000 bytes |
|---|
| 158 | Â Â Â Â # but do we use the standard? |
|---|
| 159 | Â Â Â Â use_kib_mib =Â gajim.config.get('use_kib_mib') |
|---|
| 160 | Â Â Â Â align =Â 1024. |
|---|
| 161 | Â Â Â Â bytes =Â float(string) |
|---|
| 162 |     if bytes >= align: |
|---|
| 163 |         bytes = round(bytes/align, 1) |
|---|
| 164 |         if bytes >= align: |
|---|
| 165 |             bytes = round(bytes/align, 1) |
|---|
| 166 |             if bytes >= align: |
|---|
| 167 |                 bytes = round(bytes/align, 1) |
|---|
| 168 |                 if use_kib_mib: |
|---|
| 169 | Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â #GiB means gibibyte |
|---|
| 170 |                     suffix = _('%s GiB') |
|---|
| 171 | Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â else: |
|---|
| 172 | Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â #GB means gigabyte |
|---|
| 173 |                     suffix = _('%s GB') |
|---|
| 174 | Â Â Â Â Â Â Â Â Â Â Â Â else: |
|---|
| 175 |                 if use_kib_mib: |
|---|
| 176 | Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â #MiB means mibibyte |
|---|
| 177 |                     suffix = _('%s MiB') |
|---|
| 178 | Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â else: |
|---|
| 179 | Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â #MB means megabyte |
|---|
| 180 |                     suffix = _('%s MB') |
|---|
| 181 | Â Â Â Â Â Â Â Â else: |
|---|
| 182 |             if use_kib_mib: |
|---|
| 183 | Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â #KiB means kibibyte |
|---|
| 184 |                     suffix = _('%s KiB') |
|---|
| 185 | Â Â Â Â Â Â Â Â Â Â Â Â else: |
|---|
| 186 | Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â #KB means kilo bytes |
|---|
| 187 |                 suffix = _('%s KB') |
|---|
| 188 | Â Â Â Â else: |
|---|
| 189 | Â Â Â Â Â Â Â Â #B means bytes |
|---|
| 190 |         suffix = _('%s B') |
|---|
| 191 |     return suffix % unicode(bytes) |
|---|
| 192 | |
|---|
| 193 | |
|---|
| 194 | def get_contact_dict_for_account(account): |
|---|
| 195 | Â Â Â Â ''' create a dict of jid, nick -> contact with all contacts of account. |
|---|
| 196 | Â Â Â Â Can be used for completion lists''' |
|---|
| 197 | Â Â Â Â contacts_dict =Â {} |
|---|
| 198 |     for jid in gajim.contacts.get_jid_list(account): |
|---|
| 199 | Â Â Â Â Â Â Â Â contact =Â gajim.contacts.get_contact_with_highest_priority(account, |
|---|
| 200 | Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â jid) |
|---|
| 201 | Â Â Â Â Â Â Â Â contacts_dict[jid]Â =Â contact |
|---|
| 202 | Â Â Â Â Â Â Â Â name =Â contact.name |
|---|
| 203 |         if contacts_dict.has_key(name): |
|---|
| 204 | Â Â Â Â Â Â Â Â Â Â Â Â contact1 =Â contacts_dict[name] |
|---|
| 205 |             del contacts_dict[name] |
|---|
| 206 |             contacts_dict['%s (%s)' % (name, contact1.jid)] = contact1 |
|---|
| 207 |             contacts_dict['%s (%s)' % (name, jid)] = contact |
|---|
| 208 | Â Â Â Â Â Â Â Â else: |
|---|
| 209 |             if contact.name == gajim.get_nick_from_jid(jid): |
|---|
| 210 |                 del contacts_dict[jid] |
|---|
| 211 | Â Â Â Â Â Â Â Â Â Â Â Â contacts_dict[name]Â =Â contact |
|---|
| 212 |     return contacts_dict |
|---|
| 213 | |
|---|
| 214 | def get_uf_show(show, use_mnemonic = False): |
|---|
| 215 | Â Â Â Â '''returns a userfriendly string for dnd/xa/chat |
|---|
| 216 | Â Â Â Â and makes all strings translatable |
|---|
| 217 | Â Â Â Â if use_mnemonic is True, it adds _ so GUI should call with True |
|---|
| 218 | Â Â Â Â for accessibility issues''' |
|---|
| 219 |     if show == 'dnd': |
|---|
| 220 |         if use_mnemonic: |
|---|
| 221 | Â Â Â Â Â Â Â Â Â Â Â Â uf_show =Â _('_Busy') |
|---|
| 222 | Â Â Â Â Â Â Â Â else: |
|---|
| 223 | Â Â Â Â Â Â Â Â Â Â Â Â uf_show =Â _('Busy') |
|---|
| 224 |     elif show == 'xa': |
|---|
| 225 |         if use_mnemonic: |
|---|
| 226 | Â Â Â Â Â Â Â Â Â Â Â Â uf_show =Â _('_Not Available') |
|---|
| 227 | Â Â Â Â Â Â Â Â else: |
|---|
| 228 | Â Â Â Â Â Â Â Â Â Â Â Â uf_show =Â _('Not Available') |
|---|
| 229 |     elif show == 'chat': |
|---|
| 230 |         if use_mnemonic: |
|---|
| 231 | Â Â Â Â Â Â Â Â Â Â Â Â uf_show =Â _('_Free for Chat') |
|---|
| 232 | Â Â Â Â Â Â Â Â else: |
|---|
| 233 | Â Â Â Â Â Â Â Â Â Â Â Â uf_show =Â _('Free for Chat') |
|---|
| 234 |     elif show == 'online': |
|---|
| 235 |         if use_mnemonic: |
|---|
| 236 | Â Â Â Â Â Â Â Â Â Â Â Â uf_show =Â _('_Available') |
|---|
| 237 | Â Â Â Â Â Â Â Â else: |
|---|
| 238 | Â Â Â Â Â Â Â Â Â Â Â Â uf_show =Â _('Available') |
|---|
| 239 |     elif show == 'connecting': |
|---|
| 240 | Â Â Â Â Â Â Â Â Â Â Â Â uf_show =Â _('Connecting') |
|---|
| 241 |     elif show == 'away': |
|---|
| 242 |         if use_mnemonic: |
|---|
| 243 | Â Â Â Â Â Â Â Â Â Â Â Â uf_show =Â _('A_way') |
|---|
| 244 | Â Â Â Â Â Â Â Â else: |
|---|
| 245 | Â Â Â Â Â Â Â Â Â Â Â Â uf_show =Â _('Away') |
|---|
| 246 |     elif show == 'offline': |
|---|
| 247 |         if use_mnemonic: |
|---|
| 248 | Â Â Â Â Â Â Â Â Â Â Â Â uf_show =Â _('_Offline') |
|---|
| 249 | Â Â Â Â Â Â Â Â else: |
|---|
| 250 | Â Â Â Â Â Â Â Â Â Â Â Â uf_show =Â _('Offline') |
|---|
| 251 |     elif show == 'invisible': |
|---|
| 252 |         if use_mnemonic: |
|---|
| 253 | Â Â Â Â Â Â Â Â Â Â Â Â uf_show =Â _('_Invisible') |
|---|
| 254 | Â Â Â Â Â Â Â Â else: |
|---|
| 255 | Â Â Â Â Â Â Â Â Â Â Â Â uf_show =Â _('Invisible') |
|---|
| 256 |     elif show == 'not in roster': |
|---|
| 257 | Â Â Â Â Â Â Â Â uf_show =Â _('Not in Roster') |
|---|
| 258 |     elif show == 'requested': |
|---|
| 259 | Â Â Â Â Â Â Â Â uf_show =Â Q_('?contact has status:Unknown') |
|---|
| 260 | Â Â Â Â else: |
|---|
| 261 | Â Â Â Â Â Â Â Â uf_show =Â Q_('?contact has status:Has errors') |
|---|
| 262 |     return unicode(uf_show) |
|---|
| 263 | |
|---|
| 264 | def get_uf_sub(sub): |
|---|
| 265 |     if sub == 'none': |
|---|
| 266 | Â Â Â Â Â Â Â Â uf_sub =Â Q_('?Subscription we already have:None') |
|---|
| 267 |     elif sub == 'to': |
|---|
| 268 | Â Â Â Â Â Â Â Â uf_sub =Â _('To') |
|---|
| 269 |     elif sub == 'from': |
|---|
| 270 | Â Â Â Â Â Â Â Â uf_sub =Â _('From') |
|---|
| 271 |     elif sub == 'both': |
|---|
| 272 | Â Â Â Â Â Â Â Â uf_sub =Â _('Both') |
|---|
| 273 | Â Â Â Â else: |
|---|
| 274 | Â Â Â Â Â Â Â Â uf_sub =Â sub |
|---|
| 275 | |
|---|
| 276 |     return unicode(uf_sub) |
|---|
| 277 | |
|---|
| 278 | def get_uf_ask(ask): |
|---|
| 279 |     if ask is None: |
|---|
| 280 | Â Â Â Â Â Â Â Â uf_ask =Â Q_('?Ask (for Subscription):None') |
|---|
| 281 |     elif ask == 'subscribe': |
|---|
| 282 | Â Â Â Â Â Â Â Â uf_ask =Â _('Subscribe') |
|---|
| 283 | Â Â Â Â else: |
|---|
| 284 | Â Â Â Â Â Â Â Â uf_ask =Â ask |
|---|
| 285 | |
|---|
| 286 |     return unicode(uf_ask) |
|---|
| 287 | |
|---|
| 288 | def get_uf_role(role, plural = False): |
|---|
| 289 | Â Â Â Â ''' plural determines if you get Moderators or Moderator''' |
|---|
| 290 |     if role == 'none': |
|---|
| 291 | Â Â Â Â Â Â Â Â role_name =Â Q_('?Group Chat Contact Role:None') |
|---|
| 292 |     elif role == 'moderator': |
|---|
| 293 |         if plural: |
|---|
| 294 | Â Â Â Â Â Â Â Â Â Â Â Â role_name =Â _('Moderators') |
|---|
| 295 | Â Â Â Â Â Â Â Â else: |
|---|
| 296 | Â Â Â Â Â Â Â Â Â Â Â Â role_name =Â _('Moderator') |
|---|
| 297 |     elif role == 'participant': |
|---|
| 298 |         if plural: |
|---|
| 299 | Â Â Â Â Â Â Â Â Â Â Â Â role_name =Â _('Participants') |
|---|
| 300 | Â Â Â Â Â Â Â Â else: |
|---|
| 301 | Â Â Â Â Â Â Â Â Â Â Â Â role_name =Â _('Participant') |
|---|
| 302 |     elif role == 'visitor': |
|---|
| 303 |         if plural: |
|---|
| 304 | Â Â Â Â Â Â Â Â Â Â Â Â role_name =Â _('Visitors') |
|---|
| 305 | Â Â Â Â Â Â Â Â else: |
|---|
| 306 | Â Â Â Â Â Â Â Â Â Â Â Â role_name =Â _('Visitor') |
|---|
| 307 |     return role_name |
|---|
| 308 | Â Â Â Â |
|---|
| 309 | def get_uf_affiliation(affiliation): |
|---|
| 310 | Â Â Â Â '''Get a nice and translated affilition for muc''' |
|---|
| 311 |     if affiliation == 'none': |
|---|
| 312 | Â Â Â Â Â Â Â Â affiliation_name =Â Q_('?Group Chat Contact Affiliation:None') |
|---|
| 313 |     elif affiliation == 'owner': |
|---|
| 314 | Â Â Â Â Â Â Â Â affiliation_name =Â _('Owner') |
|---|
| 315 |     elif affiliation == 'admin': |
|---|
| 316 | Â Â Â Â Â Â Â Â affiliation_name =Â _('Administrator') |
|---|
| 317 |     elif affiliation == 'member': |
|---|
| 318 | Â Â Â Â Â Â Â Â affiliation_name =Â _('Member') |
|---|
| 319 | Â Â Â Â else:Â # Argl ! An unknown affiliation ! |
|---|
| 320 | Â Â Â Â Â Â Â Â affiliation_name =Â affiliation.capitalize() |
|---|
| 321 |     return affiliation_name |
|---|
| 322 | |
|---|
| 323 | |
|---|
| 324 | def get_sorted_keys(adict): |
|---|
| 325 | Â Â Â Â keys =Â adict.keys() |
|---|
| 326 | Â Â Â Â keys.sort() |
|---|
| 327 |     return keys |
|---|
| 328 | |
|---|
| 329 | def to_one_line(msg): |
|---|
| 330 |     msg = msg.replace('\\', '\\\\') |
|---|
| 331 |     msg = msg.replace('\n', '\\n') |
|---|
| 332 | Â Â Â Â # s1 = 'test\ntest\\ntest' |
|---|
| 333 | Â Â Â Â # s11 = s1.replace('\\', '\\\\') |
|---|
| 334 | Â Â Â Â # s12 = s11.replace('\n', '\\n') |
|---|
| 335 | Â Â Â Â # s12 |
|---|
| 336 | Â Â Â Â # 'test\\ntest\\\\ntest' |
|---|
| 337 |     return msg |
|---|
| 338 | |
|---|
| 339 | def from_one_line(msg): |
|---|
| 340 | Â Â Â Â # (?<!\\) is a lookbehind assertion which asks anything but '\' |
|---|
| 341 | Â Â Â Â # to match the regexp that follows it |
|---|
| 342 | |
|---|
| 343 | Â Â Â Â # So here match '\\n' but not if you have a '\' before that |
|---|
| 344 | Â Â Â Â expr =Â re.compile(r'(?<!\\)\\n') |
|---|
| 345 |     msg = expr.sub('\n', msg) |
|---|
| 346 |     msg = msg.replace('\\\\', '\\') |
|---|
| 347 | Â Â Â Â # s12 = 'test\\ntest\\\\ntest' |
|---|
| 348 | Â Â Â Â # s13 = re.sub('\n', s12) |
|---|
| 349 | Â Â Â Â # s14 s13.replace('\\\\', '\\') |
|---|
| 350 | Â Â Â Â # s14 |
|---|
| 351 | Â Â Â Â # 'test\ntest\\ntest' |
|---|
| 352 |     return msg |
|---|
| 353 | |
|---|
| 354 | def get_uf_chatstate(chatstate): |
|---|
| 355 | Â Â Â Â '''removes chatstate jargon and returns user friendly messages''' |
|---|
| 356 |     if chatstate == 'active': |
|---|
| 357 |         return _('is paying attention to the conversation') |
|---|
| 358 |     elif chatstate == 'inactive': |
|---|
| 359 |         return _('is doing something else') |
|---|
| 360 |     elif chatstate == 'composing': |
|---|
| 361 |         return _('is composing a message...') |
|---|
| 362 |     elif chatstate == 'paused': |
|---|
| 363 | Â Â Â Â Â Â Â Â #paused means he or she was composing but has stopped for a while |
|---|
| 364 |         return _('paused composing a message') |
|---|
| 365 |     elif chatstate == 'gone': |
|---|
| 366 |         return _('has closed the chat window or tab') |
|---|
| 367 |     return '' |
|---|
| 368 | |
|---|
| 369 | def is_in_path(name_of_command, return_abs_path = False): |
|---|
| 370 | Â Â Â Â # if return_abs_path is True absolute path will be returned |
|---|
| 371 | Â Â Â Â # for name_of_command |
|---|
| 372 | Â Â Â Â # on failures False is returned |
|---|
| 373 | Â Â Â Â is_in_dir =Â False |
|---|
| 374 | Â Â Â Â found_in_which_dir =Â None |
|---|
| 375 | Â Â Â Â path =Â os.getenv('PATH').split(':') |
|---|
| 376 |     for path_to_directory in path: |
|---|
| 377 | Â Â Â Â Â Â Â Â try: |
|---|
| 378 | Â Â Â Â Â Â Â Â Â Â Â Â contents =Â os.listdir(path_to_directory) |
|---|
| 379 |         except OSError: # user can have something in PATH that is not a dir |
|---|
| 380 | Â Â Â Â Â Â Â Â Â Â Â Â pass |
|---|
| 381 | Â Â Â Â Â Â Â Â else: |
|---|
| 382 |             is_in_dir = name_of_command in contents |
|---|
| 383 |         if is_in_dir: |
|---|
| 384 |             if return_abs_path: |
|---|
| 385 | Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â found_in_which_dir =Â path_to_directory |
|---|
| 386 | Â Â Â Â Â Â Â Â Â Â Â Â break |
|---|
| 387 | |
|---|
| 388 |     if found_in_which_dir: |
|---|
| 389 |         abs_path = os.path.join(path_to_directory, name_of_command) |
|---|
| 390 |         return abs_path |
|---|
| 391 | Â Â Â Â else: |
|---|
| 392 |         return is_in_dir |
|---|
| 393 | |
|---|
| 394 | def exec_command(command): |
|---|
| 395 |     subprocess.Popen(command, shell = True) |
|---|
| 396 | |
|---|
| 397 | def build_command(executable, parameter): |
|---|
| 398 | Â Â Â Â # we add to the parameter (can hold path with spaces) |
|---|
| 399 | Â Â Â Â # "" so we have good parsing from shell |
|---|
| 400 |     parameter = parameter.replace('"', '\\"') # but first escape " |
|---|
| 401 |     command = '%s "%s"' % (executable, parameter) |
|---|
| 402 |     return command |
|---|
| 403 | |
|---|
| 404 | def launch_browser_mailer(kind, uri): |
|---|
| 405 | Â Â Â Â #kind = 'url' or 'mail' |
|---|
| 406 |     if os.name == 'nt': |
|---|
| 407 | Â Â Â Â Â Â Â Â try: |
|---|
| 408 | Â Â Â Â Â Â Â Â Â Â Â Â os.startfile(uri)Â # if pywin32 is installed we open |
|---|
| 409 | Â Â Â Â Â Â Â Â except: |
|---|
| 410 | Â Â Â Â Â Â Â Â Â Â Â Â pass |
|---|
| 411 | |
|---|
| 412 | Â Â Â Â else: |
|---|
| 413 |         if kind == 'mail' and not uri.startswith('mailto:'): |
|---|
| 414 | Â Â Â Â Â Â Â Â Â Â Â Â uri =Â 'mailto:'Â +Â uri |
|---|
| 415 | |
|---|
| 416 |         if gajim.config.get('openwith') == 'gnome-open': |
|---|
| 417 | Â Â Â Â Â Â Â Â Â Â Â Â command =Â 'gnome-open' |
|---|
| 418 |         elif gajim.config.get('openwith') == 'kfmclient exec': |
|---|
| 419 | Â Â Â Â Â Â Â Â Â Â Â Â command =Â 'kfmclient exec' |
|---|
| 420 |         elif gajim.config.get('openwith') == 'exo-open': |
|---|
| 421 | Â Â Â Â Â Â Â Â Â Â Â Â command =Â 'exo-open' |
|---|
| 422 |         elif gajim.config.get('openwith') == 'custom': |
|---|
| 423 |             if kind == 'url': |
|---|
| 424 | Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â command =Â gajim.config.get('custombrowser') |
|---|
| 425 |             if kind == 'mail': |
|---|
| 426 | Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â command =Â gajim.config.get('custommailapp') |
|---|
| 427 |             if command == '': # if no app is configured |
|---|
| 428 | Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â return |
|---|
| 429 | |
|---|
| 430 |         command = build_command(command, uri) |
|---|
| 431 | Â Â Â Â Â Â Â Â try: |
|---|
| 432 | Â Â Â Â Â Â Â Â Â Â Â Â exec_command(command) |
|---|
| 433 | Â Â Â Â Â Â Â Â except: |
|---|
| 434 | Â Â Â Â Â Â Â Â Â Â Â Â pass |
|---|
| 435 | |
|---|
| 436 | def launch_file_manager(path_to_open): |
|---|
| 437 |     if os.name == 'nt': |
|---|
| 438 | Â Â Â Â Â Â Â Â try: |
|---|
| 439 | Â Â Â Â Â Â Â Â Â Â Â Â os.startfile(path_to_open)Â # if pywin32 is installed we open |
|---|
| 440 | Â Â Â Â Â Â Â Â except: |
|---|
| 441 | Â Â Â Â Â Â Â Â Â Â Â Â pass |
|---|
| 442 | Â Â Â Â else: |
|---|
| 443 |         if gajim.config.get('openwith') == 'gnome-open': |
|---|
| 444 | Â Â Â Â Â Â Â Â Â Â Â Â command =Â 'gnome-open' |
|---|
| 445 |         elif gajim.config.get('openwith') == 'kfmclient exec': |
|---|
| 446 | Â Â Â Â Â Â Â Â Â Â Â Â command =Â 'kfmclient exec' |
|---|
| 447 |         elif gajim.config.get('openwith') == 'exo-open': |
|---|
| 448 | Â Â Â Â Â Â Â Â Â Â Â Â command =Â 'exo-open' |
|---|
| 449 |         elif gajim.config.get('openwith') == 'custom': |
|---|
| 450 | Â Â Â Â Â Â Â Â Â Â Â Â command =Â gajim.config.get('custom_file_manager') |
|---|
| 451 |         if command == '': # if no app is configured |
|---|
| 452 | Â Â Â Â Â Â Â Â Â Â Â Â return |
|---|
| 453 |         command = build_command(command, path_to_open) |
|---|
| 454 | Â Â Â Â Â Â Â Â try: |
|---|
| 455 | Â Â Â Â Â Â Â Â Â Â Â Â exec_command(command) |
|---|
| 456 | Â Â Â Â Â Â Â Â except: |
|---|
| 457 | Â Â Â Â Â Â Â Â Â Â Â Â pass |
|---|
| 458 | |
|---|
| 459 | def play_sound(event): |
|---|
| 460 |     if not gajim.config.get('sounds_on'): |
|---|
| 461 | Â Â Â Â Â Â Â Â return |
|---|
| 462 |     path_to_soundfile = gajim.config.get_per('soundevents', event, 'path') |
|---|
| 463 | Â Â Â Â play_sound_file(path_to_soundfile) |
|---|
| 464 | |
|---|
| 465 | def play_sound_file(path_to_soundfile): |
|---|
| 466 |     if path_to_soundfile == 'beep': |
|---|
| 467 | Â Â Â Â Â Â Â Â exec_command('beep') |
|---|
| 468 | Â Â Â Â Â Â Â Â return |
|---|
| 469 |     if path_to_soundfile is None or not os.path.exists(path_to_soundfile): |
|---|
| 470 | Â Â Â Â Â Â Â Â return |
|---|
| 471 |     if os.name == 'nt': |
|---|
| 472 | Â Â Â Â Â Â Â Â try: |
|---|
| 473 | Â Â Â Â Â Â Â Â Â Â Â Â winsound.PlaySound(path_to_soundfile, |
|---|
| 474 | Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â winsound.SND_FILENAME|winsound.SND_ASYNC) |
|---|
| 475 | Â Â Â Â Â Â Â Â except: |
|---|
| 476 | Â Â Â Â Â Â Â Â Â Â Â Â pass |
|---|
| 477 |     elif os.name == 'posix': |
|---|
| 478 |         if gajim.config.get('soundplayer') == '': |
|---|
| 479 | Â Â Â Â Â Â Â Â Â Â Â Â return |
|---|
| 480 | Â Â Â Â Â Â Â Â player =Â gajim.config.get('soundplayer') |
|---|
| 481 |         command = build_command(player, path_to_soundfile) |
|---|
| 482 | Â Â Â Â Â Â Â Â exec_command(command) |
|---|
| 483 | |
|---|
| 484 | def get_file_path_from_dnd_dropped_uri(uri): |
|---|
| 485 | Â Â Â Â path =Â urllib.url2pathname(uri)Â # escape special chars |
|---|
| 486 | Â Â Â Â path =Â path.strip('\r\n\x00')Â # remove \r\n and NULL |
|---|
| 487 | Â Â Â Â # get the path to file |
|---|
| 488 |     if path.startswith('file:\\\\\\'): # windows |
|---|
| 489 | Â Â Â Â Â Â Â Â path =Â path[8:]Â # 8 is len('file:///') |
|---|
| 490 |     elif path.startswith('file://'): # nautilus, rox |
|---|
| 491 | Â Â Â Â Â Â Â Â path =Â path[7:]Â # 7 is len('file://') |
|---|
| 492 |     elif path.startswith('file:'): # xffm |
|---|
| 493 | Â Â Â Â Â Â Â Â path =Â path[5:]Â # 5 is len('file:') |
|---|
| 494 |     return path |
|---|
| 495 | |
|---|
| 496 | def from_xs_boolean_to_python_boolean(value): |
|---|
| 497 | Â Â Â Â # this is xs:boolean so 'true', 'false', '1', '0' |
|---|
| 498 | Â Â Â Â # convert those to True/False (python booleans) |
|---|
| 499 |     if value in ('1', 'true'): |
|---|
| 500 | Â Â Â Â Â Â Â Â val =Â True |
|---|
| 501 | Â Â Â Â else:Â # '0', 'false' or anything else |
|---|
| 502 | Â Â Â Â Â Â Â Â val =Â False |
|---|
| 503 | |
|---|
| 504 |     return val |
|---|
| 505 | |
|---|
| 506 | def get_xmpp_show(show): |
|---|
| 507 |     if show in ('online', 'offline'): |
|---|
| 508 |         return None |
|---|
| 509 |     return show |
|---|
| 510 | |
|---|
| 511 | def get_output_of_command(command): |
|---|
| 512 | Â Â Â Â try: |
|---|
| 513 |         child_stdin, child_stdout = os.popen2(command) |
|---|
| 514 |     except ValueError: |
|---|
| 515 |         return None |
|---|
| 516 | |
|---|
| 517 | Â Â Â Â output =Â child_stdout.readlines() |
|---|
| 518 | Â Â Â Â child_stdout.close() |
|---|
| 519 | Â Â Â Â child_stdin.close() |
|---|
| 520 | |
|---|
| 521 |     return output |
|---|
| 522 | |
|---|
| 523 | def get_global_show(): |
|---|
| 524 | Â Â Â Â maxi =Â 0 |
|---|
| 525 |     for account in gajim.connections: |
|---|
| 526 |         if not gajim.config.get_per('accounts', account, |
|---|
| 527 | Â Â Â Â Â Â Â Â Â Â Â Â 'sync_with_global_status'): |
|---|
| 528 | Â Â Â Â Â Â Â Â Â Â Â Â continue |
|---|
| 529 | Â Â Â Â Â Â Â Â connected =Â gajim.connections[account].connected |
|---|
| 530 |         if connected > maxi: |
|---|
| 531 | Â Â Â Â Â Â Â Â Â Â Â Â maxi =Â connected |
|---|
| 532 |     return gajim.SHOW_LIST[maxi] |
|---|
| 533 | |
|---|
| 534 | def get_global_status(): |
|---|
| 535 | Â Â Â Â maxi =Â 0 |
|---|
| 536 |     for account in gajim.connections: |
|---|
| 537 |         if not gajim.config.get_per('accounts', account, |
|---|
| 538 | Â Â Â Â Â Â Â Â Â Â Â Â 'sync_with_global_status'): |
|---|
| 539 | Â Â Â Â Â Â Â Â Â Â Â Â continue |
|---|
| 540 | Â Â Â Â Â Â Â Â connected =Â gajim.connections[account].connected |
|---|
| 541 |         if connected > maxi: |
|---|
| 542 | Â Â Â Â Â Â Â Â Â Â Â Â maxi =Â connected |
|---|
| 543 | Â Â Â Â Â Â Â Â Â Â Â Â status =Â gajim.connections[account].status |
|---|
| 544 |     return status |
|---|
| 545 | |
|---|
| 546 | def get_icon_name_to_show(contact, account = None): |
|---|
| 547 | Â Â Â Â '''Get the icon name to show in online, away, requested, ...''' |
|---|
| 548 |     if account and gajim.events.get_nb_roster_events(account, contact.jid): |
|---|
| 549 |         return 'message' |
|---|
| 550 |     if account and gajim.events.get_nb_roster_events(account, |
|---|
| 551 | Â Â Â Â contact.get_full_jid()): |
|---|
| 552 |         return 'message' |
|---|
| 553 |     if contact.jid.find('@') <= 0: # if not '@' or '@' starts the jid ==> agent |
|---|
| 554 |         return contact.show |
|---|
| 555 |     if contact.sub in ('both', 'to'): |
|---|
| 556 |         return contact.show |
|---|
| 557 |     if contact.ask == 'subscribe': |
|---|
| 558 |         return 'requested' |
|---|
| 559 | Â Â Â Â transport =Â gajim.get_transport_name_from_jid(contact.jid) |
|---|
| 560 |     if transport: |
|---|
| 561 |         return contact.show |
|---|
| 562 |     return 'not in roster' |
|---|
| 563 | |
|---|
| 564 | def decode_string(string): |
|---|
| 565 | Â Â Â Â '''try to decode (to make it Unicode instance) given string''' |
|---|
| 566 |     if isinstance(string, unicode): |
|---|
| 567 |         return string |
|---|
| 568 | Â Â Â Â # by the time we go to iso15 it better be the one else we show bad characters |
|---|
| 569 |     encodings = (locale.getpreferredencoding(), 'utf-8', 'iso-8859-15') |
|---|
| 570 |     for encoding in encodings: |
|---|
| 571 | Â Â Â Â Â Â Â Â try: |
|---|
| 572 | Â Â Â Â Â Â Â Â Â Â Â Â string =Â string.decode(encoding) |
|---|
| 573 |         except UnicodeError: |
|---|
| 574 | Â Â Â Â Â Â Â Â Â Â Â Â continue |
|---|
| 575 | Â Â Â Â Â Â Â Â break |
|---|
| 576 | |
|---|
| 577 |     return string |
|---|
| 578 | |
|---|
| 579 | def ensure_utf8_string(string): |
|---|
| 580 | Â Â Â Â '''make sure string is in UTF-8''' |
|---|
| 581 | Â Â Â Â try: |
|---|
| 582 | Â Â Â Â Â Â Â Â string =Â decode_string(string).encode('utf-8') |
|---|
| 583 | Â Â Â Â except: |
|---|
| 584 | Â Â Â Â Â Â Â Â pass |
|---|
| 585 |     return string |
|---|
| 586 | |
|---|
| 587 | def get_windows_reg_env(varname, default=''): |
|---|
| 588 | Â Â Â Â '''asks for paths commonly used but not exposed as ENVs |
|---|
| 589 | Â Â Â Â in english Windows 2003 those are: |
|---|
| 590 | Â Â Â Â 'AppData' = %USERPROFILE%\Application Data (also an ENV) |
|---|
| 591 | Â Â Â Â 'Desktop' = %USERPROFILE%\Desktop |
|---|
| 592 | Â Â Â Â 'Favorites' = %USERPROFILE%\Favorites |
|---|
| 593 | Â Â Â Â 'NetHood' = %USERPROFILE%\NetHood |
|---|
| 594 | Â Â Â Â 'Personal' = D:\My Documents (PATH TO MY DOCUMENTS) |
|---|
| 595 | Â Â Â Â 'PrintHood' = %USERPROFILE%\PrintHood |
|---|
| 596 | Â Â Â Â 'Programs' = %USERPROFILE%\Start Menu\Programs |
|---|
| 597 | Â Â Â Â 'Recent' = %USERPROFILE%\Recent |
|---|
| 598 | Â Â Â Â 'SendTo' = %USERPROFILE%\SendTo |
|---|
| 599 | Â Â Â Â 'Start Menu' = %USERPROFILE%\Start Menu |
|---|
| 600 | Â Â Â Â 'Startup' = %USERPROFILE%\Start Menu\Programs\Startup |
|---|
| 601 | Â Â Â Â 'Templates' = %USERPROFILE%\Templates |
|---|
| 602 | Â Â Â Â 'My Pictures' = D:\My Documents\My Pictures |
|---|
| 603 | Â Â Â Â 'Local Settings' = %USERPROFILE%\Local Settings |
|---|
| 604 | Â Â Â Â 'Local AppData' = %USERPROFILE%\Local Settings\Application Data |
|---|
| 605 | Â Â Â Â 'Cache' = %USERPROFILE%\Local Settings\Temporary Internet Files |
|---|
| 606 | Â Â Â Â 'Cookies' = %USERPROFILE%\Cookies |
|---|
| 607 | Â Â Â Â 'History' = %USERPROFILE%\Local Settings\History |
|---|
| 608 | Â Â Â Â ''' |
|---|
| 609 | |
|---|
| 610 |     if os.name != 'nt': |
|---|
| 611 |         return '' |
|---|
| 612 | |
|---|
| 613 | Â Â Â Â val =Â default |
|---|
| 614 | Â Â Â Â try: |
|---|
| 615 | Â Â Â Â Â Â Â Â rkey =Â win32api.RegOpenKey(win32con.HKEY_CURRENT_USER, |
|---|
| 616 | r'Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders') |
|---|
| 617 | Â Â Â Â Â Â Â Â try: |
|---|
| 618 |             val = str(win32api.RegQueryValueEx(rkey, varname)[0]) |
|---|
| 619 | Â Â Â Â Â Â Â Â Â Â Â Â val =Â win32api.ExpandEnvironmentStrings(val)Â # expand using environ |
|---|
| 620 | Â Â Â Â Â Â Â Â except: |
|---|
| 621 | Â Â Â Â Â Â Â Â Â Â Â Â pass |
|---|
| 622 | Â Â Â Â finally: |
|---|
| 623 | Â Â Â Â Â Â Â Â win32api.RegCloseKey(rkey) |
|---|
| 624 |     return val |
|---|
| 625 | |
|---|
| 626 | def get_my_pictures_path(): |
|---|
| 627 | Â Â Â Â '''windows-only atm. [Unix lives in the past]''' |
|---|
| 628 |     return get_windows_reg_env('My Pictures') |
|---|
| 629 | |
|---|
| 630 | def get_desktop_path(): |
|---|
| 631 |     if os.name == 'nt': |
|---|
| 632 | Â Â Â Â Â Â Â Â path =Â get_windows_reg_env('Desktop') |
|---|
| 633 | Â Â Â Â else: |
|---|
| 634 |         path = os.path.join(os.path.expanduser('~'), 'Desktop') |
|---|
| 635 |     return path |
|---|
| 636 | |
|---|
| 637 | def get_documents_path(): |
|---|
| 638 |     if os.name == 'nt': |
|---|
| 639 | Â Â Â Â Â Â Â Â path =Â get_windows_reg_env('Personal') |
|---|
| 640 | Â Â Â Â else: |
|---|
| 641 | Â Â Â Â Â Â Â Â path =Â os.path.expanduser('~') |
|---|
| 642 |     return path |
|---|
| 643 | |
|---|
| 644 | def get_full_jid_from_iq(iq_obj): |
|---|
| 645 | Â Â Â Â '''return the full jid (with resource) from an iq as unicode''' |
|---|
| 646 |     return parse_jid(str(iq_obj.getFrom())) |
|---|
| 647 | |
|---|
| 648 | def get_jid_from_iq(iq_obj): |
|---|
| 649 | Â Â Â Â '''return the jid (without resource) from an iq as unicode''' |
|---|
| 650 | Â Â Â Â jid =Â get_full_jid_from_iq(iq_obj) |
|---|
| 651 |     return gajim.get_jid_without_resource(jid) |
|---|
| 652 | |
|---|
| 653 | def get_auth_sha(sid, initiator, target): |
|---|
| 654 | Â Â Â Â ''' return sha of sid + initiator + target used for proxy auth''' |
|---|
| 655 |     return sha.new("%s%s%s" % (sid, initiator, target)).hexdigest() |
|---|
| 656 | |
|---|
| 657 | |
|---|
| 658 | distro_info =Â { |
|---|
| 659 | Â Â Â Â 'Arch Linux':Â '/etc/arch-release', |
|---|
| 660 | Â Â Â Â 'Aurox Linux':Â '/etc/aurox-release', |
|---|
| 661 | Â Â Â Â 'Conectiva Linux':Â '/etc/conectiva-release', |
|---|
| 662 | Â Â Â Â 'CRUX':Â '/usr/bin/crux', |
|---|
| 663 | Â Â Â Â 'Debian GNU/Linux':Â '/etc/debian_release', |
|---|
| 664 | Â Â Â Â 'Debian GNU/Linux':Â '/etc/debian_version', |
|---|
| 665 | Â Â Â Â 'Fedora Linux':Â '/etc/fedora-release', |
|---|
| 666 | Â Â Â Â 'Gentoo Linux':Â '/etc/gentoo-release', |
|---|
| 667 | Â Â Â Â 'Linux from Scratch':Â '/etc/lfs-release', |
|---|
| 668 | Â Â Â Â 'Mandrake Linux':Â '/etc/mandrake-release', |
|---|
| 669 | Â Â Â Â 'Slackware Linux':Â '/etc/slackware-release', |
|---|
| 670 | Â Â Â Â 'Slackware Linux':Â '/etc/slackware-version', |
|---|
| 671 | Â Â Â Â 'Solaris/Sparc':Â '/etc/release', |
|---|
| 672 | Â Â Â Â 'Source Mage':Â '/etc/sourcemage_version', |
|---|
| 673 | Â Â Â Â 'SUSE Linux':Â '/etc/SuSE-release', |
|---|
| 674 | Â Â Â Â 'Sun JDS':Â '/etc/sun-release', |
|---|
| 675 | Â Â Â Â 'PLD Linux':Â '/etc/pld-release', |
|---|
| 676 | Â Â Â Â 'Yellow Dog Linux':Â '/etc/yellowdog-release', |
|---|
| 677 | Â Â Â Â # many distros use the /etc/redhat-release for compatibility |
|---|
| 678 | Â Â Â Â # so Redhat is the last |
|---|
| 679 | Â Â Â Â 'Redhat Linux':Â '/etc/redhat-release' |
|---|
| 680 | } |
|---|
| 681 | |
|---|
| 682 | def get_random_string_16(): |
|---|
| 683 | Â Â Â Â ''' create random string of length 16''' |
|---|
| 684 |     rng = range(65, 90) |
|---|
| 685 |     rng.extend(range(48, 57)) |
|---|
| 686 |     char_sequence = map(lambda e:chr(e), rng) |
|---|
| 687 |     from random import sample |
|---|
| 688 |     return reduce(lambda e1, e2: e1 + e2, |
|---|
| 689 |             sample(char_sequence, 16)) |
|---|
| 690 | Â Â Â Â |
|---|
| 691 | def get_os_info(): |
|---|
| 692 |     if os.name == 'nt': |
|---|
| 693 | Â Â Â Â Â Â Â Â ver =Â os.sys.getwindowsversion() |
|---|
| 694 |         ver_format = ver[3], ver[0], ver[1] |
|---|
| 695 | Â Â Â Â Â Â Â Â win_version =Â { |
|---|
| 696 |             (1, 4, 0): '95', |
|---|
| 697 |             (1, 4, 10): '98', |
|---|
| 698 |             (1, 4, 90): 'ME', |
|---|
| 699 |             (2, 4, 0): 'NT', |
|---|
| 700 |             (2, 5, 0): '2000', |
|---|
| 701 |             (2, 5, 1): 'XP', |
|---|
| 702 |             (2, 5, 2): '2003', |
|---|
| 703 | Â Â Â Â Â Â Â Â Â Â Â |
|---|