NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

mailSvc.py (56126B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\services\mail\mailSvc.py
      2 import copy
      3 import os
      4 import shelve
      5 import sys
      6 import zlib
      7 import blue
      8 import characterSettingsStorage.characterSettingsConsts as cSettings
      9 import eve.common.script.util.notificationconst as notificationConst
     10 import localization
     11 import log
     12 import uthread
     13 import utillib
     14 from carbon.common.script.sys.service import Service
     15 from carbon.common.script.sys.serviceConst import SERVICE_RUNNING, SERVICE_START_PENDING
     16 from carbon.common.script.util.format import FmtDate
     17 from carbonui import uiconst
     18 from carbonui.uicore import uicore
     19 from carbonui.util.various_unsorted import SortListOfTuples
     20 from characterSettingsStorage.characterSettingsObject import CharacterSettingsObject
     21 from eve.client.script.ui.shared.neocom.evemail import MailAssignColorWnd, MailReadingWnd, MailWindow, NewNewMessage
     22 from eve.client.script.ui.util import utilWindows
     23 from eve.client.script.util import eveMisc
     24 from eve.common.lib import appConst as const
     25 from eve.common.script.sys import idCheckers
     26 from eve.common.script.util.notificationconst import groupNamePaths as notificationGroupNamePaths
     27 from eveexceptions import UserError
     28 from eveexceptions.exceptionEater import ExceptionEater
     29 from localization import GetByLabel
     30 from notifications.common.formatters.mailsummary import MailSummaryFormatter
     31 from notifications.common.formatters.newMail import NewMailFormatter
     32 from player_messaging.client.ui_message import prompt_player_fsd_dialog
     33 MAIL_PATH = blue.paths.ResolvePath('cache:/EveMail/')
     34 BODIES_IN_CACHE = 30
     35 MAIL_FILE_HEADER_VERSION = 5
     36 MAIL_FILE_BODY_VERSION = 1
     37 ALL_LABELS = 255
     38 swatchColors = {0: ('ffffff', 0),
     39  1: ('ffff01', 1),
     40  2: ('ff6600', 2),
     41  3: ('fe0000', 3),
     42  4: ('9a0000', 4),
     43  5: ('660066', 5),
     44  6: ('0000fe', 6),
     45  7: ('0099ff', 7),
     46  8: ('01ffff', 8),
     47  9: ('00ff33', 9),
     48  10: ('349800', 10),
     49  11: ('006634', 11),
     50  12: ('666666', 12),
     51  13: ('999999', 13),
     52  14: ('e6e6e6', 14),
     53  15: ('ffffcd', 15),
     54  16: ('99ffff', 16),
     55  17: ('ccff9a', 17)}
     56 
     57 class mailSvc(Service):
     58     __guid__ = 'svc.mailSvc'
     59     __displayname__ = 'Mail service'
     60     __exportedcalls__ = {'MoveMessagesToTrash': [],
     61      'MoveMessagesFromTrash': [],
     62      'MarkMessagesAsUnread': [],
     63      'MarkMessagesAsRead': [],
     64      'MoveAllToTrash': [],
     65      'MoveToTrashByLabel': [],
     66      'MoveToTrashByList': [],
     67      'MoveAllFromTrash': [],
     68      'MarkAllAsUnread': [],
     69      'MarkAsUnreadByLabel': [],
     70      'MarkAsUnreadByList': [],
     71      'MarkAllAsRead': [],
     72      'MarkAsReadByLabel': [],
     73      'MarkAsReadByList': [],
     74      'EmptyTrash': [],
     75      'DeleteMails': [],
     76      'GetBody': [],
     77      'GetMailsByLabelOrListID': [],
     78      'SyncMail': [],
     79      'GetLabels': [],
     80      'EditLabel': [],
     81      'CreateLabel': [],
     82      'DeleteLabel': [],
     83      'AssignLabels': [],
     84      'RemoveLabels': [],
     85      'ReplaceLabels': [],
     86      'SaveChangesToDisk': [],
     87      'ClearCache': [],
     88      'SendMail': [],
     89      'IsFileCacheCorrupted': [],
     90      'PrimeOwners': []}
     91     __notifyevents__ = ['OnMailSent',
     92      'OnMailDeleted',
     93      'OnMailUndeleted',
     94      'OnCharacterSessionChanged',
     95      'OnLabelsCreatedByExternal',
     96      'OnMailUpdatedByExternal',
     97      'OnMailTrashed',
     98      'OnMailRestored']
     99     __startupdependencies__ = ['settings', 'characterSettings']
    100 
    101     def __init__(self):
    102         Service.__init__(self)
    103         self.mailSynced = 0
    104         self.cacheFileCorruption = False
    105 
    106     def Run(self, ms = None):
    107         self.state = SERVICE_START_PENDING
    108         self.Initialize()
    109         self.state = SERVICE_RUNNING
    110 
    111     def Initialize(self):
    112         self.mailMgr = sm.RemoteSvc('mailMgr')
    113         self.mailHeaders = {}
    114         self.mailBodies = {}
    115         self.mailBodiesOrder = []
    116         self.labels = None
    117         self.needToSaveHeaders = False
    118         self.isSaving = False
    119         self.blinkTab = False
    120         self.blinkNeocom = False
    121         self.donePrimingRecipients = False
    122         self.mailSettingObject = None
    123         if session.charid:
    124             self.mailFileHeaders = MAIL_PATH + 'mailheaders_' + str(session.charid)
    125             self.mailFileBodies = MAIL_PATH + 'mailbodies_' + str(session.charid)
    126         try:
    127             if not os.path.exists(MAIL_PATH):
    128                 os.mkdir(MAIL_PATH)
    129         except:
    130             self.LogError('Error creating mail cache folder. charid:', str(session.charid))
    131             self.cacheFileCorruption = True
    132             self.TryCloseMailWindow()
    133             raise UserError('MailCacheFileError')
    134 
    135     def OnCharacterSessionChanged(self, _oldCharacterID, _newCharacterID):
    136         if session.charid:
    137             self.Initialize()
    138             self.mailSynced = 0
    139 
    140     def PrimeOwners(self, owners):
    141         uthread.Lock(self, 'Prime')
    142         try:
    143             if len(owners) > 0:
    144                 toprime = filter(lambda x: x > 0, owners)
    145                 cfg.eveowners.Prime(toprime)
    146         finally:
    147             uthread.UnLock(self, 'Prime')
    148 
    149     def TrySyncMail(self):
    150         if not self.mailSynced:
    151             try:
    152                 self.SyncMail()
    153             except UserError as e:
    154                 raise
    155             except Exception as e:
    156                 log.LogException('mail failed to load')
    157                 raise UserError('CustomInfo', {'info': localization.GetByLabel('UI/Mail/FailedToLoad')})
    158 
    159     def SyncMail(self):
    160         self.LogInfo('Syncing mail')
    161         uthread.Lock(self)
    162         try:
    163             if self.__ReadFromHeaderFile('version') != MAIL_FILE_HEADER_VERSION:
    164                 self.__ClearHeaderCache()
    165                 self.__WriteToHeaderFile('version', MAIL_FILE_HEADER_VERSION)
    166             if self.__ReadFromBodyFile('version') != MAIL_FILE_BODY_VERSION:
    167                 self.__ClearBodyCache()
    168                 self.__WriteToBodyFile('version', MAIL_FILE_BODY_VERSION)
    169             self.mailHeaders = self.__ReadFromHeaderFile('mail')
    170             if self.mailHeaders is None:
    171                 self.mailHeaders = {}
    172                 self.__WriteToHeaderFile('mail', {})
    173             lastID = 0
    174             firstID = sys.maxint
    175             for messageID in self.mailHeaders:
    176                 if messageID > lastID:
    177                     lastID = messageID
    178                 if messageID < firstID:
    179                     firstID = messageID
    180 
    181             if lastID == 0:
    182                 firstID = None
    183             mailbox = self.mailMgr.SyncMail(firstID, lastID)
    184             toPrime = set()
    185             incoming = mailbox.newMail
    186             if mailbox.oldMail is not None:
    187                 incoming.extend(mailbox.oldMail)
    188             for mailRow in incoming:
    189                 toPrime.add(mailRow.senderID)
    190                 toListID = mailRow.toListID
    191                 toCharacterIDs = []
    192                 if mailRow.toCharacterIDs:
    193                     toCharacterIDs = [ int(x) for x in mailRow.toCharacterIDs.split(',') ]
    194                 m = utillib.KeyVal(messageID=mailRow.messageID, senderID=mailRow.senderID, toCharacterIDs=toCharacterIDs, toListID=toListID, toCorpOrAllianceID=mailRow.toCorpOrAllianceID, subject=mailRow.title, sentDate=mailRow.sentDate)
    195                 self.mailHeaders[mailRow.messageID] = m
    196 
    197             toDelete = self.mailHeaders.keys()
    198             toFetch = {}
    199             for statusRow in mailbox.mailStatus:
    200                 if statusRow.messageID not in self.mailHeaders:
    201                     m = utillib.KeyVal(messageID=statusRow.messageID, read=statusRow.statusMask & const.mailStatusMaskRead == const.mailStatusMaskRead, replied=statusRow.statusMask & const.mailStatusMaskReplied == const.mailStatusMaskReplied, forwarded=statusRow.statusMask & const.mailStatusMaskForwarded == const.mailStatusMaskForwarded, trashed=statusRow.statusMask & const.mailStatusMaskTrashed == const.mailStatusMaskTrashed, statusMask=statusRow.statusMask, labels=self.GetLabelMaskAsList(statusRow.labelMask), labelMask=statusRow.labelMask)
    202                     toFetch[statusRow.messageID] = m
    203                 else:
    204                     toDelete.remove(statusRow.messageID)
    205                     mail = self.mailHeaders[statusRow.messageID]
    206                     if getattr(mail, 'statusMask', None) != statusRow.statusMask:
    207                         mail.statusMask = statusRow.statusMask
    208                         mail.read = mail.statusMask & const.mailStatusMaskRead == const.mailStatusMaskRead
    209                         mail.replied = mail.statusMask & const.mailStatusMaskReplied == const.mailStatusMaskReplied
    210                         mail.forwarded = mail.statusMask & const.mailStatusMaskForwarded == const.mailStatusMaskForwarded
    211                         mail.trashed = mail.statusMask & const.mailStatusMaskTrashed == const.mailStatusMaskTrashed
    212                     if getattr(mail, 'labelMask', None) != statusRow.labelMask:
    213                         mail.labelMask = statusRow.labelMask
    214                         mail.labels = self.GetLabelMaskAsList(mail.labelMask)
    215                     if mail.statusMask & const.mailStatusMaskAutomated > 0 and mail.senderID == mail.toListID and mail.senderID in toPrime:
    216                         toPrime.remove(mail.senderID)
    217 
    218             if len(toFetch) > 0:
    219                 revivedMail = self.mailMgr.GetMailHeaders(toFetch.keys())
    220                 for mailRow in revivedMail:
    221                     toListID = mailRow.toListID
    222                     toCharacterIDs = []
    223                     if mailRow.toCharacterIDs:
    224                         toCharacterIDs = [ int(x) for x in mailRow.toCharacterIDs.split(',') ]
    225                     mail = toFetch[mailRow.messageID]
    226                     mail.senderID = mailRow.senderID
    227                     mail.toCharacterIDs = toCharacterIDs
    228                     mail.toListID = toListID
    229                     mail.toCorpOrAllianceID = mailRow.toCorpOrAllianceID
    230                     mail.subject = mailRow.title
    231                     mail.sentDate = mailRow.sentDate
    232                     if mail.statusMask & const.mailStatusMaskAutomated == 0 or mail.senderID != mail.toListID:
    233                         toPrime.add(mail.senderID)
    234                     self.mailHeaders[mailRow.messageID] = mail
    235 
    236             self.PrimeOwners(list(toPrime))
    237             for mail in self.mailHeaders.itervalues():
    238                 if hasattr(mail, 'senderName'):
    239                     continue
    240                 if mail.senderID is None:
    241                     raise RuntimeError('Invalid mail item', mail)
    242                 if mail.statusMask & const.mailStatusMaskAutomated and mail.senderID == mail.toListID:
    243                     mail.senderName = sm.GetService('mailinglists').GetDisplayName(mail.senderID)
    244                 else:
    245                     try:
    246                         mail.senderName = cfg.eveowners.Get(mail.senderID).name
    247                     except IndexError:
    248                         mail.senderName = localization.GetByLabel('UI/Generic/Unknown')
    249 
    250             for messageID in toDelete:
    251                 del self.mailHeaders[messageID]
    252                 self.__DeleteFromBodyFile(messageID)
    253 
    254             self.__WriteToHeaderFile('mail', self.mailHeaders)
    255             self.mailSynced = 1
    256         finally:
    257             uthread.UnLock(self)
    258 
    259         self.LogInfo('Done syncing mail')
    260 
    261     def GetLabelMaskAsList(self, mask):
    262         if mask < 0:
    263             raise RuntimeError('Invalid label mask', mask)
    264         counter = 0
    265         labels = []
    266         while mask != 0:
    267             mask, bitSet = divmod(mask, 2)
    268             if bitSet == 1:
    269                 labels.append(pow(2, counter))
    270             counter += 1
    271 
    272         return labels
    273 
    274     def GetUnreadCounts(self):
    275         self.LogInfo('GetUnreadCounts')
    276         unreadCounts = utillib.KeyVal(labels={}, lists={})
    277         totalUnread = 0
    278         for messageID, message in self.mailHeaders.iteritems():
    279             if not message.read and not message.trashed:
    280                 totalUnread += 1
    281                 mask = message.labelMask
    282                 counter = 0
    283                 while mask != 0:
    284                     mask, bitSet = divmod(mask, 2)
    285                     if bitSet == 1:
    286                         labelID = pow(2, counter)
    287                         if labelID in unreadCounts.labels:
    288                             unreadCounts.labels[labelID] += 1
    289                         else:
    290                             unreadCounts.labels[labelID] = 1
    291                     counter += 1
    292 
    293                 if message.toListID is not None:
    294                     if message.toListID in unreadCounts.lists:
    295                         unreadCounts.lists[message.toListID] += 1
    296                     else:
    297                         unreadCounts.lists[message.toListID] = 1
    298 
    299         unreadCounts.labels[None] = totalUnread
    300         self.LogInfo('unreadCounts:', unreadCounts)
    301         return unreadCounts
    302 
    303     def CheckShouldStopBlinking(self, *args):
    304         allUnreadGroups = self.GetUnreadCounts()
    305         allUnread = allUnreadGroups.labels.get(None, 0)
    306         if allUnread == 0:
    307             self.StopMailBlinking()
    308 
    309     def StopMailBlinking(self, *args):
    310         self.SetBlinkTabState(False)
    311         sm.ScatterEvent('OnMailStartStopBlinkingTab', 'mail', 0)
    312         self.SetBlinkNeocomState(False)
    313         sm.GetService('neocom').BlinkOff('mail')
    314 
    315     def MoveMessagesToTrash(self, messageIDs):
    316         self.LogInfo('Move to trash', messageIDs)
    317         self.mailMgr.MoveToTrash(messageIDs)
    318         self._RefreshTrashedStatusWhenMovingToTrash(messageIDs)
    319 
    320     def _RefreshTrashedStatusWhenMovingToTrash(self, messageIDs):
    321         self._RefreshTrashedStatus(messageIDs, True, lambda mailHeader: mailHeader.statusMask | const.mailStatusMaskTrashed)
    322         self.CheckShouldStopBlinking()
    323 
    324     def MoveMessagesFromTrash(self, messageIDs):
    325         self.LogInfo('Move from trash', messageIDs)
    326         self.mailMgr.MoveFromTrash(messageIDs)
    327         self._RefreshTrashedStatusWhenMovingFromTrash(messageIDs)
    328 
    329     def _RefreshTrashedStatusWhenMovingFromTrash(self, messageIDs):
    330         self._RefreshTrashedStatus(messageIDs, False, lambda mailHeader: mailHeader.statusMask & ALL_LABELS - const.mailStatusMaskRead)
    331         sm.ScatterEvent('OnMailCountersUpdate')
    332 
    333     def _RefreshTrashedStatus(self, messageIDs, isTrashed, statusMaskFunc):
    334         for messageID in messageIDs:
    335             mail = self.mailHeaders[messageID]
    336             mail.trashed = isTrashed
    337             mail.statusMask = statusMaskFunc(mail)
    338 
    339         self.needToSaveHeaders = True
    340 
    341     def MarkMessagesAsUnread(self, messageIDs, notifyServer = True):
    342         self.LogInfo('Mark as unread', messageIDs)
    343         if notifyServer:
    344             self.mailMgr.MarkAsUnread(messageIDs)
    345         for messageID in messageIDs:
    346             mail = self.mailHeaders[messageID]
    347             mail.read = False
    348             mail.statusMask = mail.statusMask & ALL_LABELS - const.mailStatusMaskRead
    349 
    350         self.needToSaveHeaders = True
    351         sm.ScatterEvent('OnMailCountersUpdate')
    352 
    353     def MarkMessagesAsRead(self, messageIDs, notifyServer = True):
    354         self.LogInfo('Mark as read', messageIDs, notifyServer)
    355         if notifyServer:
    356             self.mailMgr.MarkAsRead(messageIDs)
    357         for messageID in messageIDs:
    358             mail = self.mailHeaders[messageID]
    359             mail.read = True
    360             mail.statusMask = mail.statusMask | const.mailStatusMaskRead
    361 
    362         self.needToSaveHeaders = True
    363         sm.ScatterEvent('OnMailCountersUpdate')
    364         self.CheckShouldStopBlinking()
    365 
    366     def MoveAllToTrash(self):
    367         self.LogInfo('Move all to trash')
    368         self.mailMgr.MoveAllToTrash()
    369         for mail in self.mailHeaders.itervalues():
    370             mail.trashed = True
    371             mail.statusMask = mail.statusMask | const.mailStatusMaskTrashed
    372 
    373         self.needToSaveHeaders = True
    374 
    375     def MoveToTrashByLabel(self, labelID):
    376         self.LogInfo('Move label to trash', labelID)
    377         self.mailMgr.MoveToTrashByLabel(labelID)
    378         for mail in self.mailHeaders.itervalues():
    379             if mail.labelMask & labelID == labelID:
    380                 mail.trashed = True
    381                 mail.statusMask = mail.statusMask | const.mailStatusMaskTrashed
    382 
    383         self.needToSaveHeaders = True
    384 
    385     def MoveToTrashByList(self, listID):
    386         self.LogInfo('Move list to trash', listID)
    387         self.mailMgr.MoveToTrashByList(listID)
    388         for mail in self.mailHeaders.itervalues():
    389             if mail.toListID is not None and listID == mail.toListID:
    390                 mail.trashed = True
    391                 mail.statusMask = mail.statusMask | const.mailStatusMaskTrashed
    392 
    393         self.needToSaveHeaders = True
    394 
    395     def MarkAllAsUnread(self):
    396         self.LogInfo('Mark all as unread')
    397         self.mailMgr.MarkAllAsUnread()
    398         for mail in self.mailHeaders.itervalues():
    399             if not mail.trashed:
    400                 mail.read = False
    401                 mail.statusMask = mail.statusMask & ALL_LABELS - const.mailStatusMaskRead
    402 
    403         self.needToSaveHeaders = True
    404 
    405     def MarkAsUnreadByLabel(self, labelID):
    406         self.LogInfo('Mark label unread', labelID)
    407         self.mailMgr.MarkAsUnreadByLabel(labelID)
    408         for mail in self.mailHeaders.itervalues():
    409             if not mail.trashed and mail.labelMask & labelID == labelID:
    410                 mail.read = False
    411                 mail.statusMask = mail.statusMask & ALL_LABELS - const.mailStatusMaskRead
    412 
    413         self.needToSaveHeaders = True
    414 
    415     def MarkAsUnreadByList(self, listID):
    416         self.LogInfo('Mark list unread', listID)
    417         self.mailMgr.MarkAsUnreadByList(listID)
    418         for mail in self.mailHeaders.itervalues():
    419             if not mail.trashed and mail.toListID is not None and listID == mail.toListID:
    420                 mail.read = False
    421                 mail.statusMask = mail.statusMask & ALL_LABELS - const.mailStatusMaskRead
    422 
    423         self.needToSaveHeaders = True
    424 
    425     def MarkAllAsRead(self):
    426         self.LogInfo('Mark all read')
    427         self.mailMgr.MarkAllAsRead()
    428         for mail in self.mailHeaders.itervalues():
    429             if not mail.trashed:
    430                 mail.read = True
    431                 mail.statusMask = mail.statusMask | const.mailStatusMaskRead
    432 
    433         self.needToSaveHeaders = True
    434 
    435     def MarkAsReadByLabel(self, labelID):
    436         self.LogInfo('Mark label read', labelID)
    437         self.mailMgr.MarkAsReadByLabel(labelID)
    438         for mail in self.mailHeaders.itervalues():
    439             if not mail.trashed and mail.labelMask & labelID == labelID:
    440                 mail.read = True
    441                 mail.statusMask = mail.statusMask | const.mailStatusMaskRead
    442 
    443         self.needToSaveHeaders = True
    444 
    445     def MarkAsReadByList(self, listID):
    446         self.LogInfo('Mark list read', listID)
    447         self.mailMgr.MarkAsReadByList(listID)
    448         for mail in self.mailHeaders.itervalues():
    449             if not mail.trashed and mail.toListID is not None and listID == mail.toListID:
    450                 mail.read = True
    451                 mail.statusMask = mail.statusMask | const.mailStatusMaskRead
    452 
    453         self.needToSaveHeaders = True
    454 
    455     def MoveAllFromTrash(self):
    456         self.LogInfo('Move all from trash')
    457         self.mailMgr.MoveAllFromTrash()
    458         for mail in self.mailHeaders.itervalues():
    459             mail.trashed = False
    460             mail.statusMask = mail.statusMask & ALL_LABELS - const.mailStatusMaskTrashed
    461 
    462         self.needToSaveHeaders = True
    463 
    464     def EmptyTrash(self):
    465         self.LogInfo('Empty trash')
    466         self.mailMgr.EmptyTrash()
    467         deleted = []
    468         for messageID, mail in self.mailHeaders.iteritems():
    469             if mail.trashed:
    470                 deleted.append(messageID)
    471 
    472         self.LogInfo('Deleted', len(deleted), 'messages')
    473         for messageID in deleted:
    474             del self.mailHeaders[messageID]
    475             if messageID in self.mailBodies:
    476                 del self.mailBodies[messageID]
    477             if messageID in self.mailBodiesOrder:
    478                 self.mailBodiesOrder.remove(messageID)
    479             self.__DeleteFromBodyFile(messageID)
    480 
    481         self.needToSaveHeaders = True
    482 
    483     def DeleteMails(self, messageIDs):
    484         self.LogInfo('Delete', messageIDs)
    485         self.mailMgr.DeleteMail(messageIDs)
    486         for messageID in messageIDs:
    487             if messageID in self.mailHeaders:
    488                 del self.mailHeaders[messageID]
    489             if messageID in self.mailBodies:
    490                 del self.mailBodies[messageID]
    491             if messageID in self.mailBodiesOrder:
    492                 self.mailBodiesOrder.remove(messageID)
    493             self.__DeleteFromBodyFile(messageID)
    494 
    495         self.needToSaveHeaders = True
    496 
    497     def GetBody(self, messageID):
    498         self.LogInfo('GetMailBody', messageID)
    499         if messageID in self.mailBodies:
    500             self.LogInfo('Cached')
    501             if not self.mailHeaders[messageID].read:
    502                 self.MarkMessagesAsRead([messageID])
    503             return self.mailBodies[messageID]
    504         if len(self.mailBodiesOrder) > BODIES_IN_CACHE:
    505             try:
    506                 self.LogInfo('Must make room in cache')
    507                 del self.mailBodies[self.mailBodiesOrder.pop(0)]
    508             except KeyError:
    509                 pass
    510 
    511         if messageID in self.mailHeaders:
    512             body = self.__ReadFromBodyFile(messageID)
    513             shouldMarkAsRead = not self.mailHeaders[messageID].read
    514             if body is None:
    515                 compressedBody = self.mailMgr.GetBody(messageID, shouldMarkAsRead)
    516                 if compressedBody is None:
    517                     return ''
    518                 shouldMarkAsRead = False
    519                 body = zlib.decompress(compressedBody).decode('utf-8')
    520                 self.__WriteToBodyFile(messageID, body)
    521             self.MarkMessagesAsRead([messageID], shouldMarkAsRead)
    522             self.mailBodies[messageID] = body
    523             self.mailBodiesOrder.append(messageID)
    524             return body
    525         self.LogError("Asking me to get a body, but I can't find the messageID asked for in self.mailHeaders. messageID:", messageID)
    526 
    527     def GetMailsByLabelOrListID(self, labelID = None, orderBy = None, ascending = False, pos = 0, count = 20, listID = None):
    528         if orderBy is None:
    529             orderBy = localization.GetByLabel('UI/Mail/Received')
    530         self.LogInfo('Get messages', labelID, orderBy, ascending, pos, count, listID)
    531         tmpList = []
    532         isSentitems = labelID == const.mailLabelSent
    533         if isSentitems and orderBy == localization.GetByLabel('UI/Mail/Sender'):
    534             mails = []
    535             for message in self.mailHeaders.itervalues():
    536                 if labelID in message.labels:
    537                     mails.append(message)
    538 
    539             self.TryPrimeRecipients(mails, setDone=True)
    540         for message in self.mailHeaders.itervalues():
    541             if message.trashed:
    542                 continue
    543             if listID is not None:
    544                 if message.toListID is not None and listID == message.toListID:
    545                     tmpList.append(self.PrepareOrder(message, orderBy, ascending, isSentitems))
    546                 continue
    547             if labelID is None or message.labelMask & labelID == labelID:
    548                 tmpList.append(self.PrepareOrder(message, orderBy, ascending, isSentitems))
    549 
    550         ret = utillib.KeyVal()
    551         ret.totalNum = len(tmpList)
    552         ret.sorted = self.DoSort(tmpList, ascending=ascending, pos=pos, count=count)
    553         if isSentitems and orderBy != localization.GetByLabel('UI/Mail/Sender'):
    554             self.TryPrimeRecipients(ret.sorted)
    555         return ret
    556 
    557     def GetTrashedMails(self, orderBy = None, ascending = False, pos = 0, count = 20):
    558         if orderBy is None:
    559             orderBy = localization.GetByLabel('UI/Mail/Received')
    560         self.LogInfo('Get trash', orderBy, ascending, pos, count)
    561         tmpList = []
    562         for message in self.mailHeaders.itervalues():
    563             if not message.trashed:
    564                 continue
    565             tmpList.append(self.PrepareOrder(message, orderBy, ascending))
    566 
    567         ret = utillib.KeyVal()
    568         ret.totalNum = len(tmpList)
    569         ret.sorted = self.DoSort(tmpList, ascending=ascending, pos=pos, count=count)
    570         return ret
    571 
    572     def PrepareOrder(self, message, orderBy = None, ascending = False, sentItems = 0):
    573         if orderBy is None:
    574             orderBy = localization.GetByLabel('UI/Mail/Received')
    575         if ascending:
    576             secondarySortID = message.messageID
    577         else:
    578             secondarySortID = -message.messageID
    579         if orderBy == localization.GetByLabel('UI/Mail/Received'):
    580             return (message.messageID, secondarySortID)
    581         if orderBy == localization.GetByLabel('UI/Mail/Subject'):
    582             return (message.subject.lower(), secondarySortID)
    583         if orderBy == localization.GetByLabel('UI/Mail/Sender'):
    584             if sentItems:
    585                 name = self.GetRecipient(message, getName=1)
    586             else:
    587                 name = message.senderName
    588             return (name.lower(), secondarySortID)
    589         if orderBy == localization.GetByLabel('UI/Mail/Status'):
    590             order = 4
    591             if not message.read:
    592                 order = 1
    593             elif message.replied:
    594                 order = 2
    595             elif message.forwarded:
    596                 order = 3
    597             return (order, secondarySortID)
    598 
    599     def DoSort(self, list, ascending = False, pos = 0, count = 20):
    600         self.LogInfo('Sort', ascending, pos, count)
    601         retMails = []
    602         list.sort(reverse=ascending)
    603         for message in list[pos:pos + count]:
    604             retMails.append(self.mailHeaders[abs(message[1])])
    605 
    606         return retMails
    607 
    608     def GetMailsByIDs(self, messageIDs):
    609         mailDict = {}
    610         for messageID in messageIDs:
    611             mailDict[messageID] = self.GetMailByID(messageID)
    612 
    613         return mailDict
    614 
    615     def GetMailByID(self, messageID):
    616         try:
    617             return self.mailHeaders[messageID]
    618         except KeyError:
    619             return None
    620 
    621     def TryPrimeRecipients(self, messages, setDone = False):
    622         if getattr(self, 'donePrimingRecipients', False):
    623             return
    624         idSet = set()
    625         for msg in messages:
    626             entityID = self.GetRecipient(msg, getName=0)
    627             if entityID > 0:
    628                 idSet.add(entityID)
    629 
    630         owners = list(idSet)
    631         self.PrimeOwners(owners)
    632         if setDone:
    633             self.donePrimingRecipients = True
    634 
    635     def GetRecipient(self, message, getName = 1):
    636         toCharIDs = message.toCharacterIDs or []
    637         toListID = message.toListID
    638         if message.toCorpOrAllianceID is not None:
    639             toCorpIDs = [message.toCorpOrAllianceID]
    640         else:
    641             toCorpIDs = []
    642         if toListID is not None:
    643             numSentTo = 1
    644         else:
    645             numSentTo = 0
    646         numSentTo += len(toCharIDs) + len(toCorpIDs)
    647         if numSentTo > 1:
    648             if getName:
    649                 return localization.GetByLabel('UI/Mail/Multiple')
    650             else:
    651                 return -1
    652         for each in toCharIDs + toCorpIDs:
    653             if getName:
    654                 return cfg.eveowners.Get(each).ownerName
    655             return each
    656 
    657         if toListID is not None:
    658             if getName:
    659                 return sm.GetService('mailinglists').GetDisplayName(toListID)
    660             else:
    661                 return -1
    662         self.LogWarn('In GetRecipient: message = ', message)
    663         if getName:
    664             return ''
    665         else:
    666             return -1
    667 
    668     def GetLabels(self):
    669         if self.labels is None:
    670             self.labels = self.mailMgr.GetLabels()
    671         return self.labels
    672 
    673     def GetAllLabels(self, assignable = 0):
    674         self.LogInfo('GetAllLabels')
    675         allLabels = copy.copy(self.GetLabels())
    676         static = [(localization.GetByLabel('UI/Mail/LabelAlliance'), const.mailLabelAlliance),
    677          (localization.GetByLabel('UI/Mail/LabelCorp'), const.mailLabelCorporation),
    678          (localization.GetByLabel('UI/Mail/LabelInbox'), const.mailLabelInbox),
    679          (localization.GetByLabel('UI/Mail/LabelSent'), const.mailLabelSent)]
    680         for name, id in static:
    681             keyVal = allLabels.get(id, None)
    682             if keyVal is not None:
    683                 keyVal.static = 1
    684                 keyVal.name = name
    685             else:
    686                 keyVal = utillib.KeyVal()
    687                 keyVal.name = name
    688                 keyVal.labelID = id
    689                 keyVal.static = 1
    690                 keyVal.color = None
    691                 allLabels[id] = keyVal
    692 
    693         self.labels = allLabels.copy()
    694         if assignable:
    695             allLabels.pop(const.mailLabelSent, None)
    696         return allLabels
    697 
    698     def EditLabel(self, labelID, name = None, color = None):
    699         self.LogInfo('EditLabel', labelID, name, color)
    700         if name is None and color is None or name == '':
    701             raise UserError('MailLabelMustProvideName')
    702         self.mailMgr.EditLabel(labelID, name, color)
    703         staticLabels = [const.mailLabelSent,
    704          const.mailLabelInbox,
    705          const.mailLabelCorporation,
    706          const.mailLabelAlliance]
    707         self.GetLabels()
    708         if labelID not in self.labels and labelID not in staticLabels:
    709             raise RuntimeError("Invalid label cache, can't update", labelID, name)
    710         if name is not None:
    711             self.labels[labelID].name = name
    712         if color is not None:
    713             self.labels[labelID].color = color
    714         sm.ScatterEvent('OnMyLabelsChanged', 'mail_labels', None)
    715 
    716     def AddLabel(self, labelID, name, color):
    717         self.GetLabels()
    718         self.labels[labelID] = utillib.KeyVal(labelID=labelID, name=name, color=color)
    719 
    720     def CreateLabel(self, name, color = None):
    721         self.LogInfo('CreateLabel', name, color)
    722         labelID = self.mailMgr.CreateLabel(name, color)
    723         self.AddLabel(labelID, name, color)
    724         sm.ScatterEvent('OnMyLabelsChanged', 'mail_labels', labelID)
    725         return labelID
    726 
    727     def DeleteLabel(self, labelID):
    728         self.LogInfo('DeleteLabel', labelID)
    729         self.mailMgr.DeleteLabel(labelID)
    730         self.GetLabels()
    731         try:
    732             del self.labels[labelID]
    733         except KeyError:
    734             raise RuntimeError("Invalid label cache, can't remove", labelID)
    735 
    736         for message in self.mailHeaders.itervalues():
    737             if labelID in message.labels:
    738                 message.labelMask = message.labelMask & const.maxInt - labelID
    739                 message.labels = self.GetLabelMaskAsList(message.labelMask)
    740 
    741         sm.ScatterEvent('OnMyLabelsChanged', 'mail_labels', None)
    742 
    743     def AssignLabels(self, messageIDs, labelID, notifyServer = True):
    744         self.LogInfo('AssignLabels', messageIDs, labelID)
    745         mailsToGetLabel = {}
    746         for messageID in messageIDs:
    747             mail = self.mailHeaders[messageID]
    748             if labelID & mail.labelMask != labelID:
    749                 mailsToGetLabel[messageID] = mail
    750 
    751         if len(mailsToGetLabel) > 0 and notifyServer:
    752             self.mailMgr.AssignLabels(mailsToGetLabel.keys(), labelID)
    753         for mail in mailsToGetLabel.itervalues():
    754             mail.labelMask = mail.labelMask | labelID
    755             mail.labels = self.GetLabelMaskAsList(mail.labelMask)
    756 
    757         self.needToSaveHeaders = True
    758         sm.ScatterEvent('OnMailCountersUpdate')
    759 
    760     def RemoveLabels(self, messageIDs, labelID, notifyServer = True):
    761         self.LogInfo('RemoveLabels', messageIDs, labelID)
    762         mailsToRemoveLabelFrom = {}
    763         for messageID in messageIDs:
    764             mail = self.mailHeaders[messageID]
    765             if labelID & mail.labelMask > 0:
    766                 mailsToRemoveLabelFrom[messageID] = mail
    767 
    768         if len(mailsToRemoveLabelFrom) > 0 and notifyServer:
    769             self.mailMgr.RemoveLabels(mailsToRemoveLabelFrom.keys(), labelID)
    770         for mail in mailsToRemoveLabelFrom.itervalues():
    771             mail.labelMask = mail.labelMask & const.maxInt - labelID
    772             mail.labels = self.GetLabelMaskAsList(mail.labelMask)
    773 
    774         self.needToSaveHeaders = True
    775         sm.ScatterEvent('OnMailCountersUpdate')
    776 
    777     def ReplaceLabels(self, messageIDs, labelID, notifyServer = True):
    778         mailsToReplaceLabel = {}
    779         for messageID in messageIDs:
    780             mail = self.mailHeaders[messageID]
    781             if labelID != mail.labelMask:
    782                 mailsToReplaceLabel[messageID] = mail
    783 
    784         if len(mailsToReplaceLabel) > 0 and notifyServer:
    785             self.mailMgr.ReplaceLabels(mailsToReplaceLabel.keys(), labelID)
    786         for mail in mailsToReplaceLabel.itervalues():
    787             mail.labelMask = labelID
    788             mail.labels = self.GetLabelMaskAsList(mail.labelMask)
    789 
    790         self.needToSaveHeaders = True
    791         sm.ScatterEvent('OnMailCountersUpdate')
    792 
    793     def SaveChangesToDisk(self):
    794         if not self.needToSaveHeaders or self.isSaving:
    795             self.LogInfo('SaveChangesToDisk - nothing to do')
    796             return
    797         self.isSaving = True
    798         try:
    799             uthread.Lock(self)
    800             try:
    801                 self.LogInfo('SaveChangesToDisk - saving')
    802                 self.__WriteToHeaderFile('mail', self.mailHeaders)
    803                 self.needToSaveHeaders = False
    804             finally:
    805                 uthread.UnLock(self)
    806 
    807         finally:
    808             self.isSaving = False
    809 
    810     def TryCloseMailWindow(self):
    811         MailWindow.CloseIfOpen()
    812 
    813     def ClearMailCache(self):
    814         if prompt_player_fsd_dialog('AskClearMailCache', {}, uiconst.YESNO) == uiconst.ID_YES:
    815             self.TryCloseMailWindow()
    816             self.ClearCache()
    817 
    818     def ClearCache(self):
    819         uthread.Lock(self)
    820         try:
    821             self.__ClearHeaderCache()
    822             self.__ClearBodyCache()
    823         finally:
    824             uthread.UnLock(self)
    825 
    826     def __ClearHeaderCache(self):
    827         try:
    828             if os.path.exists(self.mailFileHeaders + '.dir'):
    829                 os.remove(self.mailFileHeaders + '.dir')
    830             if os.path.exists(self.mailFileHeaders + '.dat'):
    831                 os.remove(self.mailFileHeaders + '.dat')
    832             if os.path.exists(self.mailFileHeaders + '.bak'):
    833                 os.remove(self.mailFileHeaders + '.bak')
    834         except:
    835             self.cacheFileCorruption = True
    836             self.LogError('Error deleting header mail cache files. charid:', str(session.charid))
    837             raise UserError('MailCacheFileError')
    838 
    839         self.mailHeaders = {}
    840         self.labels = None
    841         self.needToSaveHeaders = False
    842         self.cacheFileCorruption = False
    843         self.mailSynced = 0
    844 
    845     def __ClearBodyCache(self):
    846         try:
    847             if os.path.exists(self.mailFileBodies + '.dir'):
    848                 os.remove(self.mailFileBodies + '.dir')
    849             if os.path.exists(self.mailFileBodies + '.dat'):
    850                 os.remove(self.mailFileBodies + '.dat')
    851             if os.path.exists(self.mailFileBodies + '.bak'):
    852                 os.remove(self.mailFileBodies + '.bak')
    853         except:
    854             self.cacheFileCorruption = True
    855             self.LogError('Error deleting body mail cache files. charid:', str(session.charid))
    856             raise UserError('MailCacheFileError')
    857 
    858         self.mailBodies = {}
    859         self.mailBodiesOrder = []
    860         self.cacheFileCorruption = False
    861         self.mailSynced = 0
    862 
    863     def __WriteToShelveFile(self, fileName, key, value):
    864         self.LogInfo('WriteToShelve', fileName, key)
    865         s = blue.os.GetWallclockTimeNow()
    866         key = str(key)
    867         try:
    868             fileHandle = shelve.open(fileName)
    869             fileHandle[key] = value
    870             fileHandle.close()
    871         except:
    872             self.LogError('Error writing to shelve file (', fileName, ', ', key, ',', value, ')')
    873             self.cacheFileCorruption = True
    874             self.TryCloseMailWindow()
    875             raise UserError('MailCacheFileError')
    876 
    877         self.LogInfo('Writing', blue.os.TimeDiffInMs(s, blue.os.GetWallclockTimeNow()))
    878 
    879     def __ReadFromShelveFile(self, fileName, key):
    880         self.LogInfo('ReadingFromShelve', fileName, key)
    881         s = blue.os.GetWallclockTimeNow()
    882         key = str(key)
    883         retValue = None
    884         try:
    885             fileHandle = shelve.open(fileName)
    886             if key in fileHandle:
    887                 retValue = fileHandle[key]
    888             fileHandle.close()
    889             return retValue
    890         except:
    891             self.LogError('Error reading from shelve file (', fileName, ', ', key, ')')
    892             self.cacheFileCorruption = True
    893             self.TryCloseMailWindow()
    894             raise UserError('MailCacheFileError')
    895 
    896         self.LogInfo('Reading', blue.os.TimeDiffInMs(s, blue.os.GetWallclockTimeNow()))
    897 
    898     def __WriteToHeaderFile(self, key, value):
    899         self.__WriteToShelveFile(self.mailFileHeaders, key, value)
    900 
    901     def __ReadFromHeaderFile(self, key):
    902         return self.__ReadFromShelveFile(self.mailFileHeaders, key)
    903 
    904     def __WriteToBodyFile(self, key, value):
    905         self.__WriteToShelveFile(self.mailFileBodies, key, value)
    906 
    907     def __ReadFromBodyFile(self, key):
    908         return self.__ReadFromShelveFile(self.mailFileBodies, key)
    909 
    910     def __DeleteFromBodyFile(self, key):
    911         self.LogInfo('DeletingFromShelve', key)
    912         s = blue.os.GetWallclockTimeNow()
    913         key = str(key)
    914         try:
    915             fileHandle = shelve.open(self.mailFileBodies)
    916             if key in fileHandle:
    917                 del fileHandle[key]
    918             fileHandle.close()
    919         except:
    920             self.LogError('Error deleting from body shelve file (', key, ')')
    921             self.cacheFileCorruption = True
    922             self.TryCloseMailWindow()
    923             raise UserError('MailCacheFileError')
    924 
    925         self.LogInfo('Deleting', blue.os.TimeDiffInMs(s, blue.os.GetWallclockTimeNow()))
    926 
    927     def IsFileCacheCorrupted(self):
    928         return self.cacheFileCorruption
    929 
    930     def SendMail(self, toCharacterIDs = [], toListID = None, toCorpOrAllianceID = None, title = '', body = '', isReplyTo = 0, isForwardedFrom = 0):
    931         self.LogInfo('SendMail', toCharacterIDs, toListID, toCorpOrAllianceID, isReplyTo, isForwardedFrom)
    932         if toListID is not None:
    933             myLists = sm.GetService('mailinglists').GetMyMailingLists()
    934             if toListID not in myLists:
    935                 raise UserError('EveMailCanOnlySendToOwnMailinglists')
    936         messageID = eveMisc.CSPAChargedAction('CSPAMailCheck', self.mailMgr, 'SendMail', toCharacterIDs, toListID, toCorpOrAllianceID, title, body, isReplyTo, isForwardedFrom)
    937         if messageID is None:
    938             return
    939         if len(self.mailBodiesOrder) > BODIES_IN_CACHE:
    940             try:
    941                 del self.mailBodies[self.mailBodiesOrder.pop(0)]
    942             except KeyError:
    943                 pass
    944 
    945         self.__WriteToBodyFile(messageID, body)
    946         self.mailBodies[messageID] = body
    947         self.mailBodiesOrder.append(messageID)
    948         if isReplyTo > 0:
    949             if isReplyTo in self.mailHeaders:
    950                 mail = self.mailHeaders[isReplyTo]
    951                 mail.replied = 1
    952                 mail.statusMask = mail.statusMask | const.mailStatusMaskReplied
    953                 self.needToSaveHeaders = True
    954         if isForwardedFrom > 0:
    955             if isForwardedFrom in self.mailHeaders:
    956                 mail = self.mailHeaders[isForwardedFrom]
    957                 mail.forwarded = 1
    958                 mail.statusMask = mail.statusMask | const.mailStatusMaskForwarded
    959                 self.needToSaveHeaders = True
    960         self.OnMailSent(messageID, session.charid, blue.os.GetWallclockTime() / const.MIN * const.MIN, toCharacterIDs, toListID, toCorpOrAllianceID, title, 0)
    961         sm.ScatterEvent('OnMailStatusUpdate', isReplyTo, isForwardedFrom)
    962         return messageID
    963 
    964     def SendMsgDlg(self, toCharacterIDs = [], toListID = None, toCorpOrAllianceID = [], isForwardedFrom = 0, isReplyTo = 0, subject = None, body = None):
    965         if session.inDetention:
    966             raise UserError('NotAllowedInDetention')
    967         sendPage = NewNewMessage.Open(windowID='NewMessageWindow', windowInstanceID=blue.os.GetWallclockTime(), toCharacterIDs=toCharacterIDs, toListID=toListID, toCorpOrAllianceID=toCorpOrAllianceID, isForwardedFrom=isForwardedFrom, isReplyTo=isReplyTo, subject=subject, body=body)
    968         return sendPage
    969 
    970     def GetReplyWnd(self, msg, all = 0, *args):
    971         if msg is None:
    972             return
    973         toListID = None
    974         toCorpOrAllianceID = []
    975         toCharacterIDs = []
    976         senders = []
    977         if all:
    978             if msg.toCharacterIDs is not None:
    979                 toCharacterIDs = msg.toCharacterIDs[:]
    980                 if session.charid in toCharacterIDs:
    981                     toCharacterIDs.remove(session.charid)
    982             toCharacterIDs.append(msg.senderID)
    983             if msg.toCorpOrAllianceID is not None:
    984                 toCorpOrAllianceID = [msg.toCorpOrAllianceID]
    985             toListID = msg.toListID
    986         else:
    987             toCharacterIDs = [msg.senderID]
    988         receiversText = self.GetReceiverText(msg)
    989         newmsg = self.SendMsgDlg(toCharacterIDs=toCharacterIDs, toListID=toListID, toCorpOrAllianceID=toCorpOrAllianceID, isReplyTo=msg.messageID)
    990         newmsgText = self.GetReplyMessage(msg)
    991         if msg.subject.startswith(localization.GetByLabel('UI/Mail/GenericInboxRe')):
    992             newSubjectText = msg.subject
    993         else:
    994             newSubjectText = '%s %s' % (localization.GetByLabel('UI/Mail/GenericInboxRe'), msg.subject)
    995         newmsg.sr.subjecField.SetValue(newSubjectText)
    996         newmsg.messageedit.SetValue(newmsgText, scrolltotop=1)
    997 
    998     def GetReceiverText(self, mail, format = 0):
    999         receiversChar = mail.toCharacterIDs
   1000         receiversMaillistID = mail.toListID
   1001         receiversCorp = mail.toCorpOrAllianceID or ''
   1002         receiversText = ''
   1003         if receiversMaillistID is not None:
   1004             name = sm.GetService('mailinglists').GetDisplayName(receiversMaillistID)
   1005             if format:
   1006                 name = localization.GetByLabel('UI/Map/StarMap/lblBoldName', name=name)
   1007                 receiversText += '%s, ' % name
   1008         if receiversCorp:
   1009             name = cfg.eveowners.Get(receiversCorp).ownerName
   1010             toAdd = '%s' % name
   1011             if format:
   1012                 toAdd = localization.GetByLabel('UI/Map/StarMap/lblBoldName', name=toAdd)
   1013             receiversText += '%s, ' % toAdd
   1014         charsIDs = [ charID for charID in receiversChar if charID not in (-1, None) ]
   1015         self.PrimeOwners(charsIDs)
   1016         charNameList = []
   1017         for each in charsIDs:
   1018             name = cfg.eveowners.Get(each).ownerName
   1019             text = '<a href="showinfo:1377//%s">%s</a>,  ' % (each, name)
   1020             charNameList.append((name.lower(), text))
   1021 
   1022         charNameList = SortListOfTuples(charNameList)
   1023         for each in charNameList:
   1024             receiversText += each
   1025 
   1026         return receiversText
   1027 
   1028     def GetMailText(self, node):
   1029         msg = node
   1030         body = self.GetBody(msg.messageID)
   1031         subject = msg.subject
   1032         senderID = msg.senderID
   1033         senderName = msg.senderName
   1034         date = msg.sentDate
   1035         receiversText = self.GetReceiverText(msg, format=1)
   1036         if msg.statusMask & const.mailStatusMaskAutomated == const.mailStatusMaskAutomated:
   1037             senderText = localization.GetByLabel('UI/Map/StarMap/lblBoldName', name=senderName)
   1038         else:
   1039             senderText = '<a href="showinfo:1377//%s">%s</a>' % (senderID, senderName)
   1040         txt = localization.GetByLabel('UI/Mail/MailText', subject=subject, sender=senderText, date=FmtDate(date, 'ls'), receivers=receiversText, body=body)
   1041         return txt
   1042 
   1043     def GetReplyMessage(self, msg):
   1044         if msg is None:
   1045             return ''
   1046         receiversText = self.GetReceiverText(msg)
   1047         if msg.statusMask & const.mailStatusMaskAutomated == const.mailStatusMaskAutomated:
   1048             senderText = localization.GetByLabel('UI/Map/StarMap/lblBoldName', name=msg.senderName)
   1049         else:
   1050             senderText = '<a href="showinfo:1377//%s">%s</a>' % (msg.senderID, cfg.eveowners.Get(msg.senderID).ownerName)
   1051         body = self.GetBody(msg.messageID)
   1052         newmsgText = '<br><br>%(line)s<br>%(subject)s<br>%(from)s: %(senders)s<br>%(sent)s: %(date)s<br>%(to)s: %(receivers)s<br><br>%(body)s' % {'line': '--------------------------------',
   1053          'subject': msg.subject,
   1054          'from': localization.GetByLabel('UI/Mail/From'),
   1055          'senders': senderText,
   1056          'sent': localization.GetByLabel('UI/Mail/Sent'),
   1057          'date': FmtDate(msg.sentDate, 'ls'),
   1058          'to': localization.GetByLabel('UI/Mail/To'),
   1059          'receivers': receiversText,
   1060          'body': body}
   1061         maxLen = const.mailMaxBodySize * 0.9
   1062         if len(newmsgText) > maxLen:
   1063             while len(newmsgText) > maxLen:
   1064                 try:
   1065                     brIndex = newmsgText.rindex('<br>', int(maxLen * 0.7))
   1066                     newmsgText = newmsgText[:brIndex]
   1067                 except ValueError:
   1068                     lastIndex = int(len(newmsgText) * 0.9)
   1069                     newmsgText = newmsgText[:lastIndex]
   1070 
   1071             newmsgText += '<br>...'
   1072         return newmsgText
   1073 
   1074     def GetForwardWnd(self, msg):
   1075         if msg is None:
   1076             return
   1077         newmsg = self.SendMsgDlg(isForwardedFrom=msg.messageID)
   1078         newmsgText = self.GetReplyMessage(msg)
   1079         newmsg.sr.subjecField.SetValue('%s %s' % ('FW:', msg.subject))
   1080         newmsg.messageedit.SetValue(newmsgText, scrolltotop=1)
   1081 
   1082     def OnMailSent(self, messageID, senderID, sentDate, toCharacterIDs, toListID, toCorpOrAllianceID, title, statusMask):
   1083         uthread.Lock(self)
   1084         try:
   1085             if messageID in self.mailHeaders:
   1086                 return
   1087             labelMask = 0
   1088             read = False
   1089             if session.charid in toCharacterIDs:
   1090                 labelMask = const.mailLabelInbox
   1091             if senderID == session.charid:
   1092                 if labelMask == 0 and toCorpOrAllianceID is None and toListID is None:
   1093                     read = True
   1094                     statusMask = statusMask | 1
   1095                 labelMask = labelMask | const.mailLabelSent
   1096             if toCorpOrAllianceID is not None:
   1097                 if idCheckers.IsCorporation(toCorpOrAllianceID):
   1098                     labelMask = labelMask | const.mailLabelCorporation
   1099                 else:
   1100                     labelMask = labelMask | const.mailLabelAlliance
   1101             self.mailHeaders[messageID] = utillib.KeyVal(messageID=messageID, senderID=senderID, toCharacterIDs=toCharacterIDs, toListID=toListID, toCorpOrAllianceID=toCorpOrAllianceID, subject=title, sentDate=sentDate, read=read, replied=False, forwarded=False, trashed=False, statusMask=statusMask, labelMask=labelMask, labels=self.GetLabelMaskAsList(labelMask))
   1102             if statusMask & const.mailStatusMaskAutomated > 0 and senderID == toListID:
   1103                 self.mailHeaders[messageID].senderName = sm.GetService('mailinglists').GetDisplayName(senderID)
   1104             else:
   1105                 try:
   1106                     self.mailHeaders[messageID].senderName = cfg.eveowners.Get(senderID).name
   1107                 except IndexError:
   1108                     self.mailHeaders[messageID].senderName = localization.GetByLabel('UI/Generic/Unknown')
   1109 
   1110             self.needToSaveHeaders = True
   1111             if not read:
   1112                 self.OnNewMailReceived(self.mailHeaders[messageID])
   1113         finally:
   1114             uthread.UnLock(self)
   1115 
   1116     def OnMailDeleted(self, messageIDs):
   1117         uthread.Lock(self)
   1118         try:
   1119             for messageID in messageIDs:
   1120                 if messageID in self.mailHeaders:
   1121                     del self.mailHeaders[messageID]
   1122                     self.needToSaveHeaders = True
   1123                 if messageID in self.mailBodies:
   1124                     del self.mailBodies[messageID]
   1125                 if messageID in self.mailBodiesOrder:
   1126                     self.mailBodiesOrder.remove(messageID)
   1127                 self.__DeleteFromBodyFile(messageID)
   1128 
   1129             sm.ScatterEvent('OnMailTrashedDeleted', None, 1)
   1130         finally:
   1131             uthread.UnLock(self)
   1132 
   1133     def OnMailUpdatedByExternal(self, messageIDs, is_read, new_label):
   1134         if is_read is not None:
   1135             if is_read:
   1136                 self.MarkMessagesAsRead(messageIDs, notifyServer=False)
   1137             else:
   1138                 self.MarkMessagesAsUnread(messageIDs, notifyServer=False)
   1139         if new_label is not None:
   1140             self.ReplaceLabels(messageIDs, new_label, notifyServer=False)
   1141 
   1142     def OnMailUndeleted(self, messageIDs):
   1143         self.SyncMail()
   1144         sm.ScatterEvent('OnNewMailReceived')
   1145 
   1146     def OnMailTrashed(self, messageIDs):
   1147         self._RefreshTrashedStatusWhenMovingToTrash(messageIDs)
   1148 
   1149     def OnMailRestored(self, messageIDs):
   1150         self._RefreshTrashedStatusWhenMovingFromTrash(messageIDs)
   1151 
   1152     def OnNewMailReceived(self, msg, *args):
   1153         self.SetBlinkTabState(True)
   1154         self.SetBlinkNeocomState(True)
   1155         sm.ScatterEvent('OnNewMailReceived')
   1156         mailSettings = self.GetMailSettings()
   1157         if mailSettings.GetSingleValue(cSettings.MAIL_BLINK_NEOCOM, True):
   1158             sm.GetService('neocom').Blink('mail', GetByLabel('UI/Neocom/Blink/NewMailReceived'))
   1159         if mailSettings.GetSingleValue(cSettings.MAIL_SHOW_POPUP, True):
   1160             self.GetMailNotification(msg)
   1161 
   1162     def OnLabelsCreatedByExternal(self, label):
   1163         self.AddLabel(label.labelID, label.name, label.color)
   1164         sm.ScatterEvent('OnMyLabelsChanged', 'mail_labels', label.labelID)
   1165 
   1166     def GetMailNotification(self, msg):
   1167         notificationData = NewMailFormatter.MakeData(msg)
   1168         senderIDForNotification = msg.senderID
   1169         if msg.toListID and msg.toListID == msg.senderID:
   1170             senderIDForNotification = None
   1171         sm.ScatterEvent('OnNotificationReceived', 123, notificationConst.notificationTypeNewMailFrom, senderIDForNotification, blue.os.GetWallclockTime(), data=notificationData)
   1172 
   1173     def OnClickingMailPopup(self, popup, msg, *args):
   1174         self.OnOpenPopupMail(msg)
   1175         if popup and not popup.destroyed:
   1176             popup.CloseNotification()
   1177 
   1178     def GetMailPopupOnCharacterLogin(self, mailCount):
   1179         blue.pyos.synchro.SleepWallclock(7000)
   1180         if mailCount < 1:
   1181             return
   1182         notificationData = MailSummaryFormatter.MakeData(mailCount)
   1183         sm.GetService('notificationSvc').MakeAndScatterNotification(type=notificationConst.notificationTypeMailSummary, data=notificationData)
   1184 
   1185     def OnClickingPopup(self, popup, *args):
   1186         sm.GetService('cmd').OpenMail()
   1187         popup.CloseNotification()
   1188 
   1189     def OnOpenPopupMail(self, msg, *args):
   1190         wndName = 'mail_readingWnd_%s' % msg.messageID
   1191         wnd = MailReadingWnd.Open(windowInstanceID=wndName, mail=msg, msgID=msg.messageID, txt='', toolbar=1, trashed=msg.trashed, type=const.mailTypeMail)
   1192         if not msg.read:
   1193             sm.ScatterEvent('OnMailStatusUpdate', None, None, [msg.messageID])
   1194         if wnd is not None:
   1195             wnd.Maximize()
   1196             blue.pyos.synchro.SleepWallclock(1)
   1197             wnd.SetText(self.GetMailText(msg))
   1198 
   1199     def CheckNewMessages_thread(self, mailCount):
   1200         blue.pyos.synchro.SleepSim(3000)
   1201         endTime = blue.os.GetSimTime() + 10 * const.SEC
   1202         while blue.os.GetSimTime() < endTime:
   1203             if getattr(session, 'charid', None):
   1204                 break
   1205             blue.pyos.synchro.SleepSim(1000)
   1206 
   1207         if getattr(session, 'charid', None) is None:
   1208             log.LogWarn('Mail: Waited for 13 seconds to display new messages, gave up')
   1209             return
   1210         self.BlinkNeocomIfNeeded(mailCount)
   1211 
   1212     def BlinkNeocomIfNeeded(self, mailCount):
   1213         shouldBlink = 0
   1214         mailSettings = self.GetMailSettings()
   1215         if mailCount:
   1216             self.SetBlinkNeocomState(True)
   1217             if mailSettings.GetSingleValue(cSettings.MAIL_BLINK_NEOCOM, True):
   1218                 self.SetBlinkTabState(True)
   1219                 shouldBlink = 1
   1220             if mailSettings.GetSingleValue(cSettings.MAIL_SHOW_POPUP, True):
   1221                 self.GetMailPopupOnCharacterLogin(mailCount)
   1222             if mailSettings.GetSingleValue(cSettings.MAIL_BLINK_TAB, True):
   1223                 self.SetBlinkTabState(True)
   1224         if shouldBlink:
   1225             sm.GetService('neocom').Blink('mail', GetByLabel('UI/Neocom/Blink/NewMailReceived'))
   1226 
   1227     def CheckLabelName(self, name, *args):
   1228         name = name.strip()
   1229         myLabelNames = [ label.name for label in self.GetAllLabels(assignable=0).values() ]
   1230         if name in myLabelNames:
   1231             return localization.GetByLabel('UI/Mail/LabelNameTaken')
   1232 
   1233     def RenameLabelFromUI(self, labelID):
   1234         ret = utilWindows.NamePopup(localization.GetByLabel('UI/Mail/LabelName'), localization.GetByLabel('UI/Mail/LabelTypeNew'), maxLength=const.mailMaxLabelSize, validator=self.CheckLabelName)
   1235         if ret is None:
   1236             return
   1237         name = ret
   1238         name = name.strip()
   1239         if name:
   1240             self.EditLabel(labelID, name=name)
   1241 
   1242     def ChangeLabelColorFromUI(self, color, labelID):
   1243         allLabels = self.GetAllLabels()
   1244         label = allLabels.get(labelID, None)
   1245         if label is None:
   1246             return
   1247         if color != label.color:
   1248             self.EditLabel(labelID, color=color)
   1249 
   1250     def GetSwatchColors(self, *args):
   1251         return swatchColors
   1252 
   1253     def DeleteLabelFromUI(self, labelID, labelName):
   1254         if prompt_player_fsd_dialog('DeleteMailLabel', {'labelName': labelName}, uiconst.YESNO) == uiconst.ID_YES:
   1255             self.DeleteLabel(labelID)
   1256 
   1257     def ShouldTabBlink(self, *args):
   1258         return self.blinkTab
   1259 
   1260     def SetBlinkTabState(self, state = False):
   1261         self.blinkTab = state
   1262 
   1263     def ShouldNeocomBlink(self, *args):
   1264         return self.blinkNeocom
   1265 
   1266     def SetBlinkNeocomState(self, state = False):
   1267         self.blinkNeocom = state
   1268 
   1269     def GetAssignColorWnd(self, labelID, doneCallBack = None, doneArgs = (), width = 104, height = 74, *args):
   1270         colorpar = MailAssignColorWnd(name='colorpar', parent=uicore.layer.menu, idx=0, align=uiconst.TOPLEFT, pos=(0,
   1271          0,
   1272          width,
   1273          height))
   1274         colorpar.Startup(labelID, doneCallBack, doneArgs)
   1275 
   1276     def GetMailSettings(self):
   1277         self.SetMailSettingsIfNeeded()
   1278         return self.mailSettingObject
   1279 
   1280     def SetMailSettingsIfNeeded(self):
   1281         if self.mailSettingObject is None:
   1282             self.mailSettingObject = ''
   1283             yamlString = self.FetchMailSettingsToServer()
   1284             self.mailSettingObject = CharacterSettingsObject(yamlString)
   1285             with ExceptionEater('Failed to migrate settings'):
   1286                 self.TryMigrateSettings(self.mailSettingObject)
   1287 
   1288     def FetchMailSettingsToServer(self):
   1289         yamlString = self.characterSettings.Get('mailSettings')
   1290         return yamlString
   1291 
   1292     def SaveMailSettingsOnServer(self, newSettingsObject):
   1293         isSaveNeeded = self.mailSettingObject.IsSaveNeeded(newSettingsObject.allSettings)
   1294         if not isSaveNeeded:
   1295             return
   1296         self.mailSettingObject.UpdateSettings(newSettingsObject.allSettings)
   1297         newYamlString = self.mailSettingObject.GetYamlStringForServer()
   1298         self.characterSettings.Save('mailSettings', newYamlString)
   1299 
   1300     def TryMigrateSettings(self, settingsObject):
   1301         if settingsObject.GetSingleValue('settingMigrationDone1', False):
   1302             return
   1303         for settingKey in (cSettings.MAIL_BLINK_NEOCOM,
   1304          cSettings.MAIL_SHOW_POPUP,
   1305          cSettings.MAIL_BLINK_TAB,
   1306          cSettings.MAIL_GET_SEARCH_WND):
   1307             oldSetting = settings.user.ui.Get(settingKey, True)
   1308             settingsObject.SetSingleValue(settingKey, oldSetting)
   1309 
   1310         oldMailPerPage = settings.user.ui.Get(cSettings.MAILS_PER_PAGE, 30)
   1311         settingsObject.SetSingleValue(cSettings.MAILS_PER_PAGE, oldMailPerPage)
   1312         allNotificationGroups = notificationGroupNamePaths.keys()
   1313         for newSettingKey, oldSettingKey in ((cSettings.NOTIFICATIONS_GROUPS_DONT_BLINK_NEOCOM, 'notification_blinkNecom'), (cSettings.NOTIFICATIONS_GROUPS_DONT_BLINK_TAB, 'notification_blinkTab')):
   1314             oldSetting = settings.user.ui.Get(oldSettingKey, True)
   1315             if oldSetting:
   1316                 newValue = []
   1317             else:
   1318                 newValue = allNotificationGroups
   1319             settingsObject.UpdateSettingListWithNewValues(newSettingKey, newValue)
   1320 
   1321         settingsObject.SetSingleValue('settingMigrationDone1', True)
   1322         newYamlString = settingsObject.GetYamlStringForServer()
   1323         self.characterSettings.Save('mailSettings', newYamlString)