<commit_before>// Copyright (c) 2014 <NAME>. All rights reserved.
//
// Use of this source code is governed by the BSD license that can be found in
// the LICENSE file.

// Package server contains the server functionality.
package server

import (
	"crypto/tls"
	"fmt"
	"log"
	"net"
	"os"
	"strconv"
	"sync"

	"code.google.com/p/go-uuid/uuid"

	"github.com/nightexcessive/excessiveircd/config"
)

// ListenPort represents a port to be listened on.
// IP specifies the local IP address to listen on.
// Port specifies the port number to listen on.
// TLS specifies the TLS configuration to use. If nil, the port listens for
// cleartext traffic.
type ListenPort struct {
	IP   net.IP
	Port uint16

	TLS *tls.Config
}

// Server represents a local server.
type Server struct {
	ID      uuid.UUID
	Name    string
	Network string

	Logger *log.Logger

	Events chan interface{}

	Clients map[string]*Client

	Listeners []net.Listener
}

// FriendlyName is a convenience function to return the server's display name.
func (s *Server) FriendlyName() string {
	if len(s.Name) > 0 {
		return s.Name
	}

	return s.ID.String()
}

func (s *Server) eventLoop() {
	for event := range s.Events {
		s.Logger.Printf("Event: %T", event)
		switch ev := event.(type) {
		case *SRegisterClient:
			if _, ok := s.Clients[ev.Client.Info.Name]; ok {
				ev.Reply <- false
				continue
			}
			s.Clients[ev.Client.Info.Name] = ev.Client
			ev.Reply <- true
		case *SChangeNick:
			if _, ok := s.Clients[ev.NewNick]; ok {
				ev.Reply <- false
				continue
			}
			delete(s.Clients, ev.Client.Info.Name)
			s.Clients[ev.NewNick] = ev.Client
			ev.Reply <- true
		case *SDeregisterClient:
			delete(s.Clients, ev.Client.Info.Name)
			ev.Reply <- struct{}{}
		default:
			s.Logger.Printf("Unexpected event of type %T: %#v", ev, ev)
		}
	}
}

// Start starts the server's event loop and all of its listeners.
func (s *Server) Start() error {
	s.Events = make(chan interface{})
	defer close(s.Events)

	s.Clients = make(map[string]*Client)

	go s.eventLoop()

	var listeners []*ListenPort
	if err := config.Get("ports", &listeners); err == config.ErrDoesNotExist {
		listeners = []*ListenPort{
			{
				IP:   net.IPv4(0, 0, 0, 0),
				Port: 6667,
			},
		}
	} else if err != nil {
		return err
	}

	if err := config.Get("id", &s.ID); err == config.ErrDoesNotExist {
		s.ID = uuid.NewRandom()
		if err := config.Set("id", s.ID); err != nil {
			return err
		}
	} else if err != nil {
		return err
	}

	s.Logger = log.New(os.Stderr, fmt.Sprintf("Server(%s) ", s.ID), 0)

	s.startListeners(listeners)

	return nil
}

func (s *Server) close(reason string) error {
	s.Logger.Printf("Shutting down: %s", reason)
	for _, listener := range s.Listeners {
		if err := listener.Close(); err != nil {
			s.Logger.Printf("Error closing listener on %s: %s", listener.Addr(), err)
		}
	}

	// This should be safe because the event should never be modified.
	closeEvent := &CClose{"Server shutting down"}
	if len(reason) > 0 {
		closeEvent.Reason = fmt.Sprintf("Server shutting down: %s", reason)
	}
	wg := new(sync.WaitGroup)
	wg.Add(len(s.Clients))
	for _, client := range s.Clients {
		go func(client *Client) {
			defer wg.Done()
			client.Events <- closeEvent
		}(client)
	}
	wg.Wait()

	close(s.Events)

	return nil
}

func (s *Server) listen(listenSpec *ListenPort, wg *sync.WaitGroup) {
	defer wg.Done()

	listenAddr := net.JoinHostPort(listenSpec.IP.String(), strconv.FormatInt(int64(listenSpec.Port), 10))
	listener, err := net.Listen("tcp", listenAddr)
	if err != nil {
		s.Logger.Printf("Failed to listen on %s: %s", listenAddr, err)
		return
	}
	defer listener.Close()
	s.Logger.Printf("Listening on %s...", listenAddr)

	for {
		c, err := listener.Accept()
		if err != nil {
			s.Logger.Printf("Error accepting connection on %s: %s", listenAddr, err)
			continue
		}

		s.Logger.Printf("New connection to %s from %s", listenAddr, c.RemoteAddr())<commit_msg>Use TLS listener if requested<commit_after>// Copyright (c) 2014 <NAME>. All rights reserved.
//
// Use of this source code is governed by the BSD license that can be found in
// the LICENSE file.

// Package server contains the server functionality.
package server

import (
	"crypto/tls"
	"fmt"
	"log"
	"net"
	"os"
	"strconv"
	"sync"

	"code.google.com/p/go-uuid/uuid"

	"github.com/nightexcessive/excessiveircd/config"
)

// ListenPort represents a port to be listened on.
// IP specifies the local IP address to listen on.
// Port specifies the port number to listen on.
// TLS specifies the TLS configuration to use. If nil, the port listens for
// cleartext traffic.
type ListenPort struct {
	IP   net.IP
	Port uint16

	TLS *tls.Config
}

// Server represents a local server.
type Server struct {
	ID      uuid.UUID
	Name    string
	Network string

	Logger *log.Logger

	Events chan interface{}

	Clients map[string]*Client

	Listeners []net.Listener
}

// FriendlyName is a convenience function to return the server's display name.
func (s *Server) FriendlyName() string {
	if len(s.Name) > 0 {
		return s.Name
	}

	return s.ID.String()
}

func (s *Server) eventLoop() {
	for event := range s.Events {
		s.Logger.Printf("Event: %T", event)
		switch ev := event.(type) {
		case *SRegisterClient:
			if _, ok := s.Clients[ev.Client.Info.Name]; ok {
				ev.Reply <- false
				continue
			}
			s.Clients[ev.Client.Info.Name] = ev.Client
			ev.Reply <- true
		case *SChangeNick:
			if _, ok := s.Clients[ev.NewNick]; ok {
				ev.Reply <- false
				continue
			}
			delete(s.Clients, ev.Client.Info.Name)
			s.Clients[ev.NewNick] = ev.Client
			ev.Reply <- true
		case *SDeregisterClient:
			delete(s.Clients, ev.Client.Info.Name)
			ev.Reply <- struct{}{}
		default:
			s.Logger.Printf("Unexpected event of type %T: %#v", ev, ev)
		}
	}
}

// Start starts the server's event loop and all of its listeners.
func (s *Server) Start() error {
	s.Events = make(chan interface{})
	defer close(s.Events)

	s.Clients = make(map[string]*Client)

	go s.eventLoop()

	var listeners []*ListenPort
	if err := config.Get("ports", &listeners); err == config.ErrDoesNotExist {
		listeners = []*ListenPort{
			{
				IP:   net.IPv4(0, 0, 0, 0),
				Port: 6667,
			},
		}
	} else if err != nil {
		return err
	}

	if err := config.Get("id", &s.ID); err == config.ErrDoesNotExist {
		s.ID = uuid.NewRandom()
		if err := config.Set("id", s.ID); err != nil {
			return err
		}
	} else if err != nil {
		return err
	}

	s.Logger = log.New(os.Stderr, fmt.Sprintf("Server(%s) ", s.ID), 0)

	s.startListeners(listeners)

	return nil
}

func (s *Server) close(reason string) error {
	s.Logger.Printf("Shutting down: %s", reason)
	for _, listener := range s.Listeners {
		if err := listener.Close(); err != nil {
			s.Logger.Printf("Error closing listener on %s: %s", listener.Addr(), err)
		}
	}

	// This should be safe because the event should never be modified.
	closeEvent := &CClose{"Server shutting down"}
	if len(reason) > 0 {
		closeEvent.Reason = fmt.Sprintf("Server shutting down: %s", reason)
	}
	wg := new(sync.WaitGroup)
	wg.Add(len(s.Clients))
	for _, client := range s.Clients {
		go func(client *Client) {
			defer wg.Done()
			client.Events <- closeEvent
		}(client)
	}
	wg.Wait()

	close(s.Events)

	return nil
}

func (s *Server) listen(listenSpec *ListenPort, wg *sync.WaitGroup) {
	defer wg.Done()

	listenAddr := net.JoinHostPort(listenSpec.IP.String(), strconv.FormatInt(int64(listenSpec.Port), 10))
	var listener net.Listener

	if listenSpec.TLS != nil {
		tlsListener, err := tls.Listen("tcp", listenAddr, listenSpec.TLS)
		if err != nil {
			s.Logger.Printf("Failed to listen for SSL connections on %s: %s", listenAddr, err)
			return
		}
		listener = tlsListener
		s.Logger.Printf("Listening for SSL connections on %s...", listenAddr)
	} else {
		clearListener, err := net.Listen("tcp", listenAddr)
		if err != nil {
			s.Logger.Printf("Failed to listen on %s: %s", listenAddr, err)
			return
		}
		listener = clearListener
		s.Logger.Printf("Listening on %s...", listenAddr)
	}

	defer listener.Close()

	for {
		c, err := listener.Accept()
		if err != nil {
			s.Logger.Printf("Error accepting connection on %s: %s", listenAddr, err)
			continue
		}

		s.Logger.Printf("New connection to %s from %s", listenAddr, c.RemoteAddr())
{'fraction_non_alphanumeric': 0.143272374954584, 'fraction_numerical': 0.005328811917161197, 'mean_word_length': 3.312271540469974, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, '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': '1019721', 'n_tokens_mistral': 3155, 'n_tokens_neox': 2871, 'n_words': 851}