Changeset 66

Show
Ignore:
Timestamp:
05/04/04 16:58:44 (5 years ago)
Author:
jajcus
Message:

- code split into several modules
- copyright info added into each module
- support for multiple domains/networks in one jjigw instance
- customizable disco-info identity name ("name" attribute of <network/>

configuration file element)

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/jjigw.dtd

    r45 r66  
    1313<!ATTLIST network  
    1414        jid             CDATA           #REQUIRED 
     15        name            CDATA           #IMPLIED 
    1516        encoding        CDATA           "us-ascii" 
    1617        nicks_8bit      (yes|no)        "no"> 
  • trunk/jjigw.py

    r64 r66  
    11#!/usr/bin/python -u 
    2  
    3 import sys 
    4 import libxml2 
    5 import threading 
    6 import re 
    7 import select 
    8 import socket 
    9 import md5 
    10 import string 
    11 import random 
    12 import signal 
    13 import Queue 
    14 import time 
    15 from types import StringType,UnicodeType 
    16  
    17 from pyxmpp import ClientStream,JID,Iq,Presence,Message,StreamError 
    18 import pyxmpp.jabberd 
    19 from pyxmpp.jabber.muc import MucPresence,MucX,MucUserX,MucItem,MUC_NS,MucStatus 
    20 from pyxmpp.jabber.muc import MucIq,MucAdminQuery,MUC_ADMIN_NS 
    21  
    22 class JJIGWFatalError(RuntimeError): 
    23     pass 
    24  
    25 evil_characters_re=re.compile(r"[\000-\010\013\014\016-\037]") 
    26 def remove_evil_characters(s): 
    27     return evil_characters_re.sub(" ",s) 
    28  
    29 color_re=re.compile(r"\x03\d\d|\x0f") 
    30 def strip_colors(s): 
    31     return color_re.sub("",s) 
    32  
    33 numeric_re=re.compile(r"\d\d\d") 
    34 channel_re=re.compile(r"^[&#+!][^\000 \007 ,:\r\n]{1,49}$") 
    35 nick_re=re.compile(r"^[a-zA-Z\x5b-\x60\x7b-\x7d\[\]\\`_^{|}][a-zA-Z\x5b-\x60\x7b-\x7d\[\]\\`_^{|}0-9-]{0,8}$") 
    36 nick8_re=re.compile(r"^[a-zA-Z\x5b-\x60\x7b-\x7d\[\]\\`_^{|}\x80-\xff][a-zA-Z\x5b-\x60\x7b-\x7d\[\]\\`_^{|}0-9\x80-\xff-]{0,8}$") 
    37  
    38 def escape_node_string(s): 
    39     s=s.replace(",quot,",'"') 
    40     s=s.replace(",amp,","&") 
    41     s=s.replace(",apos,","'") 
    42     s=s.replace(",slash,","/") 
    43     s=s.replace(",lt,","<") 
    44     s=s.replace(",gt,",">") 
    45     s=s.replace(",at,","@") 
    46     return s 
    47  
    48 def unescape_node_string(s): 
    49     s=s.replace('"',",quot,") 
    50     s=s.replace("&",",amp,") 
    51     s=s.replace("'",",apos,") 
    52     s=s.replace("/",",slash,") 
    53     s=s.replace("<",",lt,") 
    54     s=s.replace(">",",gt,") 
    55     s=s.replace("@",",at,") 
    56     return s 
    57  
    58 def node_to_channel(n,encoding): 
    59     s=n.encode(encoding,"strict") 
    60     s=escape_node_string(s) 
    61     if not channel_re.match(s): 
    62         raise ValueError,"Bad channel name: %r" % (s,) 
    63     return s 
    64  
    65 def channel_to_node(ch,encoding): 
    66     s=unescape_node_string(ch) 
    67     n=unicode(s,encoding,"strict") 
    68     return n 
    69  
    70 def node_to_nick(n,encoding,network): 
    71     s=n.encode(encoding,"strict") 
    72     s=escape_node_string(s) 
    73     if not network.valid_nick(s): 
    74         raise ValueError,"Bad nick name: %r" % (s,) 
    75     return s 
    76  
    77 def nick_to_node(ch,encoding): 
    78     s=unescape_node_string(ch) 
    79     n=unicode(s,encoding,"strict") 
    80     return n 
     2
     3#  Jajcus' Jabber to IRC Gateway 
     4#  Copyright (C) 2004  Jacek Konieczny <jajcus@bnet.pl> 
     5
     6#  This program is free software; you can redistribute it and/or modify 
     7#  it under the terms of the GNU General Public License as published by 
     8#  the Free Software Foundation; either version 2 of the License, or 
     9#  (at your option) any later version. 
     10
     11#  This program is distributed in the hope that it will be useful, 
     12#  but WITHOUT ANY WARRANTY; without even the implied warranty of 
     13#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     14#  GNU General Public License for more details. 
     15
     16#  You should have received a copy of the GNU General Public License along 
     17#  with this program; if not, write to the Free Software Foundation, Inc., 
     18#  59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. 
    8119 
    8220 
    83 irc_translate_table=string.maketrans( 
    84         string.ascii_uppercase+"[]\\~", 
    85         string.ascii_lowercase+"{}|^") 
    86  
    87 def normalize(s): 
    88     return s.translate(irc_translate_table) 
    89  
    90 class ConnectConfig: 
    91     def __init__(self,node): 
    92         self.node=node 
    93         self.host=node.xpathEval("host")[0].getContent() 
    94         self.port=int(node.xpathEval("port")[0].getContent()) 
    95         self.secret=node.xpathEval("secret")[0].getContent() 
    96  
    97 class SPIdentDConfig: 
    98     def __init__(self,node): 
    99         node=node.xpathEval("socket")[0] 
    100         self.socket=node.getContent() 
    101  
    102 class ServerConfig: 
    103     def __init__(self,node): 
    104         self.host=node.getContent() 
    105         self.port=node.prop("port") 
    106         try: 
    107             self.port=int(self.port) 
    108             if self.port<1 or self.port>65535: 
    109                 raise ValueError 
    110         except ValueError: 
    111             print >>sys.stderr,"Bad port value: %r, using default: 6667" % (self.port,) 
    112             self.port=6667 
    113     def __repr__(self): 
    114         return "<ServerConfig %s:%s/>" % (self.host,self.port) 
    115  
    116 class ChannelConfig: 
    117     def __init__(self,node): 
    118         self.name=node.getContent() 
    119         self.encoding=node.prop("encoding") 
    120  
    121 class NetworkConfig: 
    122     def __init__(self,node): 
    123         self.node=node 
    124         self.jid=JID(node.prop("jid")) 
    125         servers=node.xpathEval("server") 
    126         self.servers=[] 
    127         for s in servers: 
    128             self.servers.append(ServerConfig(s)) 
    129         channels=node.xpathEval("channel") 
    130         self.channels={} 
    131         if channels: 
    132             for c in channels: 
    133                 ch=ChannelConfig(c) 
    134                 self.channels[normalize(ch.name)]=ch 
    135         self.default_encoding=node.prop("encoding") 
    136         self.nicks_8bit=node.prop("nicks_8bit") 
    137     def get_servers(self): 
    138         r=self.servers 
    139         self.servers=self.servers[-1:]+self.servers[1:] 
    140         return r 
    141     def get_channel_config(self,channel): 
    142         return self.channels.get(normalize(channel)) 
    143     def valid_nick(self,s): 
    144         if self.nicks_8bit: 
    145             m=nick8_re.match(s) 
    146         else: 
    147             m=nick_re.match(s) 
    148         if m: 
    149             return 1 
    150         else: 
    151             return 0 
    152  
    153 class Config: 
    154     def __init__(self,filename): 
    155         self.doc=None 
    156         parser=libxml2.createFileParserCtxt(filename) 
    157         parser.validate(1) 
    158         parser.parseDocument() 
    159         if not parser.isValid(): 
    160             raise JJIGWFatalError,"Invalid configuration" 
    161         self.doc=parser.doc() 
    162         self.connect=ConnectConfig(self.doc.xpathEval("jjigw/connect")[0]) 
    163         self.network=NetworkConfig(self.doc.xpathEval("jjigw/network")[0]) 
    164         spidentd=self.doc.xpathEval("jjigw/spidentd") 
    165         if spidentd: 
    166             self.spidentd=SPIdentDConfig(spidentd[0]) 
    167         else: 
    168             self.spidentd=None 
    169     def __del__(self): 
    170         if self.doc: 
    171             self.doc.freeDoc() 
    172  
    173  
    174 class Request: 
    175     def __init__(self,command,stanza,args=None): 
    176         self.command=command 
    177         self.stanza=stanza 
    178         self.args=args 
    179     def match(self,commands,args=None): 
    180         if type(commands) in (StringType,UnicodeType): 
    181             commands=[commands] 
    182         for c in commands: 
    183             if not self.command==c: 
    184                 continue 
    185             if args and not self.args==args: 
    186                 continue 
    187             return 1 
    188         return 0 
    189  
    190 class RequestQueue: 
    191     def __init__(self,maxsize): 
    192         self.maxsize=maxsize 
    193         self.requests=[] 
    194     def get(self,commands,args=None): 
    195         for r in self.requests: 
    196             if r.match(commands): 
    197                 try: 
    198                     self.requests.remove(r) 
    199                 except ValueError: 
    200                     pass 
    201                 return r 
    202         return None 
    203     def add(self,command,stanza,args=None): 
    204         r=Request(command,stanza,args) 
    205         self.requests.append(r) 
    206         if len(self.requests)>10: 
    207             self.requests=self.requests[-10:] 
    208  
    209 class IRCUser: 
    210     def __init__(self,session,nick,user="",host=""): 
    211         self.sync_delay=0 
    212         self.session=session 
    213         if "!" in nick: 
    214             nick,tmp=nick.split("!",1) 
    215             if "@" in tmp: 
    216                 user,host=tmp.split("@",1) 
    217             else: 
    218                 user=tmp 
    219                 host="" 
    220         self.nick=nick 
    221         self.user=user 
    222         self.host=host 
    223         self.mode={} 
    224         self.channels={} 
    225         self.current_thread=None 
    226  
    227     def descr(self): 
    228         if self.user and self.host: 
    229             return "%s(%s@%s)" % (self.nick,self.user,self.host) 
    230         else: 
    231             return self.nick 
    232  
    233     def sync_in_channel(self,channel,status=None): 
    234         if self.sync_delay>0: 
    235             return 
    236         elif self.sync_delay<0: 
    237             self.debug("Warning: %r.sync_delay<0" % (self,)) 
    238         return channel.sync_user(self,status=status) 
    239  
    240     def join_channel(self,channel): 
    241         self.channels[normalize(channel.name)]=channel 
    242         self.sync_in_channel(channel) 
    243  
    244     def leave_channel(self,channel,status=None): 
    245         try: 
    246             del self.channels[normalize(channel.name)] 
    247             self.sync_in_channel(channel,status=status) 
    248         except KeyError: 
    249             pass 
    250  
    251     def leave_all(self): 
    252         for channel in self.channels.values(): 
    253             self.leave_channel(channel) 
    254  
    255     def sync_all(self): 
    256         for channel in self.channels.values(): 
    257             self.sync_in_channel(channel) 
    258  
    259     def whoreply(self,params): 
    260         if params[4]!=self.nick: 
    261             return 
    262         if len(params)!=7: 
    263             return 
    264         channel,user,host,server,nick,flags,rest=params 
    265         fullname=rest.split(None,1)[1] 
    266         if channel and channel!="*": 
    267             channel=self.session.channels.get(normalize(channel)) 
    268             if not channel: 
    269                 self.debug("Ignoring WHO reply: %r - unknown channel" % (params,)) 
    270                 return 
    271         else: 
    272             channel=None 
    273         self.sync_delay+=1 
    274         try: 
    275             self.nick=nick 
    276             self.host=host 
    277             self.user=user 
    278             if channel: 
    279                 self.join_channel(channel) 
    280                 if "@" in flags: 
    281                     channel.set_mode("o",self) 
    282                 elif "+" in flags: 
    283                     channel.set_mode("v",self) 
    284                 else: 
    285                     channel.reset_mode("o",self) 
    286                     channel.reset_mode("v",self) 
    287             if "G" in flags: 
    288                 self.mode["a"]=1 
    289             else: 
    290                 self.mode["a"]=0 
    291         finally: 
    292             self.sync_delay-=1 
    293         if channel: 
    294             channel.sync_user(self) 
    295  
    296     def jid(self): 
    297         return JID(nick_to_node(self.nick,self.session.default_encoding), 
    298                 self.session.network.jid.domain, 
    299                 unicode(self.user+'@'+self.host,self.session.default_encoding,"replace")) 
    300  
    301     def __repr__(self): 
    302         return "<IRCUser %r>" % (self.nick,) 
    303  
    304     def debug(self,msg): 
    305         return self.session.debug(msg) 
    306  
    307 class Channel: 
    308     toggle_modes="aimnqpsrt" 
    309     arg_modes="kl" 
    310     multiarg_modes="OovbeI" 
    311     def __init__(self,session,name): 
    312         if not channel_re.match(name): 
    313             raise ValueError,"Bad channel name" 
    314         self.name=name 
    315         self.session=session 
    316         self.state=None 
    317         self.room_jid=None 
    318         self.config=session.network.get_channel_config(name) 
    319         if self.config and self.config.encoding: 
    320             self.encoding=self.config.encoding 
    321         else: 
    322             self.encoding=session.default_encoding 
    323         self.modes={} 
    324         self.users=[] 
    325         self.muc=0 
    326         self.requests=RequestQueue(10) 
    327  
    328     def sync_user(self,user,status=None): 
    329         if user.channels.has_key(normalize(self.name)): 
    330             if user not in self.users: 
    331                 self.users.append(user) 
    332         else: 
    333             for m in self.multiarg_modes: 
    334                 ul=self.modes.get(m,[]) 
    335                 if user in ul: 
    336                     ul.remove(user) 
    337             if user in self.users: 
    338                 self.users.remove(user) 
    339                 self.send_notice_message(u"%s has quit"  
    340                         % (unicode(user.nick,self.encoding,"replace"),)) 
    341         if self.state: 
    342             p=self.get_user_presence(user,status=status) 
    343             self.session.component.send(p) 
    344  
    345     def send_notice_message(self,msg,not_in_muc=1): 
    346         if not self.state or (self.muc and not_in_muc): 
    347             return 
    348         m=Message(fr=self.room_jid.bare(),to=self.session.jid,type="groupchat",body=msg) 
    349         self.session.component.send(m) 
    350  
    351     def join(self,stanza): 
    352         if self.state: 
    353             self.debug("Channel %r not in the initial state, not joining!" % (self.name,)) 
    354             p=stanza.make_error_response(stanza,"bad-request") 
    355             self.session.component.send(p) 
    356             return 
    357         self.room_jid=stanza.get_to() 
    358         self.debug("Joining channel %r" % (self.name,)) 
    359         self.session.send("JOIN %s" % (self.name,)) 
    360         self.requests.add("JOIN",stanza) 
    361         self.state="join" 
    362         if stanza.get_join_info(): 
    363             self.muc=1 
    364  
    365     def leave(self,stanza): 
    366         status=stanza.get_status() 
    367         if not self.state: 
    368             self.debug("Channel %r in the initial state - nothing to do." % (self.name,)) 
    369         else: 
    370             if status: 
    371                 self.session.send("PART %s" % (self.name,)) 
    372             else: 
    373                 self.session.send("PART %s :%s" % (self.name, 
    374                         status.encode(self.encoding,"replace"))) 
    375             self.state=None 
    376         p=MucPresence(type="unavailable",fr=stanza.get_to(),to=stanza.get_from(),status=status) 
    377         self.session.component.send(p) 
    378         for u in self.users: 
    379             u.leave_room(self) 
    380         self.state=None 
    381  
    382     def prefix_to_jid(self,prefix): 
    383         if "!" in prefix: 
    384             return self.nick_to_jid(prefix.split("!")[0]) 
    385         return self.nick_to_jid(prefix) 
    386  
    387     def nick_to_jid(self,nick): 
    388         return JID(self.room_jid.node,self.room_jid.domain, 
    389                 unicode(nick,self.encoding,"replace")) 
    390  
    391     def get_user_presence(self,user,nick=None,actor=None,reason=None,status=None): 
    392         if self.state and user in self.users: 
    393             p=MucPresence(fr=self.nick_to_jid(user.nick),to=self.session.jid) 
    394         else: 
    395             p=MucPresence(type="unavailable",fr=self.nick_to_jid(user.nick),to=self.session.jid) 
    396         if self.muc: 
    397             if user in self.modes.get("o",[]): 
    398                 aff="admin" 
    399                 role="moderator" 
    400             elif user in self.modes.get("v",[]): 
    401                 aff="member" 
    402                 role="participant" 
    403             elif self.modes.get("m"): 
    404                 aff="none" 
    405                 role="visitor" 
    406             elif user in self.users: 
    407                 aff="none" 
    408                 role="participant" 
    409             else: 
    410                 aff="none" 
    411                 role="none" 
    412             ui=p.make_muc_userinfo() 
    413             if nick: 
    414                 nick=unicode(user.nick,self.encoding,"replace") 
    415             it=MucItem(aff,role,user.jid(),nick=nick,actor=actor,reason=reason) 
    416             ui.add_item(it) 
    417             if status: 
    418                 ui.add_item(MucStatus(status)) 
    419         return p 
    420  
    421     def nick_changed(self,oldnick,user): 
    422         p_unaval=self.get_user_presence(user,nick=user.nick,status=303) 
    423         p_unaval.set_type("unavailable") 
    424         p_unaval.set_show(None) 
    425         p_unaval.set_status(None) 
    426         p_unaval.set_from(self.nick_to_jid(oldnick)) 
    427         p_aval=self.get_user_presence(user,status=303) 
    428         self.session.component.send(p_unaval) 
    429         self.session.component.send(p_aval) 
    430         self.send_notice_message(u"%s is now known as %s"  
    431                 % (unicode(oldnick,self.encoding,"replace"), 
    432                     unicode(user.nick,self.encoding,"replace"))) 
    433          
    434     def set_mode(self,mode,arg): 
    435         if mode in self.toggle_modes: 
    436             self.modes[mode]=1 
    437         elif mode in self.arg_modes: 
    438             self.modes[mode]=arg 
    439         elif mode in self.multiarg_modes: 
    440             if self.modes.has_key(mode): 
    441                 self.modes[mode].append(arg) 
    442             else: 
    443                 self.modes[mode]=[arg] 
    444  
    445     def reset_mode(self,mode,arg): 
    446         try: 
    447             if mode in self.toggle_modes: 
    448                 del self.modes[mode] 
    449             elif mode in self.arg_modes: 
    450                 del self.modes[mode] 
    451             elif mode in self.multiarg_modes: 
    452                 if self.modes.has_key(mode): 
    453                     self.modes[mode].remove(arg) 
    454                     if not self.modes[mode]: 
    455                         del self.modes[mode] 
    456         except (KeyError,ValueError): 
    457             pass 
    458  
    459     def irc_cmd_324(self,prefix,command,params): # RPL_CHANNELMODEIS 
    460         for m in self.toggle_modes: 
    461             try: 
    462                 del self.modes[m] 
    463             except KeyError: 
    464                 pass 
    465         self.irc_mode_changed(prefix,command,params) 
    466          
    467     def irc_cmd_482(self,prefix,command,params): # ERR_CHANOPRIVSNEEDED 
    468         self.irc_error_response(prefix,command,params,["TOPIC","KICK","MODE"],"forbidden") 
    469  
    470     def irc_cmd_461(self,prefix,command,params): # ERR_NEEDMOREPARAMS 
    471         self.irc_error_response(prefix,command,params,["TOPIC","KICK","MODE"],"bad-request") 
    472  
    473     def irc_cmd_403(self,prefix,command,params): # ERR_NOSUCHCHANNEL 
    474         self.irc_error_response(prefix,command,params,["KICK"],"recipient-unavailable") 
    475      
    476     def irc_cmd_476(self,prefix,command,params): # ERR_BADCHANMASK 
    477         self.irc_error_response(prefix,command,params,["KICK"],"bad-request") 
    478      
    479     def irc_cmd_441(self,prefix,command,params): # ERR_USERNOTINCHANNEL 
    480         self.irc_error_response(prefix,command,params,["KICK","MODE"],"item-not-found") 
    481          
    482     def irc_cmd_442(self,prefix,command,params): # ERR_NOTONCHANNEL 
    483         self.irc_error_response(prefix,command,params,["TOPIC","KICK"],"forbidden") 
    484  
    485     def irc_cmd_472(self,prefix,command,params): # ERR_UNKNOWNMODE 
    486         self.irc_error_response(prefix,command,params,["MODE"],"feature-not-implemented") 
    487  
    488     def irc_cmd_477(self,prefix,command,params): # ERR_NOCHANMODES 
    489         self.irc_error_response(prefix,command,params,["TOPIC","MODE"],"not-acceptable") 
    490  
    491     def irc_error_response(self,prefix,command,params,requests,condition): 
    492         r=self.requests.get(requests) 
    493         if r: 
    494             m=r.stanza.make_error_response(condition) 
    495         else: 
    496             m=Message(fr=self.room_jid.bare(),to=self.session.jid, 
    497                     type="error", error_cond=condition) 
    498         self.session.component.send(m) 
    499  
    500     def irc_cmd_331(self,prefix,command,params): # RPL_NOTOPIC 
    501         m=Message(fr=self.room_jid.bare(),to=self.session.jid, type="groupchat", subject=u"") 
    502         self.session.component.send(m) 
    503          
    504     def irc_cmd_332(self,prefix,command,params): # RPL_TOPIC 
    505         topic=remove_evil_characters(params[1]) 
    506         m=Message(fr=self.room_jid.bare(),to=self.session.jid, 
    507                 type="groupchat", subject=unicode(topic,self.encoding,"replace")) 
    508         self.session.component.send(m) 
    509  
    510     def irc_cmd_TOPIC(self,prefix,command,params): 
    511         self.requests.get("TOPIC") 
    512         topic=remove_evil_characters(params[1]) 
    513         m=Message(fr=self.prefix_to_jid(prefix),to=self.session.jid, 
    514                 type="groupchat", subject=unicode(topic,self.encoding,"replace")) 
    515         self.session.component.send(m) 
    516          
    517     def irc_cmd_MODE(self,prefix,command,params): 
    518         if len(params)<2: 
    519             self.debug("No parameters in received MODE") 
    520             return 
    521         params_str=string.join(params[2:]," ").strip() 
    522         if params_str: 
    523             params_str=" "+params_str 
    524         if "!" in prefix: 
    525             nick,iuser=prefix.split("!",1) 
    526             iuser="(%s)" % (iuser,) 
    527         else: 
    528             nick,iuser=prefix,"" 
    529         self.send_notice_message(u"Mode change: [%s%s] by %s%s"  
    530                 % (unicode(params[1],self.encoding,"replace"), 
    531                         unicode(params_str,self.encoding,"replace"), 
    532                         unicode(nick,self.encoding,"replace"), 
    533                         unicode(iuser,self.encoding,"replace")), 
    534                 0) 
    535         if self.session.check_prefix(prefix) and len(params)>=3: 
    536             r=self.requests.get("MODE",string.join(params[1:]," ")) 
    537             if r: 
    538                 reply=r.stanza.make_result_response() 
    539                 self.session.component.send(reply) 
    540         self.irc_mode_changed(prefix,command,params) 
    541  
    542     def irc_mode_changed(self,prefix,command,params): 
    543         actor=self.session.get_user(prefix) 
    544         modes=params[1] 
    545         params=params[2:] 
    546         pm=None 
    547         for m in modes: 
    548             if m in "+-": 
    549                 pm=m 
    550                 continue 
    551             elif not pm: 
    552                 self.debug("Not '+' or '-' before '%s' in received MODE" % (m,)) 
    553                 continue 
    554             elif m in self.arg_modes or m in self.multiarg_modes: 
    555                 if not len(params): 
    556                     self.debug("No argument for mode '%s' in received MODE" % (m,)) 
    557                     continue 
    558                 arg=params.pop(0) 
    559             elif m in self.toggle_modes: 
    560                 arg=None 
    561             else: 
    562                 self.debug("Unknown mode '%s' in received MODE" % (m,)) 
    563                 continue 
    564             if m in "oOv": 
    565                 arg=self.session.get_user(arg) 
    566                 if not arg: 
    567                     continue 
    568             if pm=="+": 
    569                 self.set_mode(m,arg) 
    570             else: 
    571                 self.reset_mode(m,arg) 
    572             if m in "oOv": 
    573                 self.user_mode_changed(arg,actor,m) 
    574             elif m=="m": 
    575                 for u in self.users: 
    576                     self.sync_user(u) 
    577  
    578     def user_mode_changed(self,user,actor,mode): 
    579         if actor: 
    580             actor_jid=self.nick_to_jid(actor.nick) 
    581         else: 
    582             actor_jid=None 
    583         p=self.get_user_presence(user,actor_jid) 
    584         if actor: 
    585             by=u" by %s" % (unicode(actor.nick,self.encoding,"replace"),) 
    586         else: 
    587             by=u"" 
    588         self.session.component.send(p) 
    589         if mode=="v": 
    590             self.send_notice_message(u"%s was granted voice%s"  
    591                     % (unicode(user.nick,self.encoding,"replace"),by)) 
    592         elif mode=="o": 
    593             self.send_notice_message(u"%s was granted operator status%s"  
    594                     % (unicode(user.nick,self.encoding,"replace"),by)) 
    595         elif mode=="O": 
    596             self.send_notice_message(u"%s was granted got owner status%s"  
    597                     % (unicode(user.nick,self.encoding,"replace"),by)) 
    598  
    599     def irc_cmd_JOIN(self,prefix,command,params): 
    600         nprefix=normalize(prefix) 
    601         nnick=normalize(self.session.nick) 
    602         if nprefix==nnick or nprefix.startswith(nnick+"!"): 
    603             if self.state=="join": 
    604                 self.debug("Channel %r joined!" % (self.name,)) 
    605                 self.session.user.sync_delay+=1 
    606                 try: 
    607                     self.session.user.join_channel(self) 
    608                 finally: 
    609                     self.session.user.sync_delay-=1 
    610                 self.state="joined" 
    611                 self.requests.get("JOIN") 
    612                 self.session.send("MODE %s" % (self.name,)) 
    613                 self.session.send("WHO %s" % (self.name,)) 
    614         else: 
    615             user=self.session.get_user(prefix) 
    616             user.join_channel(self) 
    617             self.send_notice_message(u"%s has joined"  
    618                     % (unicode(user.nick,self.encoding,"replace"),)) 
    619             self.session.send("WHO %s" % (user.nick,)) 
    620  
    621     def irc_cmd_PART(self,prefix,command,params): 
    622         user=self.session.get_user(prefix) 
    623         try: 
    624             self.users.remove(user) 
    625         except ValueError: 
    626             pass 
    627         user.leave_channel(self) 
    628         self.send_notice_message(u"%s has left"  
    629                 % (unicode(user.nick,self.encoding,"replace"),)) 
    630  
    631     def irc_cmd_KICK(self,prefix,command,params): 
    632         actor=self.session.get_user(prefix) 
    633         user=self.session.get_user(params[1]) 
    634         try: 
    635             self.users.remove(user) 
    636         except ValueError: 
    637             pass 
    638         self.send_notice_message(u"%s was kicked by %s"  
    639                 % (unicode(user.descr(),self.encoding,"replace"), 
    640                     unicode(actor.descr(),self.encoding,"replace")), 
    641                 0) 
    642         user.leave_channel(self,status=307) 
    643         if user and self.session.check_prefix(prefix): 
    644             r=self.requests.get("KICK",user.nick) 
    645             if r: 
    646                 iq=r.stanza.make_result_response() 
    647                 self.session.component.send(iq) 
    648  
    649     def irc_cmd_PRIVMSG(self,prefix,command,params): 
    650         self.irc_message(prefix,command,params) 
    651  
    652     def irc_cmd_NOTICE(self,prefix,command,params): 
    653         self.irc_message(prefix,command,params) 
    654  
    655     def irc_message(self,prefix,command,params): 
    656         if not self.state or len(params)<2: 
    657             self.debug("ignoring it") 
    658             return 
    659         body=unicode(params[1],self.encoding,"replace") 
    660         if body[0]=="\001" and body[-1]=="\001": 
    661             self.CTCP(prefix,body[1:-1]) 
    662         else: 
    663             m=Message(type="groupchat",fr=self.prefix_to_jid(prefix),to=self.session.jid, 
    664                     body=remove_evil_characters(strip_colors(body))) 
    665             self.session.component.send(m) 
    666      
    667     def CTCP(self,prefix,command): 
    668         if " " in command: 
    669             command,arg=command.split(" ",1) 
    670         else: 
    671             arg=None 
    672         if command=="ACTION": 
    673             m=Message(type="groupchat",fr=self.prefix_to_jid(prefix),to=self.session.jid, 
    674                     body="/me "+remove_evil_characters(strip_colors(arg))) 
    675             self.session.component.send(m) 
    676         else: 
    677             self.debug("Unknown CTCP command: %r %r" % (command,arg)) 
    678  
    679     def change_topic(self,topic,stanza): 
    680         topic=topic.encode(self.encoding,"replace") 
    681         topic=topic.replace("\n"," ").replace("\r"," ") 
    682         self.session.send("TOPIC %s :%s" % (self.name,topic)) 
    683         self.requests.add("TOPIC",stanza) 
    684  
    685     def kick_user(self,nick,reason,stanza): 
    686         nick=nick.encode(self.encoding,"strict") 
    687         self.session.send("KICK %s %s :%s" % (self.name,nick,reason)) 
    688         self.requests.add("KICK",stanza,nick) 
    689   
    690     def op_user(self,nick,stanza): 
    691         nick=nick.encode(self.encoding,"strict") 
    692         user=self.session.users.get(normalize(nick)) 
    693         if not user in self.users: 
    694            r=stanza.make_error_response("item-not-found") 
    695            self.session.component.send(r) 
    696            return 
    697         if user in self.modes.get("o",[]): 
    698            r=stanza.make_result_response() 
    699            self.session.component.send(r) 
    700            return 
    701         if user in self.modes.get("v",[]): 
    702             change="-v+o %s %s" % (nick,nick) 
    703         else: 
    704             change="+o "+nick 
    705         self.session.send("MODE %s %s" % (self.name,change)) 
    706         self.requests.add("MODE",stanza,change) 
    707  
    708     def voice_user(self,nick,stanza): 
    709         nick=nick.encode(self.encoding,"strict") 
    710         user=self.session.users.get(normalize(nick)) 
    711         if not user in self.users: 
    712            r=stanza.make_error_response("item-not-found") 
    713            self.session.component.send(r) 
    714            return 
    715         if user in self.modes.get("v",[]): 
    716            r=stanza.make_result_response() 
    717            self.session.component.send(r) 
    718            return 
    719         if user in self.modes.get("o",[]): 
    720             change="-o+v %s %s" % (nick,nick) 
    721         else: 
    722             change="+v "+nick 
    723         self.session.send("MODE %s %s" % (self.name,change)) 
    724         self.requests.add("MODE",stanza,change) 
    725  
    726     def devoice_user(self,nick,stanza): 
    727         nick=nick.encode(self.encoding,"strict") 
    728         user=self.session.users.get(normalize(nick)) 
    729         if not user in self.users: 
    730            r=stanza.make_error_response("item-not-found") 
    731            self.session.component.send(r) 
    732            return 
    733         if user in self.modes.get("v",[]) and user in self.modes.get("o",[]): 
    734            change="-o-v %s %s" % (nick,nick) 
    735         elif user in self.modes.get("o",[]): 
    736             change="-o "+nick 
    737         elif user in self.modes.get("v",[]): 
    738             change="-v "+nick 
    739         else: 
    740            r=stanza.make_result_response() 
    741            self.session.component.send(r) 
    742            return 
    743         self.session.send("MODE %s %s" % (self.name,change)) 
    744         self.requests.add("MODE",stanza,change) 
    745  
    746     def __repr__(self): 
    747         return "<IRCChannel %r>" % (self.name,) 
    748  
    749     def debug(self,msg): 
    750         return self.session.debug(msg) 
    751  
    752  
    753 class IRCSession: 
    754     commands_dont_show=[] 
    755     def __init__(self,component,config,jid,nick): 
    756         self.component=component 
    757         self.config=config 
    758         self.network=config.network 
    759         self.default_encoding=self.network.default_encoding 
    760         self.conninfo=None 
    761         nick=nick.encode(self.default_encoding,"strict") 
    762         if not self.network.valid_nick(nick): 
    763             raise ValueError,"Bad nickname" 
    764         self.jid=jid 
    765         self.nick=nick 
    766         self.thread=threading.Thread(name=u"%s on %s as %s" % (jid,config.network.jid,nick), 
    767                 target=self.thread_run) 
    768         self.thread.setDaemon(1) 
    769         self.exit=None 
    770         self.exited=0 
    771         self.socket=None 
    772         self.lock=threading.RLock() 
    773         self.cond=threading.Condition(self.lock) 
    774         self.servers_left=self.network.get_servers() 
    775         self.input_buffer="" 
    776         self.used_for=[] 
    777         self.server=None 
    778         self.join_requests=[] 
    779         self.messages_to_channel=[] 
    780         self.messages_to_user=[] 
    781         self.ready=0 
    782         self.channels={} 
    783         self.users={} 
    784         self.user=IRCUser(self,nick) 
    785         self.thread.start() 
    786  
    787     def register_user(self,user): 
    788         self.lock.acquire() 
    789         try: 
    790             self.users[normalize(user.nick)]=user 
    791         finally: 
    792             self.lock.release() 
    793  
    794     def unregister_user(self,user): 
    795         self.lock.acquire() 
    796         try: 
    797             nnick=normalize(user.nick) 
    798             if self.users.get(nnick)==user: 
    799                 del self.users[nnick] 
    800         finally: 
    801             self.lock.release() 
    802  
    803     def rename_user(self,user,new_nick): 
    804         self.lock.acquire() 
    805         try: 
    806             self.users[normalize(new_nick)]=user 
    807             try: 
    808                 del self.users[normalize(user.nick)] 
    809             except KeyError: 
    810                 pass 
    811             user.nick=new_nick 
    812         finally: 
    813             self.lock.release() 
    814  
    815     def get_user(self,prefix,create=1): 
    816         if "!" in prefix: 
    817             nick=prefix.split("!",1)[0] 
    818         else: 
    819             nick=prefix 
    820         if not self.network.valid_nick(nick): 
    821             return None 
    822         nnick=normalize(nick) 
    823         if self.users.has_key(nnick): 
    824             return self.users[nnick] 
    825         if not create: 
    826             return None 
    827         user=IRCUser(self,prefix) 
    828         self.register_user(user) 
    829         return user 
    830  
    831     def check_nick(self,nick): 
    832         nick=nick.encode(self.default_encoding) 
    833         if normalize(nick)==normalize(self.nick): 
    834             return 1 
    835         else: 
    836             return 0 
    837  
    838     def check_prefix(self,prefix): 
    839         if "!" in prefix: 
    840             nick=prefix.split("!",1)[0] 
    841         else: 
    842             nick=prefix 
    843         return normalize(nick)==normalize(self.nick) 
    844  
    845     def prefix_to_jid(self,prefix): 
    846         if channel_re.match(prefix): 
    847             node=channel_to_node(prefix,self.default_encoding) 
    848             return JID(node,self.network.jid.domain,None) 
    849         else: 
    850             if "!" in prefix: 
    851                 nick,user=prefix.split("!",1) 
    852             else: 
    853                 nick=prefix  
    854                 user="" 
    855             node=nick_to_node(nick,self.default_encoding) 
    856             resource=unicode(user,self.default_encoding,"replace") 
    857             return JID(node,self.network.jid.domain,resource) 
    858  
    859     def thread_run(self): 
    860         clean_exit=1 
    861         try: 
    862             self.thread_loop() 
    863         except: 
    864             clean_exit=0 
    865             self.print_exception() 
    866         self.lock.acquire() 
    867         try: 
    868             if not self.exited and self.socket: 
    869                 if clean_exit and self.component.shutdown: 
    870                     self._send("QUIT :JJIGW shutdown") 
    871                 elif clean_exit and self.exit: 
    872                     self._send("QUIT :%s" % (self.exit.encode(self.default_encoding,"replace"))) 
    873                 else: 
    874                     self._send("QUIT :Internal JJIGW error") 
    875                 self.exited=1 
    876             if self.socket: 
    877                 try: 
    878                     self.socket.close() 
    879                 except: 
    880                     pass 
    881                 self.socket=None 
    882             try: 
    883                 del self.component.irc_sessions[self.jid.as_unicode()] 
    884             except KeyError: 
    885                 pass 
    886         finally: 
    887             self.lock.release() 
    888         for j in self.used_for: 
    889             p=Presence(fr=j,to=self.jid,type="unavailable") 
    890             self.component.send(p) 
    891         self.used_for=[] 
    892      
    893     def thread_loop(self): 
    894         self.debug("thread_loop()") 
    895         while not self.exit and not self.component.shutdown: 
    896             self.lock.acquire() 
    897             try: 
    898                 if self.socket is None: 
    899                     self._try_connect() 
    900                 sock=self.socket 
    901                 self.lock.release() 
    902                 if sock is None: 
    903                     continue 
    904                 id,od,ed=select.select([sock],[],[sock],1) 
    905                 self.lock.acquire() 
    906                 if self.socket in id: 
    907                     self.input_buffer+=self.socket.recv(1024) 
    908                     while self.input_buffer.find("\r\n")>-1: 
    909                         input,self.input_buffer=self.input_buffer.split("\r\n",1) 
    910                         self._safe_process_input(input) 
    911             finally: 
    912                 self.lock.release() 
    913         self.lock.acquire() 
    914         try: 
    915             if self.socket: 
    916                 self.socket.close() 
    917                 self.socket=None 
    918             if self.conninfo: 
    919                 self.component.unregister_connection(self.conninfo) 
    920                 self.conninfo=None 
    921         finally: 
    922             self.lock.release() 
    923  
    924     def _try_connect(self): 
    925         if not self.servers_left: 
    926             self.debug("No servers left, quitting") 
    927             self.exit="No servers left, quitting" 
    928             return 
    929         if self.conninfo: 
    930             self.component.unregister_connection(self.conninfo) 
    931             self.conninfo=None 
    932         if self.socket: 
    933             self.socket.close() 
    934             self.socket=None 
    935         server=self.servers_left.pop(0) 
    936         self.debug("Trying to connect to %r" % (server,)) 
    937         try: 
    938             self.socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 
    939             self.socket.connect((server.host,server.port)) 
    940         except (IOError,OSError,socket.error),err: 
    941             self.debug("Server connect error: %r" % (err,)) 
    942             if self.socket: 
    943                 try: 
    944                     self.socket.close() 
    945                     if self.conninfo: 
    946                         self.component.unregister_connection(self.conninfo) 
    947                         self.conninfo=None 
    948                 except: 
    949                     pass 
    950             self.socket=None 
    951             return 
    952         self._send("NICK %s" % (self.nick,)) 
    953         user=md5.new(self.jid.bare().as_string()).hexdigest()[:64] 
    954         self.conninfo=ConnectionInfo(self.socket,user) 
    955         self.component.register_connection(self.conninfo) 
    956         self._send("USER %s 0 * :JJIGW User %s" % (user,user)) 
    957         self.server=server 
    958         self.cond.notify() 
    959  
    960     def _send(self,str): 
    961         if self.socket and not self.exited: 
    962             self.debug("IRC OUT: %r" % (str,)) 
    963             self.socket.send(str+"\r\n") 
    964         else: 
    965             self.debug("ignoring out: %r" % (str,)) 
    966  
    967     def send(self,str): 
    968         self.lock.acquire() 
    969         try: 
    970             self._send(str) 
    971         finally: 
    972             self.lock.release() 
    973  
    974     def _safe_process_input(self,input): 
    975         try: 
    976             self._process_input(input) 
    977         except: 
    978             self.print_exception() 
    979      
    980     def _process_input(self,input): 
    981         self.debug("Server message: %r" % (input,)) 
    982         split=input.split(" ") 
    983         if split[0].startswith(":"): 
    984             prefix=split[0][1:] 
    985             split=split[1:] 
    986         else: 
    987             prefix=None 
    988         if split: 
    989             command=split[0] 
    990             split=split[1:] 
    991         else: 
    992             command=None 
    993         params=[] 
    994         while split: 
    995             if split[0].startswith(":"): 
    996                 params.append(string.join(split," ")[1:]) 
    997                 break 
    998             params.append(split[0]) 
    999             split=split[1:] 
    1000         if command and numeric_re.match(command): 
    1001             params=params[1:] 
    1002         self.lock.release() 
    1003         try: 
    1004             f=None 
    1005             for c in self.channels.keys(): 
    1006               &nbs