account.py.bak
1 # pylint: disable=too-many-instance-attributes,attribute-defined-outside-init 2 """ 3 account.py 4 ========== 5 6 Account related functions. 7 8 """ 9 10 from __future__ import absolute_import 11 12 import inspect 13 import re 14 import sys 15 import time 16 17 from PyQt4 import QtGui 18 19 import queues 20 from addresses import decodeAddress 21 from bmconfigparser import config 22 from helper_ackPayload import genAckPayload 23 from helper_sql import sqlQuery, sqlExecute 24 from .foldertree import AccountMixin 25 from .utils import str_broadcast_subscribers 26 27 28 def getSortedSubscriptions(count=False): 29 """ 30 Actually return a grouped dictionary rather than a sorted list 31 32 :param count: Whether to count messages for each fromaddress in the inbox 33 :type count: bool, default False 34 :retuns: dict keys are addresses, values are dicts containing settings 35 :rtype: dict, default {} 36 """ 37 queryreturn = sqlQuery('SELECT label, address, enabled FROM subscriptions ORDER BY label COLLATE NOCASE ASC') 38 ret = {} 39 for row in queryreturn: 40 label, address, enabled = row 41 ret[address] = {} 42 ret[address]["inbox"] = {} 43 ret[address]["inbox"]['label'] = label 44 ret[address]["inbox"]['enabled'] = enabled 45 ret[address]["inbox"]['count'] = 0 46 if count: 47 queryreturn = sqlQuery('''SELECT fromaddress, folder, count(msgid) as cnt 48 FROM inbox, subscriptions ON subscriptions.address = inbox.fromaddress 49 WHERE read = 0 AND toaddress = ? 50 GROUP BY inbox.fromaddress, folder''', str_broadcast_subscribers) 51 for row in queryreturn: 52 address, folder, cnt = row 53 if folder not in ret[address]: 54 ret[address][folder] = { 55 'label': ret[address]['inbox']['label'], 56 'enabled': ret[address]['inbox']['enabled'] 57 } 58 ret[address][folder]['count'] = cnt 59 return ret 60 61 62 def accountClass(address): 63 """Return a BMAccount for the address""" 64 if not config.has_section(address): 65 # .. todo:: This BROADCAST section makes no sense 66 if address == str_broadcast_subscribers: 67 subscription = BroadcastAccount(address) 68 if subscription.type != AccountMixin.BROADCAST: 69 return None 70 else: 71 subscription = SubscriptionAccount(address) 72 if subscription.type != AccountMixin.SUBSCRIPTION: 73 # e.g. deleted chan 74 return NoAccount(address) 75 return subscription 76 try: 77 gateway = config.get(address, "gateway") 78 for _, cls in inspect.getmembers(sys.modules[__name__], inspect.isclass): 79 if issubclass(cls, GatewayAccount) and cls.gatewayName == gateway: 80 return cls(address) 81 # general gateway 82 return GatewayAccount(address) 83 except: 84 pass 85 # no gateway 86 return BMAccount(address) 87 88 89 class AccountColor(AccountMixin): # pylint: disable=too-few-public-methods 90 """Set the type of account""" 91 92 def __init__(self, address, address_type=None): 93 self.isEnabled = True 94 self.address = address 95 if address_type is None: 96 if address is None: 97 self.type = AccountMixin.ALL 98 elif config.safeGetBoolean(self.address, 'mailinglist'): 99 self.type = AccountMixin.MAILINGLIST 100 elif config.safeGetBoolean(self.address, 'chan'): 101 self.type = AccountMixin.CHAN 102 elif sqlQuery( 103 '''select label from subscriptions where address=?''', self.address): 104 self.type = AccountMixin.SUBSCRIPTION 105 else: 106 self.type = AccountMixin.NORMAL 107 else: 108 self.type = address_type 109 110 111 class BMAccount(object): 112 """Encapsulate a Bitmessage account""" 113 114 def __init__(self, address=None): 115 self.address = address 116 self.type = AccountMixin.NORMAL 117 if config.has_section(address): 118 if config.safeGetBoolean(self.address, 'chan'): 119 self.type = AccountMixin.CHAN 120 elif config.safeGetBoolean(self.address, 'mailinglist'): 121 self.type = AccountMixin.MAILINGLIST 122 elif self.address == str_broadcast_subscribers: 123 self.type = AccountMixin.BROADCAST 124 else: 125 queryreturn = sqlQuery( 126 '''select label from subscriptions where address=?''', self.address) 127 if queryreturn: 128 self.type = AccountMixin.SUBSCRIPTION 129 130 def getLabel(self, address=None): 131 """Get a label for this bitmessage account""" 132 if address is None: 133 address = self.address 134 label = config.safeGet(address, 'label', address) 135 queryreturn = sqlQuery( 136 '''select label from addressbook where address=?''', address) 137 if queryreturn != []: 138 for row in queryreturn: 139 label, = row 140 else: 141 queryreturn = sqlQuery( 142 '''select label from subscriptions where address=?''', address) 143 if queryreturn != []: 144 for row in queryreturn: 145 label, = row 146 return label 147 148 def parseMessage(self, toAddress, fromAddress, subject, message): 149 """Set metadata and address labels on self""" 150 151 self.toAddress = toAddress 152 self.fromAddress = fromAddress 153 if isinstance(subject, unicode): 154 self.subject = str(subject) 155 else: 156 self.subject = subject 157 self.message = message 158 self.fromLabel = self.getLabel(fromAddress) 159 self.toLabel = self.getLabel(toAddress) 160 161 162 class NoAccount(BMAccount): 163 """Override the __init__ method on a BMAccount""" 164 165 def __init__(self, address=None): # pylint: disable=super-init-not-called 166 self.address = address 167 self.type = AccountMixin.NORMAL 168 169 def getLabel(self, address=None): 170 if address is None: 171 address = self.address 172 return address 173 174 175 class SubscriptionAccount(BMAccount): 176 """Encapsulate a subscription account""" 177 pass 178 179 180 class BroadcastAccount(BMAccount): 181 """Encapsulate a broadcast account""" 182 pass 183 184 185 class GatewayAccount(BMAccount): 186 """Encapsulate a gateway account""" 187 188 gatewayName = None 189 ALL_OK = 0 190 REGISTRATION_DENIED = 1 191 192 def __init__(self, address): 193 super(GatewayAccount, self).__init__(address) 194 195 def send(self): 196 """Override the send method for gateway accounts""" 197 198 # pylint: disable=unused-variable 199 status, addressVersionNumber, streamNumber, ripe = decodeAddress(self.toAddress) 200 stealthLevel = config.safeGetInt('bitmessagesettings', 'ackstealthlevel') 201 ackdata = genAckPayload(streamNumber, stealthLevel) 202 sqlExecute( 203 '''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''', 204 '', 205 self.toAddress, 206 ripe, 207 self.fromAddress, 208 self.subject, 209 self.message, 210 ackdata, 211 int(time.time()), # sentTime (this will never change) 212 int(time.time()), # lastActionTime 213 0, # sleepTill time. This will get set when the POW gets done. 214 'msgqueued', 215 0, # retryNumber 216 'sent', # folder 217 2, # encodingtype 218 # not necessary to have a TTL higher than 2 days 219 min(config.getint('bitmessagesettings', 'ttl'), 86400 * 2) 220 ) 221 222 queues.workerQueue.put(('sendmessage', self.toAddress)) 223 224 225 class MailchuckAccount(GatewayAccount): 226 """Encapsulate a particular kind of gateway account""" 227 228 # set "gateway" in keys.dat to this 229 gatewayName = "mailchuck" 230 registrationAddress = "BM-2cVYYrhaY5Gbi3KqrX9Eae2NRNrkfrhCSA" 231 unregistrationAddress = "BM-2cVMAHTRjZHCTPMue75XBK5Tco175DtJ9J" 232 relayAddress = "BM-2cWim8aZwUNqxzjMxstnUMtVEUQJeezstf" 233 regExpIncoming = re.compile(r"(.*)MAILCHUCK-FROM::(\S+) \| (.*)") 234 regExpOutgoing = re.compile(r"(\S+) (.*)") 235 236 def __init__(self, address): 237 super(MailchuckAccount, self).__init__(address) 238 self.feedback = self.ALL_OK 239 240 def createMessage(self, toAddress, fromAddress, subject, message): 241 """createMessage specific to a MailchuckAccount""" 242 self.subject = toAddress + " " + subject 243 self.toAddress = self.relayAddress 244 self.fromAddress = fromAddress 245 self.message = message 246 247 def register(self, email): 248 """register specific to a MailchuckAccount""" 249 self.toAddress = self.registrationAddress 250 self.subject = email 251 self.message = "" 252 self.fromAddress = self.address 253 self.send() 254 255 def unregister(self): 256 """unregister specific to a MailchuckAccount""" 257 self.toAddress = self.unregistrationAddress 258 self.subject = "" 259 self.message = "" 260 self.fromAddress = self.address 261 self.send() 262 263 def status(self): 264 """status specific to a MailchuckAccount""" 265 self.toAddress = self.registrationAddress 266 self.subject = "status" 267 self.message = "" 268 self.fromAddress = self.address 269 self.send() 270 271 def settings(self): 272 """settings specific to a MailchuckAccount""" 273 274 self.toAddress = self.registrationAddress 275 self.subject = "config" 276 self.message = QtGui.QApplication.translate( 277 "Mailchuck", 278 """# You can use this to configure your email gateway account 279 # Uncomment the setting you want to use 280 # Here are the options: 281 # 282 # pgp: server 283 # The email gateway will create and maintain PGP keys for you and sign, verify, 284 # encrypt and decrypt on your behalf. When you want to use PGP but are lazy, 285 # use this. Requires subscription. 286 # 287 # pgp: local 288 # The email gateway will not conduct PGP operations on your behalf. You can 289 # either not use PGP at all, or use it locally. 290 # 291 # attachments: yes 292 # Incoming attachments in the email will be uploaded to MEGA.nz, and you can 293 # download them from there by following the link. Requires a subscription. 294 # 295 # attachments: no 296 # Attachments will be ignored. 297 # 298 # archive: yes 299 # Your incoming emails will be archived on the server. Use this if you need 300 # help with debugging problems or you need a third party proof of emails. This 301 # however means that the operator of the service will be able to read your 302 # emails even after they have been delivered to you. 303 # 304 # archive: no 305 # Incoming emails will be deleted from the server as soon as they are relayed 306 # to you. 307 # 308 # masterpubkey_btc: BIP44 xpub key or electrum v1 public seed 309 # offset_btc: integer (defaults to 0) 310 # feeamount: number with up to 8 decimal places 311 # feecurrency: BTC, XBT, USD, EUR or GBP 312 # Use these if you want to charge people who send you emails. If this is on and 313 # an unknown person sends you an email, they will be requested to pay the fee 314 # specified. As this scheme uses deterministic public keys, you will receive 315 # the money directly. To turn it off again, set "feeamount" to 0. Requires 316 # subscription. 317 """) 318 self.fromAddress = self.address 319 320 def parseMessage(self, toAddress, fromAddress, subject, message): 321 """parseMessage specific to a MailchuckAccount""" 322 323 super(MailchuckAccount, self).parseMessage(toAddress, fromAddress, subject, message) 324 if fromAddress == self.relayAddress: 325 matches = self.regExpIncoming.search(subject) 326 if matches is not None: 327 self.subject = "" 328 if not matches.group(1) is None: 329 self.subject += matches.group(1) 330 if not matches.group(3) is None: 331 self.subject += matches.group(3) 332 if not matches.group(2) is None: 333 self.fromLabel = matches.group(2) 334 self.fromAddress = matches.group(2) 335 if toAddress == self.relayAddress: 336 matches = self.regExpOutgoing.search(subject) 337 if matches is not None: 338 if not matches.group(2) is None: 339 self.subject = matches.group(2) 340 if not matches.group(1) is None: 341 self.toLabel = matches.group(1) 342 self.toAddress = matches.group(1) 343 self.feedback = self.ALL_OK 344 if fromAddress == self.registrationAddress and self.subject == "Registration Request Denied": 345 self.feedback = self.REGISTRATION_DENIED 346 return self.feedback