<commit_before>"""
irc/server.py

Copyright by <NAME>, 2009

This server has basic support for:

* Connecting
* Channels
* Nicknames
* Public/private messages

It is MISSING support for notably:

* Server linking
* Modes (user and channel)
* Proper error reporting
* Basically everything else

It is mostly useful as a testing tool or perhaps for building something like a
private proxy on. Do NOT use it in any kind of production code or anything that
will ever be connected to by the public.

"""

#
# Very simple hacky ugly IRC server.
#
# Todo:
#   - Encode format for each message and reply with events.codes['needmoreparams']
#   - starting server when already started doesn't work properly. PID file is not changed, no error messsage is displayed.
#   - Delete channel if last user leaves.
#   - [ERROR] <socket.error instance at 0x7f9f203dfb90> (better error msg required)
#   - Empty channels are left behind
#   - No Op assigned when new channel is created.
#   - User can /join multiple times (doesn't add more to channel, does say 'joined')
#   - Error
#     [<socket._socketobject object at 0x26151a0>] [] []
#     ----------------------------------------
#     Exception happened during processing of request from ('127.0.0.1', 47830)
#     Traceback (most recent call last):
#       File "/usr/lib/python2.6/SocketServer.py", line 560, in process_request_thread
#         self.finish_request(request, client_address)
#       File "/usr/lib/python2.6/SocketServer.py", line 322, in finish_request
#         self.RequestHandlerClass(request, client_address, self)
#       File "./hircd.py", line 102, in __init__
#
#       File "/usr/lib/python2.6/SocketServer.py", line 617, in __init__
#         self.handle()
#       File "./hircd.py", line 120, in handle
#         if len(ready_to_read) == 1 and ready_to_read[0] == self.request:
#     error: [Errno 104] Connection reset by peer
#     ----------------------------------------
#   - PING timeouts
#   - Allow all numerical commands.
#   - Users can send commands to channels they are not in (PART)
# Not Todo (Won't be supported)
#   - Server linking.

#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following
# conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#

from __future__ import print_function, absolute_import

import argparse
import logging
import socket
import select
import re

from . import client
from . import _py2_compat
from . import logging as log_util
from . import events
from . import buffer

SRV_WELCOME = "Welcome to %s v%s, the ugliest IRC server in the world." % (
    __name__, client.VERSION)

log = logging.getLogger(__name__)

class IRCError(Exception):
    """
    Exception thrown by IRC command handlers to notify client of a server/client error.
    """
    def __init__(self, code, value):
        self.code = code
        self.value = value

    def __str__(self):
        return repr(self.value)

class IRCChannel(object):
    """
    Object representing an IRC channel.
    """
    def __init__(self, name, topic='No topic'):
        self.name = name
        self.topic_by = 'Unknown'
        self.topic = topic
        self.clients = set()

class IRCClient(_py2_compat.socketserver.BaseRequestHandler):
    """
    IRC client connect and command handling. Client connection is handled by
    the `handle` method which sets up a two-way communication with the client.
    It then handles commands sent by the client by dispatching them to the
    handle_ methods.
    """
    class Disconnect(BaseException): pass

    def __init__(self, request, client_address, server):
        self.user = None
        self.host = client_address  # Client's hostname / ip.
        self.realname = None        # Client's real name
        self.nick = None            # Client's currently registered nickname
        self.send_queue = []        # Messages to send to client (strings)
        self.channels = {}          # Channels the client is in

        _py2_compat.socketserver.BaseRequestHandler.__init__(self, request,
            client_address, server)

    def handle(self):
        log.info('Client connected: %s', self.client_ident())

        try:
            while self._handle_one():
                pass
        except self.Disconnect:
            self.request.close()

    def _handle_one(self):
        """
        Handle one read/write cycle.
        """
        ready_to_read, ready_to_write, in_error = select.select(
            [self.request], [], [], 0.1)

        # Write any commands to the client
        while self.send_queue:
            msg = self.send_queue.pop(0)
            log.debug('to %s: %s' % (self.client_ident(), msg))
            self.request.send(msg + '\n')

        # See if the client has any commands for us.
        if len(ready_to_read) == 1 and ready_to_read[0] == self.request:
            self._handle_incoming()

    def _handle_incoming(self):
        buf = buffer.LineBuffer()
        data = self.request.recv(1024)

        if not data:
            raise self.Disconnect()
        buf.feed(data)
        for line in buf:
            self._handle_line(line)

    def _handle_line(self, line):
        response = ''
        try:
            log.debug('from %s: %s' % (self.client_ident(),
                line))
            command, sep, params = line.partition(' ')
            handler = getattr(self,
                'handle_%s' % (command.lower()), None)
            if not handler:
                log.info('No handler for command: %s. '
                    'Full line: %s' % (command, line))
                raise IRCError(events.codes['unknowncommand'],
                    '%s :Unknown command' % (command))
            response = handler(params)
        except AttributeError as e:
            raise e
            log.error('%s' % (e))
        except IRCError as e:
            response = ':%s %s %s' % (self.server.servername,
                e.code, e.value)
            log.error('%s' % (response))
        except Exception as e:
            response = ':%s ERROR %s' % (self.server.servername,
                repr(e))
            log.error('%s' % (response))
            raise

        if response:
            log.debug('to %s: %s' % (self.client_ident(),
                response))
            self.request.send(response + '\r\n')

    def handle_nick(self, params):
        """
        Handle the initial setting of the user's nickname and nick changes.
        """
        nick = params

        # Valid nickname?
        if re.search('[^a-zA-Z0-9\-\[\]\'`^{}_]', nick):
            raise IRCError(events.codes['erroneusnickname'], ':%s' % (nick))

        if not self.nick:
            # New connection
            if nick in self.server.clients:
                # Someone else is using the nick
                raise IRCError(events.codes['nicknameinuse'], 'NICK :%s' % (nick))
            # Nick is available, register, send welcome and MOTD.
            self.nick = nick
            self.server.clients[nick] = self
            response = ':%s %s %s :%s' % (self.server.servername,
                events.codes['welcome'], self.nick, SRV_WELCOME)
            self.send_queue.append(response)
            response = ':%s 376 %s :End of MOTD command.' % (self.server.servername, self.nick)
            self.send_queue.append(response)
            return

        if self.server.clients.get(nick, None) == self:
            # Already registered to user
            return
        elif nick in self.server.clients:
            # Someone else is using the nick
            raise IRCError(events.codes['nicknameinuse'], 'NICK :%s' % (nick))
        # Nick is available. Change the nick.
        message = ':%s NICK :%s' % (self.client_ident(), nick)

        self.server.clients.pop(self.nick)
        self.nick = nick
        self.server.clients[self.nick] = self

        # Send a notification of the nick change to all the clients in
        # the channels the client is in.
        for channel in self.channels.values():
            for client in channel.clients:
                if client != self: # do not send to client itself.
                    client.send_queue.append(message)

        # Send a notification of the nick change to the client itself
        return message

    def handle_user(self, params):
        """
        Handle the USER command which identifies the user to the server.
        """
        if params.count(' ') < 3:
            raise IRCError(events.codes['needmoreparams'], 'USER :Not enough parameters')

        user, mode, unused, realname = params.split(' ', 3)
        self.user = user
        self.mode = mode
        self.realname = realname
        return ''
<commit_msg>Refactor to only check 'nick in use' once<commit_after>"""
irc/server.py

Copyright by <NAME>, 2009

This server has basic support for:

* Connecting
* Channels
* Nicknames
* Public/private messages

It is MISSING support for notably:

* Server linking
* Modes (user and channel)
* Proper error reporting
* Basically everything else

It is mostly useful as a testing tool or perhaps for building something like a
private proxy on. Do NOT use it in any kind of production code or anything that
will ever be connected to by the public.

"""

#
# Very simple hacky ugly IRC server.
#
# Todo:
#   - Encode format for each message and reply with events.codes['needmoreparams']
#   - starting server when already started doesn't work properly. PID file is not changed, no error messsage is displayed.
#   - Delete channel if last user leaves.
#   - [ERROR] <socket.error instance at 0x7f9f203dfb90> (better error msg required)
#   - Empty channels are left behind
#   - No Op assigned when new channel is created.
#   - User can /join multiple times (doesn't add more to channel, does say 'joined')
#   - Error
#     [<socket._socketobject object at 0x26151a0>] [] []
#     ----------------------------------------
#     Exception happened during processing of request from ('127.0.0.1', 47830)
#     Traceback (most recent call last):
#       File "/usr/lib/python2.6/SocketServer.py", line 560, in process_request_thread
#         self.finish_request(request, client_address)
#       File "/usr/lib/python2.6/SocketServer.py", line 322, in finish_request
#         self.RequestHandlerClass(request, client_address, self)
#       File "./hircd.py", line 102, in __init__
#
#       File "/usr/lib/python2.6/SocketServer.py", line 617, in __init__
#         self.handle()
#       File "./hircd.py", line 120, in handle
#         if len(ready_to_read) == 1 and ready_to_read[0] == self.request:
#     error: [Errno 104] Connection reset by peer
#     ----------------------------------------
#   - PING timeouts
#   - Allow all numerical commands.
#   - Users can send commands to channels they are not in (PART)
# Not Todo (Won't be supported)
#   - Server linking.

#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following
# conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#

from __future__ import print_function, absolute_import

import argparse
import logging
import socket
import select
import re

from . import client
from . import _py2_compat
from . import logging as log_util
from . import events
from . import buffer

SRV_WELCOME = "Welcome to %s v%s, the ugliest IRC server in the world." % (
    __name__, client.VERSION)

log = logging.getLogger(__name__)

class IRCError(Exception):
    """
    Exception thrown by IRC command handlers to notify client of a server/client error.
    """
    def __init__(self, code, value):
        self.code = code
        self.value = value

    def __str__(self):
        return repr(self.value)

class IRCChannel(object):
    """
    Object representing an IRC channel.
    """
    def __init__(self, name, topic='No topic'):
        self.name = name
        self.topic_by = 'Unknown'
        self.topic = topic
        self.clients = set()

class IRCClient(_py2_compat.socketserver.BaseRequestHandler):
    """
    IRC client connect and command handling. Client connection is handled by
    the `handle` method which sets up a two-way communication with the client.
    It then handles commands sent by the client by dispatching them to the
    handle_ methods.
    """
    class Disconnect(BaseException): pass

    def __init__(self, request, client_address, server):
        self.user = None
        self.host = client_address  # Client's hostname / ip.
        self.realname = None        # Client's real name
        self.nick = None            # Client's currently registered nickname
        self.send_queue = []        # Messages to send to client (strings)
        self.channels = {}          # Channels the client is in

        _py2_compat.socketserver.BaseRequestHandler.__init__(self, request,
            client_address, server)

    def handle(self):
        log.info('Client connected: %s', self.client_ident())

        try:
            while self._handle_one():
                pass
        except self.Disconnect:
            self.request.close()

    def _handle_one(self):
        """
        Handle one read/write cycle.
        """
        ready_to_read, ready_to_write, in_error = select.select(
            [self.request], [], [], 0.1)

        # Write any commands to the client
        while self.send_queue:
            msg = self.send_queue.pop(0)
            log.debug('to %s: %s' % (self.client_ident(), msg))
            self.request.send(msg + '\n')

        # See if the client has any commands for us.
        if len(ready_to_read) == 1 and ready_to_read[0] == self.request:
            self._handle_incoming()

    def _handle_incoming(self):
        buf = buffer.LineBuffer()
        data = self.request.recv(1024)

        if not data:
            raise self.Disconnect()
        buf.feed(data)
        for line in buf:
            self._handle_line(line)

    def _handle_line(self, line):
        response = ''
        try:
            log.debug('from %s: %s' % (self.client_ident(),
                line))
            command, sep, params = line.partition(' ')
            handler = getattr(self,
                'handle_%s' % (command.lower()), None)
            if not handler:
                log.info('No handler for command: %s. '
                    'Full line: %s' % (command, line))
                raise IRCError(events.codes['unknowncommand'],
                    '%s :Unknown command' % (command))
            response = handler(params)
        except AttributeError as e:
            raise e
            log.error('%s' % (e))
        except IRCError as e:
            response = ':%s %s %s' % (self.server.servername,
                e.code, e.value)
            log.error('%s' % (response))
        except Exception as e:
            response = ':%s ERROR %s' % (self.server.servername,
                repr(e))
            log.error('%s' % (response))
            raise

        if response:
            log.debug('to %s: %s' % (self.client_ident(),
                response))
            self.request.send(response + '\r\n')

    def handle_nick(self, params):
        """
        Handle the initial setting of the user's nickname and nick changes.
        """
        nick = params

        # Valid nickname?
        if re.search('[^a-zA-Z0-9\-\[\]\'`^{}_]', nick):
            raise IRCError(events.codes['erroneusnickname'], ':%s' % (nick))

        if self.server.clients.get(nick, None) == self:
            # Already registered to user
            return

        if nick in self.server.clients:
            # Someone else is using the nick
            raise IRCError(events.codes['nicknameinuse'], 'NICK :%s' % (nick))

        if not self.nick:
            # New connection and nick is available; register and send welcome
            # and MOTD.
            self.nick = nick
            self.server.clients[nick] = self
            response = ':%s %s %s :%s' % (self.server.servername,
                events.codes['welcome'], self.nick, SRV_WELCOME)
            self.send_queue.append(response)
            response = ':%s 376 %s :End of MOTD command.' % (self.server.servername, self.nick)
            self.send_queue.append(response)
            return

        # Nick is available. Change the nick.
        message = ':%s NICK :%s' % (self.client_ident(), nick)

        self.server.clients.pop(self.nick)
        self.nick = nick
        self.server.clients[self.nick] = self

        # Send a notification of the nick change to all the clients in
        # the channels the client is in.
        for channel in self.channels.values():
            for client in channel.clients:
                if client != self: # do not send to client itself.
                    client.send_queue.append(message)

        # Send a notification of the nick change to the client itself
        return message

    def handle_user(self, params):
        """
        Handle the USER command which identifies the user to the server.
        """
        if params.count(' ') < 3:
            raise IRCError(events.codes['needmoreparams'], 'USER :Not enough parameters')

        user, mode, unused, realname = params.split(' ', 3)
        self.user = user
        self.mode = mode
        self.realname = realname
        return ''

{'fraction_non_alphanumeric': 0.10581401433879324, 'fraction_numerical': 0.007849704327803653, 'mean_word_length': 2.2577565632458234, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1144681', 'n_tokens_mistral': 5456, 'n_tokens_neox': 5182, 'n_words': 1882}