<commit_before>package main

import (
	"crypto/md5"
	"fmt"
	"net"
	"regexp"
	"strings"
	"sync"
	"syscall"
	"time"

	"golang.org/x/crypto/ssh"
)

const (
	maxNameLength        = 32
	historyLength        = 20
	systemMessageFormat  = "\033[1;90m"
	privateMessageFormat = "\033[1m"
	beep                 = "\007"
)

var (
	reStripText = regexp.MustCompile("[^0-9A-Za-z_.-]")
)

// Clients is a map of clients
type Clients map[string]*Client

// Server holds all the fields used by a server
type Server struct {
	sshConfig *ssh.ServerConfig
	done      chan struct{}
	clients   Clients
	count     int
	history   *History
	motd      string
	whitelist map[string]struct{}   // fingerprint lookup
	admins    map[string]struct{}   // fingerprint lookup
	bannedPK  map[string]*time.Time // fingerprint lookup
	started   time.Time
	sync.Mutex
}

// NewServer constructs a new server
func NewServer(privateKey []byte) (*Server, error) {
	signer, err := ssh.ParsePrivateKey(privateKey)
	if err != nil {
		return nil, err
	}

	server := Server{
		done:      make(chan struct{}),
		clients:   Clients{},
		count:     0,
		history:   NewHistory(historyLength),
		motd:      "",
		whitelist: map[string]struct{}{},
		admins:    map[string]struct{}{},
		bannedPK:  map[string]*time.Time{},
		started:   time.Now(),
	}

	config := ssh.ServerConfig{
		NoClientAuth: false,
		// Auth-related things should be constant-time to avoid timing attacks.
		PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
			fingerprint := Fingerprint(key)
			if server.IsBanned(fingerprint) {
				return nil, fmt.Errorf("Banned.")
			}
			if !server.IsWhitelisted(fingerprint) {
				return nil, fmt.Errorf("Not Whitelisted.")
			}
			perm := &ssh.Permissions{Extensions: map[string]string{"fingerprint": fingerprint}}
			return perm, nil
		},
	}
	config.AddHostKey(signer)

	server.sshConfig = &config

	return &server, nil
}

// Len returns the number of clients
func (s *Server) Len() int {
	return len(s.clients)
}

// SysMsg broadcasts the given message to everyone
func (s *Server) SysMsg(msg string, args ...interface{}) {
	s.Broadcast(ContinuousFormat(systemMessageFormat, " * "+fmt.Sprintf(msg, args...)), nil)
}

// Broadcast broadcasts the given message to everyone except for the given client
func (s *Server) Broadcast(msg string, except *Client) {
	logger.Debugf("Broadcast to %d: %s", s.Len(), msg)
	s.history.Add(msg)

	for _, client := range s.clients {
		if except != nil && client == except {
			continue
		}

		if strings.Contains(msg, client.Name) {
			// Turn message red if client's name is mentioned, and send BEL if they have enabled beeping
			tmpMsg := strings.Split(msg, Reset)
			if client.beepMe {
				tmpMsg[0] += beep
			}
			client.Send(strings.Join(tmpMsg, Reset+Bold+"\033[31m") + Reset)
		} else {
			client.Send(msg)
		}
	}
}

// Privmsg sends a message to a particular nick, if it exists
func (s *Server) Privmsg(nick, message string, sender *Client) error {
	// Get the recipient
	target, ok := s.clients[strings.ToLower(nick)]
	if !ok {
		return fmt.Errorf("no client with that nick")
	}
	// Send the message
	target.Msg <- fmt.Sprintf(beep+"[PM from %v] %s%v%s", sender.ColoredName(), privateMessageFormat, message, Reset)
	logger.Debugf("PM from %v to %v: %v", sender.Name, nick, message)<commit_msg>Improve highlighting of mentions. (Closes #60)<commit_after>package main

import (
	"crypto/md5"
	"fmt"
	"net"
	"regexp"
	"strings"
	"sync"
	"syscall"
	"time"

	"golang.org/x/crypto/ssh"
)

const (
	maxNameLength        = 32
	historyLength        = 20
	systemMessageFormat  = "\033[1;90m"
	privateMessageFormat = "\033[1m"
	highlightFormat      = Bold + "\033[48;5;11m\033[38;5;16m"
	beep                 = "\007"
)

var (
	reStripText = regexp.MustCompile("[^0-9A-Za-z_.-]")
)

// Clients is a map of clients
type Clients map[string]*Client

// Server holds all the fields used by a server
type Server struct {
	sshConfig *ssh.ServerConfig
	done      chan struct{}
	clients   Clients
	count     int
	history   *History
	motd      string
	whitelist map[string]struct{}   // fingerprint lookup
	admins    map[string]struct{}   // fingerprint lookup
	bannedPK  map[string]*time.Time // fingerprint lookup
	started   time.Time
	sync.Mutex
}

// NewServer constructs a new server
func NewServer(privateKey []byte) (*Server, error) {
	signer, err := ssh.ParsePrivateKey(privateKey)
	if err != nil {
		return nil, err
	}

	server := Server{
		done:      make(chan struct{}),
		clients:   Clients{},
		count:     0,
		history:   NewHistory(historyLength),
		motd:      "",
		whitelist: map[string]struct{}{},
		admins:    map[string]struct{}{},
		bannedPK:  map[string]*time.Time{},
		started:   time.Now(),
	}

	config := ssh.ServerConfig{
		NoClientAuth: false,
		// Auth-related things should be constant-time to avoid timing attacks.
		PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
			fingerprint := Fingerprint(key)
			if server.IsBanned(fingerprint) {
				return nil, fmt.Errorf("Banned.")
			}
			if !server.IsWhitelisted(fingerprint) {
				return nil, fmt.Errorf("Not Whitelisted.")
			}
			perm := &ssh.Permissions{Extensions: map[string]string{"fingerprint": fingerprint}}
			return perm, nil
		},
	}
	config.AddHostKey(signer)

	server.sshConfig = &config

	return &server, nil
}

// Len returns the number of clients
func (s *Server) Len() int {
	return len(s.clients)
}

// SysMsg broadcasts the given message to everyone
func (s *Server) SysMsg(msg string, args ...interface{}) {
	s.Broadcast(ContinuousFormat(systemMessageFormat, " * "+fmt.Sprintf(msg, args...)), nil)
}

// Broadcast broadcasts the given message to everyone except for the given client
func (s *Server) Broadcast(msg string, except *Client) {
	logger.Debugf("Broadcast to %d: %s", s.Len(), msg)
	s.history.Add(msg)

	for _, client := range s.clients {
		if except != nil && client == except {
			continue
		}

		if strings.Contains(msg, client.Name) {
			// Turn message red if client's name is mentioned, and send BEL if they have enabled beeping
			personalMsg := strings.Replace(msg, client.Name, highlightFormat+client.Name+Reset, -1)
			if client.beepMe {
				personalMsg += beep
			}
			client.Send(personalMsg)
		} else {
			client.Send(msg)
		}
	}
}

// Privmsg sends a message to a particular nick, if it exists
func (s *Server) Privmsg(nick, message string, sender *Client) error {
	// Get the recipient
	target, ok := s.clients[strings.ToLower(nick)]
	if !ok {
		return fmt.Errorf("no client with that nick")
	}
	// Send the message
	target.Msg <- fmt.Sprintf(beep+"[PM from %v] %s%v%s", sender.ColoredName(), privateMessageFormat, message, Reset)
	logger.Debugf("PM from %v to %v: %v", sender.Name, nick, message)
{'fraction_non_alphanumeric': 0.13370637608599617, 'fraction_numerical': 0.00986599911647769, 'mean_word_length': 3.5891891891891894, 'pattern_counts': {'":': 2, '<': 5, '<?xml version=': 0, '>': 3, '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': '1935365', 'n_tokens_mistral': 2503, 'n_tokens_neox': 2269, 'n_words': 633}