/ src / bitmessageqt / account.py.bak
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