Example #1
0
 def connect(self):
     """ Attempt to connect to a given server"""
     try:
         logging.info("Attempting to start client.")
         self.__server = sockmod.socket(sockmod.AF_INET, sockmod.SOCK_STREAM)
         self.__server.connect((self.getHost(), self.getPort()))
         self.__server = SocketBuffer(self.__server)
         logging.info("Client Connected to server.")
         return True
     except sockmod.error as e:
         if e.errno == 111:
             logging.critical(
                 "Can't connect to {host}:{port}. Is the server running?".format(
                     host=self.getHost(),
                     port=self.getPort()
                 )
             )
             return False
         else:
             raise e
Example #2
0
class IRCClient(IRC.Handler.IRCHandler):
    """ The IRC Client

    Takes the base IRCHandler and uses it to produce
    a fully formed Client for the IRC protocol.
    """

    def __init__(self, host, port, userinput=sys.stdin, autoQuit=False):
        """ Initialize Client"""
        self.__nick = "NEWUSER"
        super(IRCClient, self).__init__(self.__nick, host, port)
        self.__cmdProc = CommandProcessor()
        self.__server = None
        self.__noneChannel = self.__currentChannel = ClientChannel("None")
        self.__input = userinput
        self.__gui = ClientConsole(self)
        self.__tempNames = []
        self.__tempChannels = []
        self.__autoQuit = autoQuit
        self.__allUsers = {}
        self.__allChannels = {self.__noneChannel.getName(): self.__noneChannel}

        self.__noneChannel.addUser(self.findOrCreateUser(self.__nick))

    def getNoneChannel(self):
        return self.__noneChannel

    def connect(self):
        """ Attempt to connect to a given server"""
        try:
            logging.info("Attempting to start client.")
            self.__server = sockmod.socket(sockmod.AF_INET, sockmod.SOCK_STREAM)
            self.__server.connect((self.getHost(), self.getPort()))
            self.__server = SocketBuffer(self.__server)
            logging.info("Client Connected to server.")
            return True
        except sockmod.error as e:
            if e.errno == 111:
                logging.critical(
                    "Can't connect to {host}:{port}. Is the server running?".format(
                        host=self.getHost(),
                        port=self.getPort()
                    )
                )
                return False
            else:
                raise e

    def getNick(self):
        """ Get's the client nickname"""
        return self.__nick

    def getJoined(self):
        """ Return list of joined rooms """
        return [
            c
            for c in self.__allChannels.values()
            if c.userInChannel(self.findOrCreateUser(self.__nick))
        ]

    def getChats(self):
        """ Return a dictionary of chat messages for rooms"""
        return self.__chats

    def isGUI(self):
        """ Returns if GUI enabled """
        return self.__gui.isGUI()

    def updateChat(self, msg, channels=None):
        """ Update the corresponding chat messages

        Keeps a record of the chat messages in a given channel
        so that the user can read the transcripts
        """
        if channels == None:
            self.currentChannel().addHistory(msg)
        else:
            for c in channels:
                c.addHistory(msg)

        if not self.__gui.isGUI():
            print msg
        else:
            self.__gui.updateChat()

    def guirun(self, screen):
        """ Runs the client in a GUI mode """
        (height, width) = screen.getmaxyx()
        if height < 10 or width < 50:
            logging.warning("Screen size too small")
            self.stop()
        else:
            self.__gui = ClientGUI(self, screen)
            self.__gui.update()
            self.run(shutdown=False)

    def setInput(self, newinput):
        """ Sets the input stream for the client"""
        self.__input = newinput

    def getUserInput(self):
        """ Reads from the current input stream a new line"""
        return self.__input.readline()

    def getChannels(self):
        """ Gets the known available channels"""
        return self.__allChannels.values()

    def currentChannel(self):
        """ Returns users current channel"""
        return self.__currentChannel

    def setChannel(self, chan):
        """ Sets the current channel"""
        self.__currentChannel = chan
        self.__gui.update()

    def getInputSocketList(self):
        """ Returns  the list of input sockets to listen"""
        return [self.__input, self.__server]

    def getOutputSocketList(self):
        """Returns a list of sockets ready to send messages"""
        return filter(lambda s: s.readyToSend(), [self.__server])

    def serverSocket(self):
        """ Returns the current server socket"""
        return self.__server

    def inputCmd(self, line):
        """ Attempts to execute a given user input line"""
        try:
            cmd = self.__cmdProc.processCmd(line)
            if cmd:
                cmd.execute(self)
        except CommandParseError as e:
            self.notify("Error Encountered Parsing Command: %s" % (e))

    def socketInputReady(self, socket):
        """ Determine what to do with a given socket input"""
        if socket == self.__input:
            line = self.__gui.keypress()
            if line and len(line) > 0:
                self.inputCmd(line)
        else:
            self.receiveMsg(socket)

    def socketExceptReady(self, socket):
        """ Notify client of socket exception"""
        pass  #does the client care?

    def connectionDrop(self, socket):
        """ Server Disconnect, shutdown client. """
        if socket == self.__server:
            self.notify("*** Server Disconnect ***")

        if self.__autoQuit and socket == self.__server:
            self.stop()

    def notify(self, msg):
        """ Notify the GUI that there is a new message """
        self.updateChat(msg)

    def findOrCreateUser(self, name):
        """ Find or create a user reference """
        if name in self.__allUsers:
            return self.__allUsers[name]
        else:
            newuser = ClientUser(name)
            self.__allUsers[name] = newuser
            return newuser

    def findChannel(self, name):
        """ Find a a channel with the possibility of failure """
        if name in self.__allChannels:
            return self.__allChannels[name]
        else:
            return None

    def findOrCreateChannel(self, name):
        """ Find and potentially create channel """
        if self.findChannel(name):
            return self.findChannel(name)
        else:
            newchannel = ClientChannel(name)
            self.__allChannels[name] = newchannel
            return newchannel

    def allChannelsWithName(self, name):
        """ Return a list of all channels with a user in them """
        chans = []
        user = self.findOrCreateUser(name)
        for c in self.__allChannels.values():
            if c.userInChannel(user):
                chans.append(c)
        return chans

    def receivedNick(self, socket, src, newnick):
        """ Received a nickname.

        If it's for the client, update the nickname.
        Otherwise update the nickname of the specified clients
        """
        notify = self.allChannelsWithName(src)
        user = self.findOrCreateUser(src)
        user.updateName(newnick)

        self.__allUsers[newnick] = user
        del self.__allUsers[src]

        if self.__nick == src:
            self.__nick = newnick
            self._ircmsg.updateSrc(newnick)
            self.updateChat(
                "*** You ({oldnick}) are now {newnick} ****".format(
                    oldnick=src,
                    newnick=newnick
                ),
                notify
            )
        else:
            self.updateChat(
                "*** {oldnick} is now {newnick} ****".format(
                    oldnick=src,
                    newnick=newnick
                ),
                notify
            )
        self.__gui.update()

    def receivedQuit(self, socket, src, msg):
        """ Received a quit command

        If it's for the client, shutdown.
        If it's for another client, remove them from chats.
        """
        user = self.findOrCreateUser(src)
        if src == "SERVER":
            notify = self.__allChannels.values()
        else:
            notify = self.allChannelsWithName(src)
            for c in notify:
                c.removeUser(user)

        if src in self.__allUsers:
            del self.__allUsers[src]

        self.updateChat(
            "*** {src} quit ({msg})".format(
                src=src,
                msg=msg
            ),
            notify
        )
        self.__gui.updateUsers()

        if src == self.__nick:
            self.stop()

    def receivedSQuit(self, socket, msg):
        """ Unused server command """
        pass

    def receivedJoin(self, socket, src, channels):
        """ Received join

        If the join is for the user, add them to the channel.
        If it's for another user, store them in the channel.
        """
        user = self.findOrCreateUser(src)
        notify_chan = []
        for c in channels:
            chan = self.findOrCreateChannel(c)
            chan.addUser(user)
            notify_chan.append(chan)

        if src == self.__nick:
            self.updateChat(
                "*** You joined the channel(s) {channels}".format(
                    channels=",".join(channels)
                ), notify_chan + [self.__noneChannel]
            )
        else:
            self.updateChat(
                "*** {src} joined the channel".format(src=src),
                notify_chan
            )
        self.__gui.update()

    def receivedLeave(self, socket, src, channels, msg):
        """ Received Leave

        If it's for the user, remove from specified channels.
        If it's for someone else, remove them from channels.
        """
        user = self.findOrCreateUser(src)
        notify_chan = []
        for c in channels:
            chan = self.findOrCreateChannel(c)
            if chan.userInChannel(user):
                chan.removeUser(user)
                notify_chan.append(chan)

        if src == self.__nick and self.__currentChannel in notify_chan:
            self.__currentChannel = self.__noneChannel

        self.updateChat(
            "*** {src} left the channel(s) {chans} ({msg})".format(
                src=src,
                msg=msg,
                chans=", ".join([c.getName() for c in notify_chan])
            ),
            [self.__noneChannel] + notify_chan
        )
        self.__gui.update()

    @clientIgnore
    def receivedChannels(self, socket):
        """ Client does not receive channels messages"""
        pass

    @clientIgnore
    def receivedUsers(self, socket, channels, client_req):
        """ Client does not receive users requests"""
        pass

    def filterChannels(self, c):
        return re.match("^#", c) != None

    def receivedMsg(self, socket, src, targets, msg):
        """ Update the chats with the new message depending on target"""

        channels = filter(lambda t: self.filterChannels(t), targets)
        channels = map(lambda c: self.findOrCreateChannel(c), channels)

        if self.__nick in targets:
            self.notify("*** {src}: {msg}".format(src=src, msg=msg))
        elif len(channels) > 0:
            self.updateChat("{src}: {msg}".format(src=src, msg=msg), channels)

    def receivedPing(self, socket, msg):
        """ Reply to ping with pong """
        self.sendMsg(socket, self._ircmsg.cmdPong(msg))

    @clientIgnore
    def receivedPong(self, socket, msg):
        """ Client does not recieve pongs"""
        pass

    def receivedNames(self, socket, channel, names, client):
        """ Receive the names list

        If the user is in GUI mode, update the users window.
        Otherwise print out the user information
        """
        if self.__gui.isGUI() and not client:
            self.__tempNames.extend(names)
            if len(names) == 0:
                c = self.findOrCreateChannel(channel)
                newusers = []
                for n in self.__tempNames:
                    newusers.append(self.findOrCreateUser(n))
                c.receivedNames(newusers)

                self.__tempNames = []
            self.__gui.update()
        elif len(names) > 0:
            self.notify(
                "{chan}: {users}".format(
                    chan=channel,
                    users=" ".join(names)
                )
            )

    def receivedChannelsReply(self, socket, channels):
        """ Receive the channels list

        If the user is in gui mode update the channels window.
        Otherwise print out the channels list.
        """
        if self.__gui.isGUI():
            self.__tempChannels.extend(channels)

            if channels == []:
                new_set = set(self.__tempChannels)
                old_set = set(self.__allChannels.keys())
                remove = old_set - new_set
                add = new_set - old_set
                for d in remove:
                    del self.__allChannels[d]
                for a in add:
                    self.findOrCreateChannel(a)

                self.__gui.update()
        elif len(channels) > 0:
            self.notify("CHANNELS: {chans}".format(chans=" ".join(channels)))

    def receivedError(self, socket, error_name, error_msg):
        """ Notify user of error"""
        self.notify(
            "ERROR: {error_t}: {error_m}".format(
                error_t=error_name,
                error_m=error_msg
            )
        )

    def receivedInvalid(self, socket, msg):
        """ Notify user of invalid server message"""
        self.notify("BAD SERVER MSG: {msg}".format(msg=msg))

    def receivedSignal(self, sig, frame):
        """ Handle signal gradefully """
        if sig == signal.SIGINT:
            msg = "Client interrupted with Ctrl-C."
            logging.info(msg)
            self.notify(msg)
            self.stop()

    def sentInvalid(self, socket, msg):
        """ Notify user that invalid message was sent"""
        self.notify("CLIENT ERROR: {msg}".format(msg=msg))

    def shutdown(self):
        """ Shutdown client"""
        self.notify("*** Shutting Down Client ***")
        self.__server.close()