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

xmppchatsvc.py (80370B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\xmppchatclient\xmppchatsvc.py
      2 import json
      3 import logging
      4 import socket
      5 from player_messaging.client.ui_message import prompt_player_fsd_dialog
      6 from xml.sax.saxutils import escape, quoteattr
      7 import stackless
      8 from stackless_tracing.attributes import SERVICE_NAME, SERVICE_NAME_PREFIX
      9 import yaml
     10 from collections import OrderedDict
     11 import gametime
     12 import localization
     13 import locks
     14 import uthread2
     15 import carbonui.const as uiconst
     16 import monolithhoneycomb_events as mhe
     17 from bannedwords.client import bannedwords
     18 from carbon.common.script.util.format import FmtDate
     19 from monolithhoneycomb_events.events import CHAT_CLIENT_LOGIN, CHAT_CLIENT_HANDLE_LOGGED_IN, CHAT_CLIENT_HANDLE_CONNECTION_LOST, CHAT_CLIENT_CONNECTION_OBJECT_CREATED
     20 from carbon.common.script.sys import service
     21 from chatutil import IsInvite, GetErrorCode, GetUsersFromResponse, GetChannelCategory, GetInviteReason, GetInviteSender, GetChannelDifferentiator, UpdateChatFilterVariables, IsDecline, GetDeclineReason, GetDeclineSender, GetInvitePassword, TruncateVeryLongText, GetCharacterBannnedExpiry, GetErrorType, GetErrorTextElement, InvalidChatChannelNameError, InvalidSessionChatChannelNameError
     22 from chatutil.channelnames import IsValidChannelName, GetSessionBasedChannelName
     23 from eve.common.script.sys.idCheckers import IsNPC, IsCharacter, IsCorporation, IsAlliance
     24 from eveexceptions import UserError
     25 from eveexceptions.exceptionEater import ExceptionEater
     26 from eveprefs import prefs
     27 from uihighlighting import UiHighlightDirections
     28 from xmpp import XmppConnection
     29 import xmppchatclient.const as xmppchatConst
     30 from xmppchatclient.chatinvitewindow import PresentChatInviteWindow
     31 from xmppchatclient.xmppchatpassword import ChannelPasswordWindow
     32 from xmppchatclient.xmppchatwindow import XmppChatWindow
     33 from chat.common.util import is_local_chat_suppressed
     34 from chat.common.const import MIGRATED_CHAT_CATEGORIES
     35 from collections import defaultdict
     36 from globalConfig import GetEjabberdAddress
     37 logger = logging.getLogger('xmppchat')
     38 SESSION_CHANGE_CATEGORIES = OrderedDict([('solarsystemid2', xmppchatConst.CATEGORY_LOCAL),
     39  ('allianceid', xmppchatConst.CATEGORY_ALLIANCE),
     40  ('corpid', xmppchatConst.CATEGORY_CORP),
     41  ('fleetid', xmppchatConst.CATEGORY_FLEET),
     42  ('warfactionid', xmppchatConst.CATEGORY_FACTION)])
     43 
     44 class XmppChatService(service.Service):
     45     __guid__ = 'svc.XmppChat'
     46     __displayname__ = 'Xmpp Chat Service'
     47     __exportedcalls__ = {'AskYesNoQuestion': [service.ROLE_SERVICE]}
     48     __notifyevents__ = ['OnSessionChanged',
     49      'OnDisconnect',
     50      'OnCharacterPokedByGM',
     51      'ProcessSessionReset',
     52      'OnSessionReset',
     53      'OnCharacterPermanentlyBanned',
     54      'OnCharacterTemporarilyBanned',
     55      'OnCharacterUnbanned']
     56 
     57     def __init__(self):
     58         super(XmppChatService, self).__init__()
     59         self.verbose = prefs.GetValue('xmppChatVerbose', False)
     60         self._has_local_chat_migrated = None
     61         self.xmppConnection = None
     62         self.hostname = None
     63         self.ejabberd_address = None
     64         self.messageWindows = {}
     65         self.groupChatWindows = {}
     66         self.myName = None
     67         self.charsToPrime = set()
     68         self.myPendingPrivateChats = {}
     69         self.myPendingInvites = {}
     70         self.pendingJoins = []
     71         self.outstandingJoins = {}
     72         self.channelJoinsInProgress = set()
     73         self.isReconnecting = False
     74         self.chatFilters = None
     75         self.userData = {}
     76         self.translatedMsgText = localization.GetByLabel('UI/Common/Message')
     77         self.hasLoggedInOnce = False
     78         self.memberRolesByChannels = defaultdict(dict)
     79         self.isDoingLogoff = False
     80         self._threads = []
     81         self.banExpiry = xmppchatConst.BAN_EXPIRY_STATUS_NONE
     82 
     83     def Run(self, memStream = None):
     84         self.state = service.SERVICE_RUNNING
     85 
     86     def UpdateLocalChatMigrated(self, local_chat_migrated):
     87         if local_chat_migrated == self._has_local_chat_migrated:
     88             return
     89         self._has_local_chat_migrated = local_chat_migrated
     90         if not session.charid:
     91             return
     92         channelName = GetSessionBasedChannelName(xmppchatConst.CATEGORY_LOCAL, session.solarsystemid2)
     93         if self._has_local_chat_migrated:
     94             self.CloseChannelWindow(channelName)
     95         else:
     96             self.GetOrCreateGroupChatWindow(channelName)
     97 
     98     def _IsCategoryMigrated(self, category):
     99         return self._has_local_chat_migrated and category in MIGRATED_CHAT_CATEGORIES
    100 
    101     def HandleLoggingIn(self):
    102         uthread2.start_tasklet(self.Login)
    103         uthread2.start_tasklet(self.InitChatFilters)
    104 
    105     def IsCharacterBanned(self):
    106         return self.banExpiry == xmppchatConst.BAN_EXPIRY_STATUS_NEVER or gametime.GetWallclockTime() <= self.banExpiry
    107 
    108     def OnCharacterPermanentlyBanned(self):
    109         self._CharacterBanned(xmppchatConst.BAN_EXPIRY_STATUS_NEVER)
    110 
    111     def OnCharacterTemporarilyBanned(self, banExpiry):
    112         self._CharacterBanned(banExpiry)
    113 
    114     def _CharacterBanned(self, banExpiry):
    115         self._SetBanExpiry(banExpiry)
    116         for window in self.groupChatWindows.values():
    117             window.OnCharacterBannedNotify()
    118 
    119         prompt_player_fsd_dialog('CharacterBannedFromChat')
    120 
    121     def _SetBanExpiry(self, banExpiry):
    122         self.banExpiry = banExpiry
    123 
    124     def OnCharacterUnbanned(self):
    125         self._SetBanExpiry(xmppchatConst.BAN_EXPIRY_STATUS_NONE)
    126 
    127     def GetCharacterBannedMessage(self):
    128         if self.banExpiry == xmppchatConst.BAN_EXPIRY_STATUS_NEVER:
    129             return localization.GetByLabel('UI/Chat/ChatSvc/CharacterPermanentlyBanned')
    130         if self.banExpiry > 0:
    131             return localization.GetByLabel('UI/Chat/ChatSvc/CharacterTemporarilyBanned', datetime=FmtDate(self.banExpiry, 'll'))
    132         raise RuntimeError('Character is not banned')
    133 
    134     def OnSessionChanged(self, isremote, sess, changes):
    135         if 'charid' in changes and changes['charid'][1] is not None:
    136             self.HandleLoggingIn()
    137         channelsToLeave = []
    138         for category, prefix in SESSION_CHANGE_CATEGORIES.iteritems():
    139             if self._IsCategoryMigrated(category):
    140                 continue
    141             if category in changes:
    142                 old, _new = changes[category]
    143                 if old:
    144                     channelName = GetSessionBasedChannelName(prefix, old)
    145                     channelsToLeave.append(channelName)
    146 
    147         uthread2.start_tasklet(self._LeaveChannels, channelsToLeave)
    148         uthread2.start_tasklet(self._HandleLocalSuppressedSessionChange, changes)
    149 
    150     def ProcessSessionReset(self):
    151         self.isDoingLogoff = True
    152 
    153     def OnSessionReset(self):
    154         self.xmppConnection.Disconnect()
    155         self.xmppConnection = None
    156         self.messageWindows = {}
    157         self.groupChatWindows = {}
    158         self.myName = None
    159         self.charsToPrime = set()
    160         self.myPendingPrivateChats = {}
    161         self.myPendingInvites = {}
    162         self.pendingJoins = []
    163         self.outstandingJoins = {}
    164         self.channelJoinsInProgress = set()
    165         self.chatFilters = None
    166         self.userData = {}
    167         self.memberRolesByChannels = defaultdict(dict)
    168         self.pendingJoins = []
    169         threads = self._threads[:]
    170         self._threads = []
    171         for t in threads:
    172             t.kill()
    173 
    174         self.isDoingLogoff = False
    175         self.banExpiry = xmppchatConst.BAN_EXPIRY_STATUS_NONE
    176 
    177     def _HandleLocalSuppressedSessionChange(self, changes):
    178         if 'solarsystemid2' in changes:
    179             newSolarSystemID = changes['solarsystemid2'][1]
    180             if is_local_chat_suppressed(newSolarSystemID):
    181                 self.GetOrCreateGroupChatWindow(xmppchatConst.CATEGORY_LOCAL, setWindowsChannel=False)
    182                 channelName = GetSessionBasedChannelName(xmppchatConst.CATEGORY_LOCAL_SUPPRESSED, newSolarSystemID)
    183                 self.JoinChannel(channelName)
    184 
    185     def OnDisconnect(self, reason = 0, msg = ''):
    186         if self.xmppConnection:
    187             self.xmppConnection.Disconnect()
    188 
    189     def TryJoinPlayerChannelsFromOldSettings_Temp(self):
    190         if settings.char.ui.Get('chat_OldChannelsMigrated', False):
    191             return
    192         oldChannels = settings.char.ui.Get('lscengine_mychannels', [])
    193         channelsToAttemptToJoin = []
    194         for channelID in oldChannels:
    195             if not isinstance(channelID, int):
    196                 continue
    197             if 0 <= channelID <= xmppchatConst.SYSTEM_CHANNEL_MAX_CHANNELID:
    198                 continue
    199             newChannelID = '%s_%s' % (xmppchatConst.CATEGORY_PLAYER, channelID)
    200             channelsToAttemptToJoin.append(newChannelID)
    201 
    202         if channelsToAttemptToJoin:
    203             uthread2.start_tasklet(self._JoinChannels, channelsToAttemptToJoin)
    204         settings.char.ui.Set('chat_OldChannelsMigrated', True)
    205 
    206     def _LeaveChannels(self, channelsToLeave):
    207         for each in channelsToLeave:
    208             self.LeaveChannel(each)
    209 
    210     def _JoinChannels(self, channelsToJoin):
    211         for each in channelsToJoin:
    212             if not self.JoinChannel(each):
    213                 self._RemoveWindowForChannel(each)
    214 
    215     def _RemoveWindowForChannel(self, each):
    216         window = self.groupChatWindows.pop(each, None)
    217         if window:
    218             window.access_lost = True
    219             window.Close()
    220 
    221     def Login(self):
    222         self._CreateConnection()
    223         self.myName = self.GetUserName()
    224         logger.info('Logging in as %s on %s', self.myName, self.xmppConnection.ejabberd_address)
    225         sm.GetService('settings').LoadCharSettingsIfNeeded()
    226         channelListInSettings = settings.char.ui.Get('chatchannels', [])
    227         self.CreateSessionChannelWindows()
    228         self._RestoreChannelList(channelListInSettings)
    229         while True:
    230             try:
    231                 self.xmppConnection.Connect(self.myName, 'eveclient')
    232                 self.hasLoggedInOnce = True
    233                 self.send_honey_event(CHAT_CLIENT_LOGIN)
    234                 break
    235             except socket.error as e:
    236                 msg = 'Error connecting to {0}'.format(self.xmppConnection.ejabberd_address)
    237                 logger.exception(msg)
    238                 ret = prompt_player_fsd_dialog('RetryChatServerConnection', {'server': self.xmppConnection.ejabberd_address,
    239                  'port': self.xmppConnection.port}, uiconst.YESNO)
    240                 if ret != uiconst.ID_YES:
    241                     self._DisableChat()
    242                     self.send_honey_event(CHAT_CLIENT_LOGIN, {'error': True,
    243                      'eve.xmpp.connection.retry': False})
    244                     return
    245                 self.send_honey_event(CHAT_CLIENT_LOGIN, {'error': True,
    246                  'eve.xmpp.connection.retry': True})
    247 
    248     def _DisableChat(self):
    249         for window in self.groupChatWindows.values():
    250             try:
    251                 msg = localization.GetByLabel('UI/Chat/ChatSvc/NoChatServer', server=self.xmppConnection.ejabberd_address, port=self.xmppConnection.port)
    252                 window.Receive(const.ownerSystem, msg)
    253             except Exception:
    254                 pass
    255 
    256     def _CreateConnection(self):
    257         self.hostname = self.XmppChatMgr.Hostname()
    258         self.ejabberd_address = self.get_ejabberd_address()
    259         if self.xmppConnection is not None:
    260             self.xmppConnection.hostname = self.hostname
    261             self.xmppConnection.ejabberd_address = self.ejabberd_address
    262             return
    263         if self.ejabberd_address is None:
    264             self.send_honey_event(CHAT_CLIENT_CONNECTION_OBJECT_CREATED, {'error': True})
    265             logger.exception('No ejabberd address configured for client.')
    266             return
    267         logger.info('xmppchatsvc._CreateConnection::ejabberd_address={}, hostname={}'.format(self.ejabberd_address, self.hostname))
    268         self.xmppConnection = XmppConnection(hostname=self.hostname, ejabberd_address=self.ejabberd_address)
    269         self.xmppConnection.handle_message = self.HandleXmppMessage
    270         self.xmppConnection.handle_logged_in = self.HandleXmppLoggedIn
    271         self.xmppConnection.handle_connection_lost = self.HandleXmppConnectionLost
    272         self.xmppConnection.handle_presence = self.HandleXmppPresence
    273         self.xmppConnection.handle_stream_error = self.HandleStreamError
    274         self.xmppConnection.get_password = self.GetAuthenticationToken
    275         self.xmppConnection.verbose = self.verbose
    276         self.send_honey_event(CHAT_CLIENT_CONNECTION_OBJECT_CREATED, {'eve.xmpp.connection.created.id': id(self.xmppConnection)})
    277 
    278     def get_ejabberd_address(self):
    279         address = GetEjabberdAddress(sm.GetService('machoNet'))
    280         if address is not None and len(address) > 0:
    281             return address
    282         return self.XmppChatMgr.GetDeprecatedPrefsFallback()
    283 
    284     def GetUserName(self):
    285         return self.GetUserNameFromCharId(session.charid)
    286 
    287     def GetAuthenticationToken(self):
    288         token = sm.RemoteSvc('chatAuthenticationService').GetAuthenticationToken()
    289         if token:
    290             return token
    291         else:
    292             logger.debug("Can't get token from authentication service - are you on a local server?")
    293             return 'ejabberd'
    294 
    295     def SendGroupChat(self, group, messageText):
    296         sm.ScatterEvent('OnClientEvent_ChatMessageSent')
    297         if not self.xmppConnection or not self.xmppConnection.IsConnected():
    298             self.Login()
    299         if not self.xmppConnection or not self.xmppConnection.IsConnected():
    300             logger.warning("Can't send message when not connected")
    301             return
    302         if group in self.myPendingPrivateChats:
    303             other, isInvited = self.myPendingPrivateChats[group]
    304             if not isInvited:
    305                 self.myPendingPrivateChats[group] = (other, True)
    306                 roomName = self.GetFullRoomName(group)
    307                 self.xmppConnection.Invite(self.GetUserNameFromCharId(other), roomName, 'Private chat')
    308         receiver = self.GetFullRoomName(group)
    309         messageText = TruncateVeryLongText(messageText)
    310         return self.xmppConnection.GroupChat(receiver, messageText)
    311 
    312     def _JoinSystemChannels(self):
    313         channelsToJoin = None
    314         while channelsToJoin is None:
    315             if self.verbose:
    316                 logger.debug('Calling ResyncSystemChannelAccess on proxy')
    317             channelsToJoin = self.XmppChatMgr.ResyncSystemChannelAccess()
    318             if not channelsToJoin:
    319                 uthread2.sleep(5)
    320 
    321         categoriesToJoin = set()
    322         for each in channelsToJoin:
    323             self.JoinChannel(each)
    324             with ExceptionEater('_JoinSystemChannels'):
    325                 category = GetChannelCategory(each)
    326                 if category in (xmppchatConst.CATEGORY_LOCAL_SUPPRESSED,
    327                  xmppchatConst.CATEGORY_WORMHOLE,
    328                  xmppchatConst.CATEGORY_NULLSEC,
    329                  xmppchatConst.CATEGORY_TRIGLAVIAN):
    330                     category = xmppchatConst.CATEGORY_LOCAL
    331                 categoriesToJoin.add(category)
    332 
    333         uthread2.start_tasklet(self._JoinChannels, channelsToJoin)
    334         windowsToClose = set(SESSION_CHANGE_CATEGORIES.values()) - categoriesToJoin
    335         for eachWindowName in windowsToClose:
    336             self._RemoveWindowForChannel(eachWindowName)
    337 
    338     def HandleXmppLoggedIn(self):
    339         self.send_honey_event(CHAT_CLIENT_HANDLE_LOGGED_IN)
    340         logger.info('Logged in to %s at %s as %s', self.xmppConnection.hostname, self.xmppConnection.ejabberd_address, self.xmppConnection.username)
    341         channelsToJoin = self.pendingJoins
    342         self.pendingJoins = []
    343         for window in self.groupChatWindows.values():
    344             uthread2.start_tasklet(window.SetCaptionAsDisplayName)
    345             channel = window.receiver
    346             category = GetChannelCategory(channel)
    347             if category in [xmppchatConst.CATEGORY_PRIVATE,
    348              xmppchatConst.CATEGORY_PLAYER,
    349              xmppchatConst.CATEGORY_BOT,
    350              xmppchatConst.CATEGORY_SYSTEM]:
    351                 channelsToJoin.append(channel)
    352 
    353         self._threads.append(uthread2.start_tasklet(self._JoinChannels, channelsToJoin))
    354         self._threads.append(uthread2.start_tasklet(self._JoinSystemChannels))
    355         self._threads.append(uthread2.start_tasklet(self.TryJoinPlayerChannelsFromOldSettings_Temp))
    356         if session.role & service.ROLE_NEWBIE:
    357             self._threads.append(uthread2.start_tasklet(self.JoinRookieHelpChannel))
    358         if self.isReconnecting:
    359             for window in self.groupChatWindows.values():
    360                 window.Receive(const.ownerSystem, localization.GetByLabel('UI/Chat/ChatSvc/ReconnectedToChatServer'))
    361                 window.ResetAfterReconnect()
    362 
    363             self.isReconnecting = False
    364         sm.ScatterEvent('OnRefreshChannels')
    365 
    366     def HandleXmppConnectionLost(self):
    367         logger.warning('Connection to chat server lost\n%s\n%s\n%s', self.xmppConnection.hostname, self.xmppConnection.ejabberd_address, self.xmppConnection.username)
    368         self.send_honey_event(CHAT_CLIENT_HANDLE_CONNECTION_LOST)
    369         if self.isReconnecting or session.charid is None:
    370             return
    371         for window in self.groupChatWindows.values():
    372             try:
    373                 window.Receive(const.ownerSystem, localization.GetByLabel('UI/Chat/ChatSvc/ConnectionChatServerLost'))
    374             except Exception:
    375                 pass
    376 
    377         self.pendingJoins = []
    378         self.channelJoinsInProgress.clear()
    379         for each in self.outstandingJoins.itervalues():
    380             each.send(None)
    381 
    382         self.outstandingJoins.clear()
    383         self.isReconnecting = True
    384 
    385     def HandleXmppMessage(self, response):
    386         sender = response.attributes.getValue('from')
    387         what = response.attributes.get('type', '')
    388         if what == 'groupchat':
    389             self._HandleGroupchat(sender, response)
    390         elif what == 'error':
    391             self._HandleMessageError(sender, response)
    392         else:
    393             uthread2.start_tasklet(self._HandleMessage, sender, response)
    394 
    395     def _HandleMessageError(self, sender, response):
    396         errorType = GetErrorType(response)
    397         if errorType == xmppchatConst.ERROR_CHAT_BANNED:
    398             textElement = GetErrorTextElement(response)
    399             if textElement:
    400                 values = textElement.text.split(' ', 2)
    401                 if len(values) >= 2 and values[0] == 'mod_ban':
    402                     self._SetBanExpiry(GetCharacterBannnedExpiry(values[1]))
    403                     channelname, _ = sender.split('@', 1)
    404                     window = self.GetGroupChatWindow(channelname)
    405                     if window:
    406                         window.OnCharacterBannedNotify()
    407                     return
    408         errorCode = GetErrorCode(response)
    409         if errorCode == xmppchatConst.ERROR_CHANNEL_UNAVAILABLE:
    410             raise UserError('LSCCannotSendMessage', {'msg': localization.GetByLabel('UI/Chat/ChatEngine/YouAreNotAbleToSpeak')})
    411         elif errorCode == xmppchatConst.ERROR_LACKING_CONTROL_RIGHTS:
    412             if IsInvite(response):
    413                 raise UserError('LSCCannotAccessControl', {'msg': (101, 'UI/Chat/ChatSvc/YouDontHavePriviledgeToAccessControl')})
    414         elif errorCode == xmppchatConst.ERROR_CONSTRAINED:
    415             text = 'Rate limited'
    416             errorElem = response.find_child_with_tag('error')
    417             if errorElem:
    418                 textElem = errorElem.find_child_with_tag('text')
    419                 text = textElem.text
    420             logger.warning(text)
    421             channelname, rest = sender.split('@', 1)
    422             window = self.GetGroupChatWindow(channelname)
    423             if window:
    424                 window.HandleErrorResponse(response)
    425             return
    426         logger.warning('Error response: %s', response.toXml())
    427 
    428     def _HandleGroupchat(self, sender, response):
    429         group, rest = sender.split('@', 1)
    430         senderparts = rest.split('/')
    431         if len(senderparts) > 1:
    432             sender = senderparts[1]
    433         else:
    434             sender = 'admin'
    435         if GetChannelCategory(group) == xmppchatConst.CATEGORY_LOCAL:
    436             if GetChannelDifferentiator(group) != str(session.solarsystemid2):
    437                 return
    438         window = self.GetOrCreateGroupChatWindow(group)
    439         if window is None or window.receiver != group:
    440             return
    441         body = response.find_child_with_tag('body')
    442         if body and body.text is not None:
    443             text = body.text
    444             if sender == 'admin':
    445                 self._HandleMessageFromAdmin(text, window)
    446             else:
    447                 senderID = int(sender)
    448                 userdata = self.GetUserData(senderID)
    449                 addressBookSvc = sm.GetService('addressbook')
    450                 isBlocked = addressBookSvc.IsBlocked(senderID) or addressBookSvc.IsBlocked(userdata.get('corpid')) or addressBookSvc.IsBlocked(userdata.get('allianceid'))
    451                 if not isBlocked:
    452                     isBlocked = sm.GetService('chat').is_reported_spammer(senderID)
    453                 if not isBlocked:
    454                     msg_id = response.attributes.get('id')
    455                     text = TruncateVeryLongText(text)
    456                     window.Receive(senderID, text, msg_id=msg_id)
    457         subject = response.find_child_with_tag('subject')
    458         if subject:
    459             if self.verbose:
    460                 logger.debug("PAT: _HandleGroupchat 'subject' (motd) change with sender=%s, response=%s", sender, response)
    461             if sender == 'admin':
    462                 sender = None
    463             text = subject.text
    464             window.SetMotd(sender, text)
    465         x = response.find_child_with_tag('x')
    466         if x:
    467             status = x.find_child_with_tag('status')
    468             if status:
    469                 if status.attributes.get('code', 0) == '104':
    470                     if self.verbose:
    471                         logger.debug('Config changed for %s', group)
    472                     window.SetCaptionAsDisplayName()
    473                     msg = localization.GetByLabel('UI/Chat/ChannelConfigChanged')
    474                     window.Receive(const.ownerSystem, msg)
    475 
    476     def _HandleMessageFromAdmin(self, text, window):
    477         try:
    478             data = json.loads(text)
    479             if self.verbose:
    480                 logger.debug('Message from admin:\n{0}'.format(data))
    481             cmd = data['cmd']
    482             who = data['charid']
    483             if cmd == 'join':
    484                 userdata = data['userdata']
    485                 infoLine = userdata.get('info')
    486                 if infoLine:
    487                     self._PopulateEveownersFromInfo(who, infoLine)
    488                     del userdata['info']
    489                 self.userData[who] = userdata
    490                 window.CharacterJoinedChannel(who)
    491             elif cmd == 'leave':
    492                 window.CharacterLeftChannel(who)
    493             elif cmd == 'speak':
    494                 messageText = data['messageText']
    495                 window.Receive(who, messageText)
    496             elif cmd == 'refresh_char':
    497                 charID = data['charid']
    498                 logger.debug('info on char %s is stale', charID)
    499                 if charID == session.charid:
    500                     sm.GetService('stateSvc').NotifyOnStateSetupChange('flagState')
    501                 else:
    502                     sm.ScatterEvent('OnCharStateChanged', charID, data)
    503         except ValueError:
    504             logger.warning('Invalid message from admin: %s', text)
    505 
    506     def _HandleMessage(self, sender, response):
    507         channel_name = sender.split('@')[0]
    508         if IsInvite(response):
    509             self._HandleInvite(channel_name, response)
    510         elif IsDecline(response):
    511             self._HandleDecline(channel_name, response)
    512 
    513     def _HandleInvite(self, channel_name, response):
    514         inviteDetails = GetInviteReason(response)
    515         sender = GetInviteSender(response)
    516         if self.verbose:
    517             logger.debug('Channel invite from %s to %s: %s', sender, channel_name, inviteDetails)
    518         if sender == 'admin':
    519             uthread2.start_tasklet(self.JoinChannel, channel_name)
    520         else:
    521             uthread2.start_tasklet(self._HandleOptionalInvite, sender, channel_name, response)
    522 
    523     def _HandleOptionalInvite(self, sender, channel_name, response):
    524         if self._AcceptInvitation(sender, channel_name):
    525             password = GetInvitePassword(response)
    526             if password:
    527                 settings.user.ui.Set('%sPassword' % channel_name, password)
    528             self.JoinChannel(channel_name)
    529 
    530     def _AcceptInvitation(self, sender, channel_name):
    531         try:
    532             senderId = int(sender)
    533         except ValueError:
    534             logger.warning('Not a valid user id: %s', sender)
    535             return False
    536 
    537         if sm.GetService('addressbook').IsBlocked(senderId):
    538             if self.verbose:
    539                 logger.debug('%s is blocked', senderId)
    540             self.RejectInvite(sender, channel_name, 'blocked')
    541             return False
    542         if settings.user.ui.Get('autoRejectInvitations', 0):
    543             if self.verbose:
    544                 logger.debug('Auto-reject invitations enabled')
    545             self.RejectInvite(sender, channel_name, 'rejected')
    546             return False
    547         if sm.GetService('addressbook').IsInAddressBook(senderId, 'contact'):
    548             if self.verbose:
    549                 logger.debug('%s is in address book - accepting', senderId)
    550             return True
    551         if self.IsJoined(channel_name):
    552             if self.verbose:
    553                 logger.debug('Already in channel %s', channel_name)
    554             return False
    555         sm.GetService('logger').AddText(localization.GetByLabel('UI/Chat/InvitedToConversation', inviter=senderId))
    556         accepted, extra = PresentChatInviteWindow(senderId)
    557         if not accepted:
    558             if extra == 'block':
    559                 self.RejectInvite(sender, channel_name, 'rejected_and_blocked')
    560             else:
    561                 self.RejectInvite(sender, channel_name, 'rejected')
    562         return accepted
    563 
    564     def _HandleDecline(self, channel_name, response):
    565         who = GetDeclineSender(response)
    566         try:
    567             who = int(who)
    568         except ValueError:
    569             pass
    570 
    571         if who not in self.myPendingInvites:
    572             return
    573         del self.myPendingInvites[who]
    574         if channel_name in self.myPendingPrivateChats:
    575             self.CloseChannelWindow(channel_name)
    576         reason = GetDeclineReason(response)
    577         if self.verbose:
    578             logger.debug('Invite from %s was declined: %s', who, reason)
    579         if reason == 'rejected':
    580             raise UserError('ChtRejected', {'char': who})
    581         elif reason == 'blocked':
    582             raise UserError('ChtCharNotReachable', {'char': who})
    583         elif reason == 'rejected_and_blocked':
    584             raise UserError('ChtBlockedNow', {'char': who})
    585 
    586     def HandleXmppPresence(self, response):
    587         if self.isDoingLogoff:
    588             return
    589         self._HandleXmppPresence(response)
    590 
    591     def _HandleXmppPresence(self, response):
    592         if 'id' in response.attributes:
    593             response_id = response.attributes['id']
    594             if response_id in self.outstandingJoins:
    595                 channel = self.outstandingJoins[response_id]
    596                 del self.outstandingJoins[response_id]
    597                 channel.send(response)
    598         who = response.attributes['from']
    599         user, domain = who.split('@', 1)
    600         if domain.startswith('conference.'):
    601             channel = user
    602             category = GetChannelCategory(channel)
    603             character = domain.split('/')[1]
    604             try:
    605                 charid = int(character)
    606             except ValueError:
    607                 charid = character
    608 
    609             if charid == 'admin':
    610                 return
    611             if category in [xmppchatConst.CATEGORY_WORMHOLE, xmppchatConst.CATEGORY_NULLSEC, xmppchatConst.CATEGORY_TRIGLAVIAN]:
    612                 if charid == session.charid:
    613                     return
    614             if charid == 'viking':
    615                 self._UnpackVikingPresence(channel, response)
    616                 return
    617             other, isInvited = self.myPendingPrivateChats.get(channel, (None, False))
    618             if other == charid:
    619                 del self.myPendingPrivateChats[channel]
    620             channelForPendingInvite = self.myPendingInvites.get(charid)
    621             if channelForPendingInvite and channelForPendingInvite == channel:
    622                 del self.myPendingInvites[charid]
    623                 window = self.GetGroupChatWindow(channel)
    624             presenceType = response.attributes.get('type', None)
    625             if self.verbose:
    626                 logger.debug('Presence type: %s', presenceType)
    627             if presenceType in ('unavailable', 'leave'):
    628                 if charid != session.charid:
    629                     if category in xmppchatConst.CHANNELS_WITH_SPECIAL_PRESENCE_HANDLING:
    630                         return
    631                     window = self.GetGroupChatWindow(channel)
    632                     if window and window.receiver == channel:
    633                         window.CharacterLeftChannel(charid)
    634                 else:
    635                     category = GetChannelCategory(channel)
    636                     if category not in xmppchatConst.UNCLOSABLE_CHAT_WNDS:
    637                         self.CloseChannelWindow(channel)
    638                 self.memberRolesByChannels[channel].pop(charid, None)
    639             elif presenceType is None:
    640                 userData = self._GetPresenceUserData(charid, response)
    641                 if userData:
    642                     self.userData[charid] = userData
    643                 if self.verbose:
    644                     logger.debug('User data for %s: %s', charid, userData)
    645                 self._HandleRoleChange(channel, charid, response)
    646                 window = self.GetOrCreateGroupChatWindow(channel)
    647                 if window and window.receiver == channel:
    648                     self._AddCharacterToChannelWindow(window, charid)
    649                 memberRole = self.GetRoleFromResponse(response)
    650                 if memberRole:
    651                     self.memberRolesByChannels[channel][charid] = memberRole
    652                 else:
    653                     self.memberRolesByChannels[channel].pop(charid, None)
    654             elif presenceType == 'error':
    655                 errorCode = GetErrorCode(response)
    656                 if errorCode == xmppchatConst.ERROR_CHANNEL_UNAVAILABLE:
    657                     prompt_player_fsd_dialog('CustomNotify', {'notify': localization.GetByLabel('UI/Chat/ChatSvc/ChannelUnavailable')})
    658             else:
    659                 raise RuntimeError('Unhandled presence type')
    660 
    661     def _UnpackVikingPresence(self, channel, packet):
    662         window = self.GetOrCreateGroupChatWindow(channel)
    663         if window is None or window.receiver != channel:
    664             return
    665         for vp in packet.children:
    666             userData = {}
    667             for key in ['corpid',
    668              'allianceid',
    669              'warfactionid',
    670              'typeid']:
    671                 try:
    672                     value = int(vp.attributes.get(key, 0))
    673                 except ValueError:
    674                     value = 0
    675 
    676                 userData[key] = value
    677 
    678             try:
    679                 value = long(vp.attributes.get('role', 0))
    680             except ValueError:
    681                 value = 0
    682 
    683             userData['role'] = value
    684             try:
    685                 infoLine = vp.attributes.get('info')
    686                 charid = json.loads(infoLine)[0]
    687                 self._PopulateEveownersFromInfo(charid, infoLine)
    688             except (TypeError, IndexError):
    689                 logger.warning('Skipping a bad chat viking presence record. Channel: %s', channel)
    690                 continue
    691 
    692             if userData:
    693                 self.userData[charid] = userData
    694             self._AddCharacterToChannelWindow(window, charid)
    695             self.memberRolesByChannels[channel][charid] = 'participant'
    696 
    697     def _AddCharacterToChannelWindow(self, window, charID):
    698         charID = int(charID)
    699         if cfg.eveowners.GetIfExists(charID):
    700             self._DoAddCharToWindow(charID, window)
    701             return
    702         self.charsToPrime.add(charID)
    703         with locks.TempLock('xmppChatPrimingCharForChannel'):
    704             isPrimed = cfg.eveowners.GetIfExists(charID)
    705             if not isPrimed:
    706                 charsToPrime = self.charsToPrime.copy()
    707                 self.charsToPrime.clear()
    708                 cfg.eveowners.Prime(charsToPrime)
    709                 uthread2.sleep(0.1)
    710         self._DoAddCharToWindow(charID, window)
    711 
    712     def _DoAddCharToWindow(self, charID, window):
    713         if window and not window.destroyed:
    714             window.CharacterJoinedChannel(charID)
    715 
    716     def _HandleRoleChange(self, channel, charid, response):
    717         presenceItem = self._GetPresenceItem(response)
    718         if presenceItem:
    719             window = self.GetOrCreateGroupChatWindow(channel)
    720             if window is None:
    721                 return
    722             reason = self._GetPresenceReason(presenceItem)
    723             user = self._GetUserFromJidAttribute(presenceItem)
    724             if not user:
    725                 user = charid
    726             role = presenceItem.attributes.get('role', None)
    727             if role == 'visitor':
    728                 if not window.IsCharacterMuted(user):
    729                     if self.verbose:
    730                         logger.debug('User is being muted\n%s', response.toXml())
    731                     window.SetCharacterMuted(user, reason)
    732             elif role == 'participant':
    733                 if window.IsCharacterMuted(user):
    734                     if self.verbose:
    735                         logger.debug('User is no longer muted\n%s', response.toXml())
    736                     window.SetCharacterUnmuted(user)
    737 
    738     def _GetPresenceUserData(self, charid, presenceItem):
    739         userDataElem = presenceItem.find_child_with_tag('eve_user_data')
    740         userData = {}
    741         if userDataElem:
    742             for key in ['corpid',
    743              'allianceid',
    744              'warfactionid',
    745              'typeid']:
    746                 try:
    747                     value = int(userDataElem.attributes.get(key, 0))
    748                 except ValueError:
    749                     value = 0
    750 
    751                 userData[key] = value
    752 
    753             try:
    754                 value = long(userDataElem.attributes.get('role', 0))
    755             except ValueError:
    756                 value = 0
    757 
    758             userData['role'] = value
    759             infoLine = userDataElem.attributes.get('info')
    760             self._PopulateEveownersFromInfo(charid, infoLine)
    761         return userData
    762 
    763     def _PopulateEveownersFromInfo(self, charid, infoLine):
    764         if charid not in cfg.eveowners:
    765             if infoLine:
    766                 info = json.loads(infoLine)
    767                 cfg.eveowners.Hint(charid, info)
    768 
    769     def _GetPresenceReason(self, presenceItem):
    770         reasonElem = presenceItem.find_child_with_tag('reason')
    771         if reasonElem:
    772             reason = reasonElem.text
    773         else:
    774             reason = None
    775         return reason
    776 
    777     def GetRoleFromResponse(self, response):
    778         presenceItem = self._GetPresenceItem(response)
    779         return presenceItem.attributes.get('role', None)
    780 
    781     def _GetPresenceItem(self, response):
    782         presenceItem = None
    783         x = response.find_child_with_tag('x')
    784         if x:
    785             xmlns = x.attributes['xmlns']
    786             if xmlns == 'http://jabber.org/protocol/muc#user':
    787                 item = x.find_child_with_tag('item')
    788                 if item:
    789                     presenceItem = item
    790         return presenceItem
    791 
    792     def _GetUserFromJidAttribute(self, item):
    793         user = None
    794         jid = item.attributes.get('jid', None)
    795         if jid:
    796             user = jid.split('@')[0]
    797             try:
    798                 user = int(user)
    799             except ValueError:
    800                 pass
    801 
    802         return user
    803 
    804     def HandleStreamError(self, state, response):
    805         child = response.children[0]
    806         if child.tag == 'host-unknown':
    807             logger.error('Virtual host has not been set up for %s', self.hostname)
    808             self.xmppConnection.Disconnect()
    809 
    810     def GetOrCreateGroupChatWindow(self, channel, setWindowsChannel = True):
    811         window = self.GetGroupChatWindow(channel)
    812         if not window:
    813             windowName = self.GetWindowNameForGroup(channel)
    814             if self._IsCategoryMigrated(windowName):
    815                 return
    816             stackless_channel = stackless.channel()
    817             self.groupChatWindows[windowName] = stackless_channel
    818             try:
    819                 if self.verbose:
    820                     logger.debug('Creating window for %s', channel)
    821                 displayName = self.GetDisplayName(channel)
    822                 windowId = 'chatchannel_%s' % windowName
    823                 window = XmppChatWindow(receiver=channel, isGroupChat=True, channel=channel, name=windowName, caption=displayName, windowID=windowId, check_open_minimized=True)
    824                 window.verbose = self.verbose
    825                 self.groupChatWindows[windowName] = window
    826             finally:
    827                 if self.verbose:
    828                     logger.debug('Window for %s has been created', channel)
    829                 while stackless_channel.balance < 0:
    830                     stackless_channel.send(window)
    831 
    832                 if self.verbose:
    833                     logger.debug('Channels waiting for %s have been notified', channel)
    834 
    835             uthread2.start_tasklet(self.StoreChannelList)
    836         if setWindowsChannel:
    837             window.SetChannel(channel)
    838         return window
    839 
    840     def GetGroupChatWindow(self, group):
    841         windowName = self.GetWindowNameForGroup(group)
    842         window = self.groupChatWindows.get(windowName, None)
    843         if type(window) == stackless.channel:
    844             if self.verbose:
    845                 logger.debug('Waiting for window for %s', group)
    846             window = window.receive()
    847         if window and window.destroyed:
    848             del self.groupChatWindows[windowName]
    849             window = None
    850         return window
    851 
    852     def GetWindowNameForGroup(self, group):
    853         parts = group.split('_')
    854         try:
    855             windowName = parts[0]
    856         except IndexError:
    857             windowName = group
    858 
    859         if windowName in [xmppchatConst.CATEGORY_PRIVATE,
    860          xmppchatConst.CATEGORY_PLAYER,
    861          xmppchatConst.CATEGORY_BOT,
    862          xmppchatConst.CATEGORY_SYSTEM]:
    863             windowName = group
    864         if windowName in [xmppchatConst.CATEGORY_LOCAL_SUPPRESSED,
    865          xmppchatConst.CATEGORY_WORMHOLE,
    866          xmppchatConst.CATEGORY_NULLSEC,
    867          xmppchatConst.CATEGORY_TRIGLAVIAN]:
    868             windowName = xmppchatConst.CATEGORY_LOCAL
    869         return windowName
    870 
    871     def GetChannels(self):
    872         if not self.xmppConnection:
    873             return []
    874         channels = []
    875         playerChannels = set()
    876         to = 'conference.{0}'.format(self.hostname)
    877         queryTemplate = "<query xmlns='http://jabber.org/protocol/disco#items' node='forme'></query>"
    878         initialQuery = queryTemplate.format('')
    879         response = self.xmppConnection.IssueRequest(initialQuery, 'get', to=to, method_tag='get_channels')
    880         response_channels = []
    881         try:
    882             queryNode = response.children[0]
    883             response_channels = queryNode.children
    884         except AttributeError:
    885             logger.error('GetChannel request returned nothing')
    886 
    887         for item in response_channels:
    888             jid = item.attributes.get('jid', None)
    889             if jid is None:
    890                 logger.error('Error with getting jid')
    891                 continue
    892             if item.tag == 'item':
    893                 channel = jid.split('@')[0]
    894                 name = item.attributes.get('name', '')
    895                 affiliation = item.attributes.get('node')
    896                 channels.append((channel, name, affiliation))
    897                 category = GetChannelCategory(channel)
    898                 if category == xmppchatConst.CATEGORY_PLAYER:
    899                     playerChannels.add(channel)
    900 
    901         playerChannelsJoined = settings.user.ui.Get('chatPlayerChannelsJoined', {})
    902         for pc, displayName in playerChannelsJoined.iteritems():
    903             if pc not in playerChannels and displayName:
    904                 channels.append((pc, displayName))
    905 
    906         return channels
    907 
    908     def GetNumChannelOccupants(self, channelName):
    909         if not self.xmppConnection:
    910             return 0
    911         query = "<query xmlns='http://jabber.org/protocol/disco#info'/>"
    912         roomName = self.GetFullRoomName(channelName)
    913         response = self.xmppConnection.IssueRequest(query, 'get', to=roomName, method_tag='get_num_channel_occupants')
    914         if response and response.attributes.get('type') == 'result':
    915             num_occupants = self._GetNumOccupantsFromInfoQuery(response)
    916         else:
    917             num_occupants = 0
    918         return num_occupants
    919 
    920     def GetChannelByDisplayName(self, displayName):
    921         channels = []
    922         if not self.xmppConnection:
    923             return channels
    924         to = 'conference.{0}'.format(self.hostname)
    925         queryTemplate = "<query xmlns='http://jabber.org/protocol/disco#items' node={0}></query>"
    926         node = 'byname/' + escape(displayName).encode('utf-8')
    927         quoted_node = quoteattr(node)
    928         initialQuery = queryTemplate.format(quoted_node)
    929         response = self.xmppConnection.IssueRequest(initialQuery, 'get', to=to, method_tag='get_channel_by_display_name')
    930         queryNode = response.children[0]
    931         for item in queryNode.children:
    932             if item.tag == 'item':
    933                 channel = item.attributes['jid'].split('@')[0]
    934                 name = item.attributes['name']
    935                 channels.append((channel, name))
    936 
    937         return channels
    938 
    939     def GetMembersWithNoRole(self, channel):
    940         query = "<query xmlns='http://jabber.org/protocol/disco#items' node='norole'></query>"
    941         roomName = self.GetFullRoomName(channel)
    942         response = self.xmppConnection.IssueRequest(query, 'get', to=roomName, method_tag='get_members_with_no_role')
    943         queryNode = response.children[0]
    944         chars = []
    945         for item in queryNode.children:
    946             if item.tag == 'item':
    947                 name = item.attributes['name']
    948                 try:
    949                     name = int(name)
    950                 except ValueError:
    951                     pass
    952 
    953                 chars.append(name)
    954 
    955         return chars
    956 
    957     def JoinRookieHelpChannel(self):
    958         self.JoinChannel(self.XmppChatMgr.GetRookieChannel())
    959 
    960     def JoinHelpChannelForLanguage(self, languageID):
    961         self.JoinChannel(self.XmppChatMgr.GetHelpChannel(languageID))
    962 
    963     def JoinHelpChannels(self):
    964         normal_help_channel = self.XmppChatMgr.GetHelpChannel()
    965         self.JoinChannel(normal_help_channel)
    966         if session.role & service.ROLE_NEWBIE:
    967             rookie_help_channel = self.XmppChatMgr.GetRookieChannel()
    968             if rookie_help_channel != normal_help_channel:
    969                 self.JoinChannel(rookie_help_channel)
    970 
    971     def JoinChannel(self, channelName, password = '', suppressMessage = False, isConnectionCheck = False, historySeconds = 0):
    972         if not IsValidChannelName(channelName):
    973             if channelName in SESSION_CHANGE_CATEGORIES.values():
    974                 raise InvalidSessionChatChannelNameError('Invalid channel name')
    975             raise InvalidChatChannelNameError('Invalid channel name')
    976         if channelName in self.channelJoinsInProgress:
    977             if self.verbose:
    978                 logger.debug('Joining channel %s already in progress', channelName)
    979             return
    980         channelCategory = GetChannelCategory(channelName)
    981         if self._IsCategoryMigrated(channelCategory):
    982             return
    983         self.channelJoinsInProgress.add(channelName)
    984         if channelCategory == xmppchatConst.CATEGORY_PLAYER and not suppressMessage:
    985             prompt_player_fsd_dialog('ChannelTryingToJoin')
    986         try:
    987             result = self._JoinChannelInternal(channelName, password=password, isConnectionCheck=isConnectionCheck, historySeconds=historySeconds)
    988         finally:
    989             self.channelJoinsInProgress.discard(channelName)
    990 
    991         return result
    992 
    993     def _JoinChannelInternal(self, channelName, password = '', isConnectionCheck = False, historySeconds = 0):
    994         if self.xmppConnection and self.xmppConnection.disconnectRequested:
    995             if self.verbose:
    996                 logger.debug("Can't join channel %s because we're disconnecting...")
    997             self.pendingJoins = []
    998             return
    999         if not self.xmppConnection or not self.xmppConnection.IsConnected():
   1000             if self.verbose:
   1001                 logger.debug("Can't join channel %s until logged in to chat server - adding to pending list", channelName)
   1002             self.pendingJoins.append(channelName)
   1003             return
   1004         if self.verbose:
   1005             logger.debug('Joining channel %s', channelName)
   1006         category = GetChannelCategory(channelName)
   1007         if category == xmppchatConst.CATEGORY_LOCAL_SUPPRESSED:
   1008             if not isConnectionCheck:
   1009                 sm.ScatterEvent('OnChannelJoined', channelName)
   1010             self.GetOrCreateGroupChatWindow(channelName)
   1011             return True
   1012         if not password:
   1013             password = settings.user.ui.Get('%sPassword' % channelName)
   1014         retriesRemaining = 4
   1015         isRetry = False
   1016         while retriesRemaining > 0:
   1017             retriesRemaining -= 1
   1018             response = self._JoinChannelHelper(channelName, password, historySeconds)
   1019             if not response:
   1020                 return False
   1021             responseType = response.attributes.get('type', None)
   1022             if responseType == 'error':
   1023                 errorCode = GetErrorCode(response)
   1024                 if self.verbose:
   1025                     logger.debug('Error response while attempting to join channel %s: %s', channelName, str(response))
   1026                 if errorCode == xmppchatConst.ERROR_WRONG_PASSWORD:
   1027                     displayName = self.GetDisplayName(channelName)
   1028                     if retriesRemaining == 0:
   1029                         prompt_player_fsd_dialog('LSCWrongPassword', {'channelName': displayName})
   1030                         return False
   1031                     password = self._GetPasswordHelper(channelName, displayName, isRetry)
   1032                     if not password:
   1033                         break
   1034                     isRetry = True
   1035                 else:
   1036                     if errorCode == xmppchatConst.ERROR_CHANNEL_UNAVAILABLE:
   1037                         return False
   1038                     if errorCode == xmppchatConst.ERROR_CANT_JOIN_CHANNEL:
   1039                         if category in [xmppchatConst.CATEGORY_PLAYER] and session.role & (service.ROLE_CHTADMINISTRATOR | service.ROLE_GML):
   1040                             self.XmppChatMgr.GrantChannelOwnership(channelName)
   1041                         elif category in xmppchatConst.PROXY_CONTROLLED_CATEGORIES:
   1042                             if retriesRemaining == 0:
   1043                                 gotSomething = self._ResyncWithRetries()
   1044                                 if gotSomething:
   1045                                     logger.warning("Can't join channel %s, waiting for proxy to resync" % channelName)
   1046                                 else:
   1047                                     logger.exception("Can't join channel that proxy should have given us access to")
   1048                                 return False
   1049                             logger.warning("Can't join channel %s, retrying" % channelName)
   1050                             uthread2.sleep(3)
   1051                         else:
   1052                             displayName = self.GetDisplayName(channelName)
   1053                             prompt_player_fsd_dialog('LSCCannotJoin', {'msg': (const.UE_LOC, 'UI/Chat/ChatSvc/YouHaveBeenBanned', {'channelName': displayName})})
   1054                             return False
   1055                     elif errorCode == xmppchatConst.ERROR_CONSTRAINED:
   1056                         prompt_player_fsd_dialog('LSCCannotCreate', {'msg': (const.UE_LOC, 'UI/Chat/ChatSvc/YouHaveTooManyChannels', {'maxChannels': 10})})
   1057                         return False
   1058                     else:
   1059                         logger.debug("Can't join channel %s", channelName)
   1060                         return False
   1061 
   1062             else:
   1063                 if category == xmppchatConst.CATEGORY_PLAYER:
   1064                     playerChannelsJoined = settings.user.ui.Get('chatPlayerChannelsJoined', {})
   1065                     if channelName not in playerChannelsJoined:
   1066                         displayName = self.GetDisplayName(channelName)
   1067                         playerChannelsJoined[channelName] = displayName
   1068                         settings.user.ui.Set('chatPlayerChannelsJoined', playerChannelsJoined)
   1069                 if not isConnectionCheck:
   1070                     sm.ScatterEvent('OnChannelJoined', channelName)
   1071                 return True
   1072 
   1073         return False
   1074 
   1075     def _ResyncWithRetries(self):
   1076         resyncRetriesRemaining = 5
   1077         gotSomething = False
   1078         while resyncRetriesRemaining > 0 and not gotSomething:
   1079             resyncRetriesRemaining -= 1
   1080             channelsToJoin = self.XmppChatMgr.ResyncSystemChannelAccess()
   1081             if not channelsToJoin:
   1082                 logger.warning('ResyncSystemChannelAccess returned None, retrying')
   1083                 uthread2.sleep(3)
   1084             else:
   1085                 gotSomething = True
   1086 
   1087         return gotSomething
   1088 
   1089     def _GetPasswordHelper(self, channelName, displayName, isRetry):
   1090         if session.role & (service.ROLE_CHTADMINISTRATOR | service.ROLE_GML):
   1091             self.XmppChatMgr.GrantChannelOwnership(channelName)
   1092             return 'bypass_with_roles'
   1093         else:
   1094             title = localization.GetByLabel('UI/Chat/PasswordRequired', channelName=displayName)
   1095             dlg = ChannelPasswordWindow.Open(channel=channelName, title=title, retry=isRetry)
   1096             dlg.ShowDialog()
   1097             password = dlg.password
   1098             return password
   1099 
   1100     def _JoinChannelHelper(self, channelName, password, historySeconds):
   1101         roomName = self.GetFullRoomName(channelName)
   1102         response_id = self.xmppConnection.JoinRoom(roomName, password=password, historySeconds=historySeconds)
   1103         stackless_channel = stackless.channel()
   1104         self.outstandingJoins[response_id] = stackless_channel
   1105         response = stackless_channel.receive()
   1106         return response
   1107 
   1108     def LeaveChannel(self, channelName):
   1109         if self.verbose:
   1110             logger.debug('LeaveChannel %s', channelName)
   1111         if channelName in self.pendingJoins:
   1112             self.pendingJoins.remove(channelName)
   1113             return
   1114         roomName = self.GetFullRoomName(channelName)
   1115         self.xmppConnection.LeaveRoom(roomName)
   1116         category = GetChannelCategory(channelName)
   1117         if category not in xmppchatConst.UNCLOSABLE_CHAT_WNDS:
   1118             self.CloseChannelWindow(channelName)
   1119         self.memberRolesByChannels.pop(channelName, None)
   1120 
   1121     def CloseChannelWindow(self, channelName):
   1122         if self.verbose:
   1123             logger.debug('Scattering OnChannelLeft for %s', channelName)
   1124         sm.ScatterEvent('OnChannelLeft', channelName)
   1125         if channelName in self.myPendingPrivateChats:
   1126             other, isInvited = self.myPendingPrivateChats[channelName]
   1127             del self.myPendingPrivateChats[channelName]
   1128             if other in self.myPendingInvites:
   1129                 del self.myPendingInvites[other]
   1130         window = self.GetGroupChatWindow(channelName)
   1131         if window:
   1132             window.access_lost = True
   1133             window.Close()
   1134             windowName = self.GetWindowNameForGroup(channelName)
   1135             del self.groupChatWindows[windowName]
   1136             self.StoreChannelList()
   1137 
   1138     def IsJoined(self, channelName):
   1139         return channelName in self.groupChatWindows
   1140 
   1141     def GetFullRoomName(self, channelName):
   1142         roomName = '{0}@conference.{1}'.format(channelName, self.hostname)
   1143         return roomName
   1144 
   1145     def GetUserNameFromCharId(self, charid):
   1146         return self.xmppConnection.GetFullUsername(charid)
   1147 
   1148     def CreatePrivateChannel(self, charid):
   1149         channelName = self.XmppChatMgr.CreatePrivateChannel(charid)
   1150         self.myPendingPrivateChats[channelName] = (charid, False)
   1151         self.JoinChannel(channelName)
   1152         return channelName
   1153 
   1154     def CreateChannel(self, name):
   1155         return self.XmppChatMgr.CreatePlayerOwnedChannel(name)
   1156 
   1157     def DeleteChannel(self, channelName):
   1158         query = "<query xmlns='http://jabber.org/protocol/muc#owner'><destroy/></query>"
   1159         roomName = self.GetFullRoomName(channelName)
   1160         displayName = self.GetDisplayName(channelName)
   1161         isElevatedPlayer = session.role & service.ROLEMASK_ELEVATEDPLAYER
   1162         if isElevatedPlayer:
   1163             msgName = 'LSCConfirmDestroyChannelElevatedPlayer'
   1164         else:
   1165             msgName = 'LSCConfirmDestroyChannel'
   1166         if prompt_player_fsd_dialog(msgName, {'displayName': displayName}, uiconst.YESNO) != uiconst.ID_YES:
   1167             return
   1168         response = self.xmppConnection.IssueRequest(query, 'set', to=roomName, method_tag='delete_channel')
   1169         if response.attributes.getValue('type') == 'result':
   1170             playerChannelsJoined = settings.user.ui.Get('chatPlayerChannelsJoined', {})
   1171             try:
   1172                 del playerChannelsJoined[channelName]
   1173             except KeyError:
   1174                 pass
   1175 
   1176             settings.user.ui.Set('chatPlayerChannelsJoined', playerChannelsJoined)
   1177             sm.ScatterEvent('OnChannelDeleted', channelName)
   1178         else:
   1179             logger.warn('DeleteChannel: Incorrect response')
   1180 
   1181     def ForgetChannel(self, channelName):
   1182         playerChannelsJoined = settings.user.ui.Get('chatPlayerChannelsJoined', {})
   1183         if channelName in playerChannelsJoined:
   1184             del playerChannelsJoined[channelName]
   1185         settings.user.ui.Set('chatPlayerChannelsJoined', playerChannelsJoined)
   1186         sm.ScatterEvent('OnRefreshChannels')
   1187 
   1188     def Invite(self, charid, channelName = None, isRecruiting = None):
   1189         if charid == session.charid:
   1190             raise UserError('ChtCannotInviteSelf', {'char': charid,
   1191              'channel': channelName})
   1192         if IsNPC(charid) or not IsCharacter(charid):
   1193             prompt_player_fsd_dialog('CustomInfo', {'info': localization.GetByLabel('UI/Chat/ErrorNotAPlayerCharacter')})
   1194             return
   1195         reason = ''
   1196         isPrivate = False
   1197         if channelName is None:
   1198             if charid in self.myPendingInvites:
   1199                 raise UserError('ChtAlreadyInvited', {'char': charid})
   1200             channelName = self.CreatePrivateChannel(charid)
   1201             if not channelName:
   1202                 raise RuntimeError('Could not create channel for private chat')
   1203             reason = 'Private chat'
   1204             isPrivate = True
   1205             self.myPendingInvites[charid] = channelName
   1206         if session.role & (service.ROLE_CHTADMINISTRATOR | service.ROLE_GML | service.ROLE_LEGIONEER):
   1207             self.XmppChatMgr.AddCharacterToChannel(channelName, charid, 'Added by %s' % session.charid)
   1208         elif not isPrivate:
   1209             roomName = self.GetFullRoomName(channelName)
   1210             self.xmppConnection.Invite(self.GetUserNameFromCharId(charid), roomName, reason)
   1211 
   1212     def RejectInvite(self, charid, channelName, reason):
   1213         roomName = self.GetFullRoomName(channelName)
   1214         self.xmppConnection.RejectInvite(self.GetUserNameFromCharId(charid), roomName, reason)
   1215 
   1216     def GetChannelsAvailableForInvite(self, charid):
   1217         channels = []
   1218         for each in self.groupChatWindows.itervalues():
   1219             if each.CanInvite(charid):
   1220                 channels.append(each.GetChannelId())
   1221 
   1222         return channels
   1223 
   1224     def GetDisplayName(self, channelName):
   1225         if self.verbose:
   1226             logger.debug('GetDisplayName %s', channelName)
   1227         k = GetChannelCategory(channelName)
   1228         if k in ('incursion', 'spreadingIncursion'):
   1229             try:
   1230                 v = int(GetChannelDifferentiator(channelName))
   1231             except ValueError:
   1232                 return channelName
   1233 
   1234         if k == 'global':
   1235             return localization.GetByLabel('UI/Common/Global')
   1236         if k == xmppchatConst.CATEGORY_FLEET:
   1237             return localization.GetByLabel('UI/Fleet/Fleet')
   1238         if k == 'squad':
   1239             return localization.GetByLabel('UI/Fleet/Squad')
   1240         if k == 'wing':
   1241             return localization.GetByLabel('UI/Fleet/Wing')
   1242         if k == xmppchatConst.CATEGORY_CORP:
   1243             return localization.GetByLabel('UI/Common/Corp')
   1244         if k == xmppchatConst.CATEGORY_FACTION:
   1245             return localization.GetByLabel('UI/Common/Militia')
   1246         if k == xmppchatConst.CATEGORY_ALLIANCE:
   1247             return localization.GetByLabel('UI/Common/Alliance')
   1248         if k in (xmppchatConst.CATEGORY_LOCAL,
   1249          xmppchatConst.CATEGORY_LOCAL_SUPPRESSED,
   1250          xmppchatConst.CATEGORY_WORMHOLE,
   1251          xmppchatConst.CATEGORY_NULLSEC,
   1252          xmppchatConst.CATEGORY_TRIGLAVIAN):
   1253             return localization.GetByLabel('UI/Chat/Local')
   1254         if k == 'solarsystem':
   1255             return localization.GetByLabel('UI/Common/LocationTypes/System')
   1256         if k == 'constellation':
   1257             return localization.GetByLabel('UI/Common/LocationTypes/Constellation')
   1258         if k == 'region':
   1259             return localization.GetByLabel('UI/Common/LocationTypes/Region')
   1260         if k == xmppchatConst.CATEGORY_RW:
   1261             return sm.GetService('rwService').get_site_name()
   1262         if k.startswith('incursion'):
   1263             incursionSvc = sm.GetService('incursion')
   1264             distributionName = incursionSvc.GetDistributionName(v)
   1265             constellationName = incursionSvc.GetConstellationNameFromTaleIDForIncursionChat(v)
   1266             return localization.GetByLabel('UI/Chat/IncursionConstellationChannel', constellationName=constellationName, distributionName=distributionName)
   1267         if k.startswith('spreadingIncursion'):
   1268             incursionSvc = sm.GetService('incursion')
   1269             distributionName = incursionSvc.GetDistributionName(v)
   1270             return distributionName
   1271         if k.startswith('team'):
   1272             return localization.GetByLabel('UI/Chat/District')
   1273         if k == xmppchatConst.CATEGORY_SYSTEM:
   1274             _, groupMessageId, channelMessageId = channelName.split('_', 3)
   1275             return localization.GetByMessageID(int(channelMessageId))
   1276         roomName = self.GetFullRoomName(channelName)
   1277         if k == xmppchatConst.CATEGORY_PRIVATE:
   1278             query = "<query xmlns='http://jabber.org/protocol/disco#info'/>"
   1279             response = self.xmppConnection.IssueRequest(query, 'get', to=roomName, method_tag='get_private_display_name')
   1280             if not response.attributes.getValue('type') == 'result':
   1281                 return None
   1282             num_occupants = self._GetNumOccupantsFromInfoQuery(response)
   1283             if num_occupants < 2:
   1284                 return localization.GetByLabel('UI/Chat/PrivateChatAlone')
   1285             elif channelName in self.groupChatWindows and hasattr(self.groupChatWindows[channelName], 'GetMembers'):
   1286                 return self.GetPrivateChannelDisplayName(channelName)
   1287             else:
   1288                 return localization.GetByLabel('UI/Chat/PrivateChat', title=num_occupants)
   1289         if k == xmppchatConst.CATEGORY_PLAYER:
   1290             query = "<query xmlns='http://jabber.org/protocol/disco#info' node='{0}'/>".format(channelName)
   1291             response = self.xmppConnection.IssueRequest(query, 'get', to='conference.' + self.hostname, method_tag='get_player_display_name')
   1292             return self._GetNameFromInfoQuery(response)
   1293         return channelName
   1294 
   1295     def GetPrivateChannelDisplayName(self, channelName):
   1296         window = self.groupChatWindows[channelName]
   1297         if not window:
   1298             return localization.GetByLabel('UI/Chat/PrivateChatAlone')
   1299         members = window.GetMembers()
   1300         if not members:
   1301             return localization.GetByLabel('UI/Chat/PrivateChatAlone')
   1302         member_names = []
   1303         for each in members:
   1304             if each == session.charid:
   1305                 continue
   1306             name = cfg.eveowners.Get(each).name
   1307             member_names.append(name)
   1308 
   1309         if len(member_names) == 0:
   1310             if channelName in self.myPendingPrivateChats:
   1311                 other, isInvited = self.myPendingPrivateChats[channelName]
   1312                 name = cfg.eveowners.Get(other).name
   1313                 member_names.append(name)
   1314         if len(member_names) == 0:
   1315             return localization.GetByLabel('UI/Chat/PrivateChatAlone')
   1316         if len(member_names) == 1:
   1317             return localization.GetByLabel('UI/Chat/PrivateChat', title=member_names[0])
   1318         return localization.GetByLabel('UI/Chat/GroupChat', title=', '.join(member_names))
   1319 
   1320     def GetMemberIDsForNamedWindow(self, windowName):
   1321         window = self.groupChatWindows[windowName]
   1322         memberIDs = window.GetMembers()
   1323         return memberIDs
   1324 
   1325     def _GetNameFromInfoQuery(self, response):
   1326         displayName = None
   1327         if response:
   1328             queryElement = response.find_child_with_tag('query')
   1329             if queryElement:
   1330                 identityElement = queryElement.find_child_with_tag('identity')
   1331                 if identityElement:
   1332                     displayName = identityElement.attributes.get('name', None)
   1333         return displayName
   1334 
   1335     def _GetNumOccupantsFromInfoQuery(self, response):
   1336         num_occupants = 0
   1337         queryElement = response.find_child_with_tag('query')
   1338         if queryElement:
   1339             xElement = queryElement.find_child_with_tag('x')
   1340             if xElement:
   1341                 for child in xElement.children:
   1342                     var = child.attributes.get('var', None)
   1343                     if var == 'muc#roominfo_occupants':
   1344                         valueElement = child.find_child_with_tag('value')
   1345                         if valueElement:
   1346                             occupants = valueElement.text
   1347                             try:
   1348                                 num_occupants = int(occupants)
   1349                             except ValueError:
   1350                                 pass
   1351 
   1352         return num_occupants
   1353 
   1354     def GetDetailedDisplayName(self, channelName):
   1355         k = GetChannelCategory(channelName)
   1356         try:
   1357             v = int(GetChannelDifferentiator(channelName))
   1358         except ValueError:
   1359             return self.GetDisplayName(channelName)
   1360 
   1361         if k == xmppchatConst.CATEGORY_CORP:
   1362             return localization.GetByLabel('UI/Chat/FeatureChannelName', featureType=localization.GetByLabel('UI/Common/Corp'), featureName=cfg.eveowners.Get(v).name)
   1363         if k == xmppchatConst.CATEGORY_WARFACTION:
   1364             return localization.GetByLabel('UI/Chat/FeatureChannelName', featureType=localization.GetByLabel('UI/Common/Militia'), featureName=cfg.eveowners.Get(v).name)
   1365         if k == xmppchatConst.CATEGORY_ALLIANCE:
   1366             return localization.GetByLabel('UI/Chat/FeatureChannelName', featureType=localization.GetByLabel('UI/Common/Alliance'), featureName=cfg.eveowners.Get(v).name)
   1367         if k == xmppchatConst.CATEGORY_LOCAL_SUPPRESSED:
   1368             return localization.GetByLabel('UI/Chat/FeatureChannelName', featureType=localization.GetByLabel('UI/Chat/Local'), featureName=localization.GetByLabel('UI/Chat/Unknown'))
   1369         if k in (xmppchatConst.CATEGORY_LOCAL,
   1370          xmppchatConst.CATEGORY_WORMHOLE,
   1371          xmppchatConst.CATEGORY_NULLSEC,
   1372          xmppchatConst.CATEGORY_TRIGLAVIAN):
   1373             return localization.GetByLabel('UI/Chat/FeatureChannelName', featureType=localization.GetByLabel('UI/Chat/Local'), featureName=cfg.evelocations.Get(v).name)
   1374         if k == 'solarsystem':
   1375             return localization.GetByLabel('UI/Chat/FeatureChannelName', featureType=localization.GetByLabel('UI/Common/LocationTypes/System'), featureName=cfg.evelocations.Get(v).name)
   1376         if k == 'constellation':
   1377             return localization.GetByLabel('UI/Chat/FeatureChannelName', featureType=localization.GetByLabel('UI/Common/LocationTypes/Constellation'), featureName=cfg.evelocations.Get(v).name)
   1378         if k == 'region':
   1379             return localization.GetByLabel('UI/Chat/FeatureChannelName', featureType=localization.GetByLabel('UI/Common/LocationTypes/Region'), featureName=cfg.evelocations.Get(v).name)
   1380         if k == xmppchatConst.CATEGORY_SYSTEM:
   1381             _, groupMessageId, channelMessageId = channelName.split('_', 3)
   1382             return localization.GetByMessageID(int(channelMessageId))
   1383         return self.GetDisplayName(channelName)
   1384 
   1385     def IsOwnerOrOperatorOfChannel(self, channelName, ignoreRole = False):
   1386         if not ignoreRole:
   1387             if session.role & (service.ROLE_CHTADMINISTRATOR | service.ROLE_GML):
   1388                 category = GetChannelCategory(channelName)
   1389                 if category in [xmppchatConst.CATEGORY_PLAYER, xmppchatConst.CATEGORY_SYSTEM]:
   1390                     return True
   1391         for affiliationType in ('owner', 'admin'):
   1392             try:
   1393                 users = self.GetUsersWithAffiliation(channelName, affiliationType)
   1394             except AttributeError:
   1395                 logger.exception('Error when getting users with %s affiliation' % affiliationType)
   1396                 users = []
   1397 
   1398             if session.charid in users:
   1399                 return True
   1400             if session.corpid in users:
   1401                 return True
   1402             if session.allianceid in users:
   1403                 return True
   1404 
   1405         return False
   1406 
   1407     def IsOwnerOfChannel(self, channelName):
   1408         try:
   1409             users = self.GetUsersWithAffiliation(channelName, 'owner')
   1410         except AttributeError:
   1411             logger.exception('Error when getting users with owner affiliation')
   1412             users = []
   1413 
   1414         if session.charid in users:
   1415             return True
   1416         return False
   1417 
   1418     def IsOperatorOfChannel(self, channelName):
   1419         try:
   1420             users = self.GetUsersWithAffiliation(channelName, 'admin')
   1421         except AttributeError:
   1422             logger.exception('Error when getting users with operator affiliation')
   1423             users = []
   1424 
   1425         if session.charid in users:
   1426             return True
   1427         return False
   1428 
   1429     def SetMotd(self, channelName, motd):
   1430         bannedwords.check_words_allowed(motd)
   1431         if self.verbose:
   1432             logger.debug('SetMotd for %s: %s', channelName, motd)
   1433         roomName = self.GetFullRoomName(channelName)
   1434         channelCategory = GetChannelCategory(channelName)
   1435         if channelCategory == xmppchatConst.CATEGORY_FLEET:
   1436             sm.GetService('fleet').SetRemoteMotd(motd)
   1437             return
   1438         if channelCategory in [xmppchatConst.CATEGORY_CORP, xmppchatConst.CATEGORY_ALLIANCE]:
   1439             return self.XmppChatMgr.SetMotd(roomName, motd)
   1440         self.xmppConnection.SendXmppSubject(roomName, motd)
   1441 
   1442     def GetMotd(self, channelName):
   1443         window = self.GetGroupChatWindow(channelName)
   1444         return window.GetMotd()
   1445 
   1446     def ConfigureChannel(self, channelName, roomconfig):
   1447         form = "<field var='FORM_TYPE'><value>http://jabber.org/protocol/muc#roomconfig</value></field>\n"
   1448         for key, value in roomconfig.items():
   1449             field = "<field var='{0}'><value>{1}</value></field>\n".format(key, value)
   1450             form += field
   1451 
   1452         query = "\n<query xmlns='http://jabber.org/protocol/muc#owner'>\n<x xmlns='jabber:x:data' type='submit'>\n"
   1453         query += form
   1454         query += '</x>\n</query>'
   1455         roomName = self.GetFullRoomName(channelName)
   1456         response = self.xmppConnection.IssueRequest(query, 'set', to=roomName, method_tag='configure_channel')
   1457         if not response.attributes.getValue('type') == 'result':
   1458             logger.warn('ConfigureChannel: Incorrect response')
   1459             return False
   1460         return True
   1461 
   1462     def BanUserFromChannel(self, channelName, charid, reason, durationInSecs):
   1463         self.SetAffiliation(channelName, charid, 'outcast')
   1464         self._RegisterTemporaryRestriction('ban', channelName, charid, durationInSecs)
   1465         if durationInSecs:
   1466             self.SetAffiliation(channelName, charid, 'none')
   1467 
   1468     def UnbanUserFromChannel(self, channelName, charid):
   1469         if self.verbose:
   1470             logger.debug('UnbanUserFromChannel(%s, %s)', channelName, charid)
   1471         self.SetAffiliation(channelName, charid, 'none')
   1472         if str(charid).startswith('set_'):
   1473             prefix, charid = charid.split('_', 1)
   1474             self.SetAffiliation(channelName, charid, 'none')
   1475         self._UnregisterTemporaryRestriction('ban', channelName, charid)
   1476 
   1477     def MuteUser(self, channelName, charid, reason, durationInSecs = 0):
   1478         self.SetRole(channelName, charid, 'visitor', reason)
   1479         if durationInSecs <= 0:
   1480             durationInSecs = 315360000
   1481         self._RegisterTemporaryRestriction('mute', channelName, charid, durationInSecs)
   1482 
   1483     def UnmuteUser(self, channelName, charid):
   1484         self.SetRole(channelName, charid, 'participant')
   1485         self._UnregisterTemporaryRestriction('mute', channelName, charid)
   1486 
   1487     def _RegisterTemporaryRestriction(self, category, channelName, charid, durationInSecs):
   1488         roomName = self.GetFullRoomName(channelName)
   1489         jid = self.GetUserNameFromCharId(charid)
   1490         self.xmppConnection.RegisterTemporaryRestriction(category, roomName, jid, durationInSecs)
   1491 
   1492     def _UnregisterTemporaryRestriction(self, category, channelName, charID):
   1493         roomName = self.GetFullRoomName(channelName)
   1494         jid = self.GetUserNameFromCharId(charID)
   1495         self.xmppConnection.UnregisterTemporaryRestriction(category, roomName, jid)
   1496 
   1497     def IsMuted(self, channelID, charID):
   1498         muted = self.GetUsersWithRole(channelID, 'visitor')
   1499         if self.verbose:
   1500             logger.debug('IsGagged(%s, %s): %s', channelID, charID, muted)
   1501         return charID in muted
   1502 
   1503     def IsOperator(self, channelID, charID):
   1504         memberRole = self.memberRolesByChannels[channelID].get(charID, None)
   1505         if memberRole:
   1506             return memberRole == 'moderator'
   1507         else:
   1508             moderators = self.GetUsersWithRole(channelID, 'moderator')
   1509             return charID in moderators
   1510 
   1511     def SetAffiliation(self, channelName, charid, affiliation):
   1512         if self.verbose:
   1513             logger.debug('SetAffiliation(%s, %s, %s)', channelName, charid, affiliation)
   1514         roomName = self.GetFullRoomName(channelName)
   1515         idtype = 'character'
   1516         if IsCorporation(charid):
   1517             idtype = 'corporation'
   1518         elif IsAlliance(charid):
   1519             idtype = 'alliance'
   1520         template = "<query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='{1}' jid='{0}'><reason>{2}</reason></item></query>"
   1521         query = template.format(self.GetUserNameFromCharId(charid), affiliation, idtype)
   1522         response = self.xmppConnection.IssueRequest(query, 'set', to=roomName, method_tag='set_affiliation')
   1523         if not response.attributes.getValue('type') == 'result':
   1524             logger.warn('SetAffiliation: Incorrect response')
   1525 
   1526     def GetUsersWithAffiliation(self, channelName, affiliation):
   1527         if GetChannelCategory(channelName) == xmppchatConst.CATEGORY_LOCAL_SUPPRESSED:
   1528             return []
   1529         roomName = self.GetFullRoomName(channelName)
   1530         template = "<query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='{0}'/></query>"
   1531         query = template.format(affiliation)
   1532         response = self.xmppConnection.IssueRequest(query, 'get', to=roomName, method_tag='get_users_with_affiliation')
   1533         if response.attributes.getValue('type') == 'result':
   1534             return GetUsersFromResponse(response)
   1535         else:
   1536             return []
   1537 
   1538     def SetRole(self, channelName, charId, role, reason = None):
   1539         roomName = self.GetFullRoomName(channelName)
   1540         self.xmppConnection.SetRole(roomName, charId, role, reason)
   1541 
   1542     def GetUsersWithRole(self, channelName, role):
   1543         if GetChannelCategory(channelName) == xmppchatConst.CATEGORY_LOCAL_SUPPRESSED:
   1544             return []
   1545         roomName = self.GetFullRoomName(channelName)
   1546         template = "<query xmlns='http://jabber.org/protocol/muc#admin'><item role='{0}'/></query>"
   1547         query = template.format(role)
   1548         response = self.xmppConnection.IssueRequest(query, 'get', to=roomName, method_tag='get_users_with_role')
   1549         if response.attributes.getValue('type') == 'result':
   1550             return GetUsersFromResponse(response)
   1551         else:
   1552             return []
   1553 
   1554     def GetUsersWithTemporaryRestriction(self, channelName, category):
   1555         roomName = self.GetFullRoomName(channelName)
   1556         template = "<query xmlns='urn:xmpp:expiring_record#search' room='{0}' category='{1}'/>"
   1557         query = template.format(roomName, category)
   1558         response = self.xmppConnection.IssueRequest(query, 'get', to=self.hostname, method_tag='get_users_with_temporary_restriction')
   1559         if response.attributes.getValue('type') == 'result':
   1560             return GetUsersFromResponse(response)
   1561         else:
   1562             return []
   1563 
   1564     def GetChannelConfig(self, channel):
   1565         roomName = self.GetFullRoomName(channel)
   1566         query = "<query xmlns='http://jabber.org/protocol/muc#owner'/>"
   1567         response = self.xmppConnection.IssueRequest(query, 'get', to=roomName, method_tag='get_channel_config')
   1568         if response.attributes.getValue('type') == 'result':
   1569             return response
   1570         else:
   1571             return None
   1572 
   1573     def LocalEchoAll(self, msg, charid):
   1574         window = self.GetGroupChatWindow(xmppchatConst.CATEGORY_LOCAL)
   1575         if window:
   1576             if charid is None:
   1577                 charid = localization.GetByLabel('UI/Common/Message')
   1578             window.Receive(charid, msg)
   1579 
   1580     def StoreChannelList(self):
   1581         channelList = []
   1582         for windowName, window in self.groupChatWindows.items():
   1583             if type(window) == stackless.channel:
   1584                 continue
   1585             displayName = window.displayName
   1586             channel = window.GetChannelId()
   1587             channelList.append((windowName, channel, displayName))
   1588 
   1589         settings.char.ui.Set('chatchannels', channelList)
   1590 
   1591     def RemoveChannelFromCurrentChannelsSettings(self, windowName):
   1592         allChannels = settings.char.ui.Get('chatchannels', [])
   1593         allChannelsNew = []
   1594         for eachChannel in allChannels:
   1595             if eachChannel[0] != windowName:
   1596                 allChannelsNew.append(eachChannel)
   1597 
   1598         settings.char.ui.Set('chatchannels', allChannelsNew)
   1599 
   1600     def CreateSessionChannelWindows(self):
   1601         for sessionVariable, groupName in SESSION_CHANGE_CATEGORIES.iteritems():
   1602             if getattr(session, sessionVariable) and not self._IsCategoryMigrated(groupName):
   1603                 self.GetOrCreateGroupChatWindow(groupName, False)
   1604 
   1605     def _RestoreChannelList(self, channelListInSettings):
   1606         if self.verbose:
   1607             logger.debug('Restoring channels from settings')
   1608         for windowName, channel, caption in channelListInSettings:
   1609             if not self.ShouldChannelBeRestoredFromSettings(windowName, channel):
   1610                 continue
   1611             w = self.groupChatWindows.get(windowName, None)
   1612             if w is None or w.destroyed:
   1613                 if self.verbose:
   1614                     logger.debug('Creating window %s for channel %s (%s)', windowName, channel, caption)
   1615                 windowId = 'chatchannel_%s' % windowName
   1616                 window = XmppChatWindow(receiver=channel, isGroupChat=True, channel=channel, name=windowName, caption=caption, windowID=windowId, check_open_minimized=True)
   1617                 window.verbose = self.verbose
   1618                 self.groupChatWindows[windowName] = window
   1619 
   1620     def ShouldChannelBeRestoredFromSettings(self, windowName, channel):
   1621         if windowName in SESSION_CHANGE_CATEGORIES:
   1622             return False
   1623         try:
   1624             if GetChannelCategory(channel) in (xmppchatConst.CATEGORY_PLAYER, xmppchatConst.CATEGORY_SYSTEM):
   1625                 return True
   1626         except IndexError:
   1627             logger.warn('ShouldChannelBeRestoredFromSettings: Could not find channel category: %s' % channel)
   1628 
   1629         return False
   1630 
   1631     def InitChatFilters(self):
   1632         self.characterSettings = sm.GetService('characterSettings')
   1633         chatFilters = self.GetChatFilters()
   1634         UpdateChatFilterVariables(chatFilters.get('bannedWords', []), chatFilters.get('highlightWords', []))
   1635 
   1636     def GetChatFilters(self):
   1637         if session.charid is None:
   1638             return
   1639         if self.chatFilters is None:
   1640             self.chatFilters = self.FetchFiltersFromServer()
   1641         return self.chatFilters
   1642 
   1643     def FetchFiltersFromServer(self):
   1644         chatFilters = {}
   1645         yamlStr = self.characterSettings.Get('chatFilters')
   1646         if yamlStr is not None:
   1647             chatFilters = yaml.load(yamlStr, Loader=yaml.CLoader)
   1648         return chatFilters
   1649 
   1650     def SaveChatFiltersOnServer(self, bannedWords, highlightWords, blinkOnHighlightWords):
   1651         currentFilters = self.GetChatFilters()
   1652         if currentFilters:
   1653             currentBanned = currentFilters.get('bannedWords', [])
   1654             currentHighlight = currentFilters.get('highlightWords')
   1655             currentBlink = currentFilters.get('blinkOnHighlightWords')
   1656             if currentBanned == bannedWords and currentHighlight == highlightWords and currentBlink == blinkOnHighlightWords:
   1657                 return
   1658         self.chatFilters = {'bannedWords': bannedWords,
   1659          'highlightWords': highlightWords,
   1660          'blinkOnHighlightWords': blinkOnHighlightWords}
   1661         yamlFilters = yaml.safe_dump(self.chatFilters)
   1662         self.characterSettings.Save('chatFilters', yamlFilters)
   1663         UpdateChatFilterVariables(bannedWords, highlightWords)
   1664 
   1665     def GetChannelMessages(self):
   1666         messageText = ''
   1667         for window in self.groupChatWindows.itervalues():
   1668             messageText += window.GetMessages()
   1669 
   1670         return messageText
   1671 
   1672     def AskYesNoQuestion(self, question, props, defaultChoice = 1):
   1673         if defaultChoice:
   1674             defaultChoice = uiconst.ID_YES
   1675         else:
   1676             defaultChoice = uiconst.ID_NO
   1677         return prompt_player_fsd_dialog(question, props, uiconst.YESNO, defaultChoice) == uiconst.ID_YES
   1678 
   1679     def ReloadChannel(self, channelID):
   1680         window = self.GetGroupChatWindow(channelID)
   1681         if window:
   1682             window.LoadMessages()
   1683 
   1684     def GetUserData(self, charid):
   1685         userData = self.userData.get(charid)
   1686         if userData and userData.get('corpid', 0) != 0:
   1687             return userData
   1688         user = self.GetUserNameFromCharId(charid)
   1689         retries = 3
   1690         while retries > 0:
   1691             response = self.xmppConnection.GetEveUserData(user)
   1692             if response:
   1693                 userData = self._GetPresenceUserData(charid, response)
   1694                 if userData and userData.get('corpid', 0) != 0:
   1695                     self.userData[charid] = userData
   1696                     return userData
   1697                 if userData:
   1698                     logger.warning('User data has no corp for %s', charid)
   1699                 else:
   1700                     logger.warning('Empty user data for %s', charid)
   1701             else:
   1702                 logger.warning('Fetching user data failed for %s', charid)
   1703             retries -= 1
   1704             uthread2.sleep(0.1)
   1705 
   1706         logger.warning('Returning empty user data for %s', charid)
   1707         return {}
   1708 
   1709     def IsPrivateChatPending(self, channelName):
   1710         return channelName in self.myPendingPrivateChats
   1711 
   1712     def IsSenderMessage(self, sender):
   1713         return sender == self.translatedMsgText
   1714 
   1715     def GetNumPilotsInSystem(self):
   1716         window = self.GetGroupChatWindow('local')
   1717         if window:
   1718             return len(window.GetMembers())
   1719 
   1720     def PokePlayerAboutChatMsgGm(self, charID, channelName):
   1721         sm.RemoteSvc('mailMgr').PokePlayerAboutChatMsgGm(charID, channelName)
   1722 
   1723     def OnCharacterPokedByGM(self, gmCharID, channelName):
   1724         text = localization.GetByLabel('UI/Chat/ChatSvc/GmIsTryingToReachYou', charID=session.charid, gmCharID=gmCharID)
   1725         elelmentName = 'chatchannel_%s' % channelName
   1726         sm.GetService('uiHighlightingService').highlight_ui_element_by_name(elelmentName, text, 7, default_direction=UiHighlightDirections.UP)
   1727 
   1728     def send_honey_event(self, event_name, extra_fields = None):
   1729         event_fields = {'eve.xmpp.connection.has_logged_in': self.hasLoggedInOnce,
   1730          'eve.xmpp.connection.address': self.xmppConnection.hostname,
   1731          'enduser.id': self.xmppConnection.username,
   1732          'eve.xmpp.connection.reconnecting': self.isReconnecting,
   1733          'eve.xmpp.connection.id': id(self.xmppConnection),
   1734          SERVICE_NAME: SERVICE_NAME_PREFIX + 'xmppchatsvc'}
   1735         if extra_fields is not None:
   1736             event_fields.update(extra_fields)
   1737         mhe.send(event_name, event_fields)
   1738 
   1739     @property
   1740     def XmppChatMgr(self):
   1741         return sm.ProxySvc('XmppChatMgr')