Added all of the existing code
This commit is contained in:
509
services.go
Normal file
509
services.go
Normal file
@@ -0,0 +1,509 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ServicesManager handles all network services
|
||||
type ServicesManager struct {
|
||||
server *Server
|
||||
config *ServicesConfig
|
||||
nickServ *NickServ
|
||||
chanServ *ChanServ
|
||||
operServ *OperServ
|
||||
memoServ *MemoServ
|
||||
enabled bool
|
||||
}
|
||||
|
||||
// NickServ handles nickname registration and authentication
|
||||
type NickServ struct {
|
||||
manager *ServicesManager
|
||||
nick string
|
||||
accounts map[string]*NickAccount
|
||||
}
|
||||
|
||||
// ChanServ handles channel registration and management
|
||||
type ChanServ struct {
|
||||
manager *ServicesManager
|
||||
nick string
|
||||
channels map[string]*RegisteredChannel
|
||||
}
|
||||
|
||||
// OperServ handles network operator services
|
||||
type OperServ struct {
|
||||
manager *ServicesManager
|
||||
nick string
|
||||
}
|
||||
|
||||
// MemoServ handles user-to-user messages
|
||||
type MemoServ struct {
|
||||
manager *ServicesManager
|
||||
nick string
|
||||
memos map[string][]*Memo
|
||||
}
|
||||
|
||||
// NickAccount represents a registered nickname
|
||||
type NickAccount struct {
|
||||
Nick string `json:"nick"`
|
||||
PasswordHash string `json:"password_hash"`
|
||||
Email string `json:"email"`
|
||||
RegisterTime time.Time `json:"register_time"`
|
||||
LastSeen time.Time `json:"last_seen"`
|
||||
Settings map[string]string `json:"settings"`
|
||||
AccessList []string `json:"access_list"`
|
||||
Flags []string `json:"flags"`
|
||||
}
|
||||
|
||||
// RegisteredChannel represents a registered channel
|
||||
type RegisteredChannelServices struct {
|
||||
Name string `json:"name"`
|
||||
Founder string `json:"founder"`
|
||||
RegisterTime time.Time `json:"register_time"`
|
||||
Topic string `json:"topic"`
|
||||
Modes string `json:"modes"`
|
||||
AccessList map[string]*ChannelAccess `json:"access_list"`
|
||||
Settings map[string]string `json:"settings"`
|
||||
AutoModes map[string]string `json:"auto_modes"`
|
||||
}
|
||||
|
||||
// ChannelAccess represents access levels for channels - using the one from database.go
|
||||
// type ChannelAccess struct {
|
||||
// Nick string `json:"nick"`
|
||||
// Level int `json:"level"` // 0=banned, 1=voice, 2=halfop, 3=op, 4=sop, 5=founder
|
||||
// SetBy string `json:"set_by"`
|
||||
// SetTime time.Time `json:"set_time"`
|
||||
// LastUsed time.Time `json:"last_used"`
|
||||
// }
|
||||
|
||||
// Memo represents a user memo
|
||||
type Memo struct {
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
Message string `json:"message"`
|
||||
Time time.Time `json:"time"`
|
||||
Read bool `json:"read"`
|
||||
}
|
||||
|
||||
// ServicesConfig configuration for services
|
||||
type ServicesConfig struct {
|
||||
Enable bool `json:"enable"`
|
||||
NickServ NickServConfig `json:"nickserv"`
|
||||
ChanServ ChanServConfig `json:"chanserv"`
|
||||
OperServ OperServConfig `json:"operserv"`
|
||||
MemoServ MemoServConfig `json:"memoserv"`
|
||||
Database DatabaseConfig `json:"database"`
|
||||
}
|
||||
|
||||
type NickServConfig struct {
|
||||
Enable bool `json:"enable"`
|
||||
Nick string `json:"nick"`
|
||||
User string `json:"user"`
|
||||
Host string `json:"host"`
|
||||
Realname string `json:"realname"`
|
||||
ExpireTime int `json:"expire_time"` // Days until unused nicks expire
|
||||
IdentifyTimeout int `json:"identify_timeout"` // Seconds to identify before kill
|
||||
EmailVerify bool `json:"email_verify"`
|
||||
RestrictReg bool `json:"restrict_registration"`
|
||||
}
|
||||
|
||||
type ChanServConfig struct {
|
||||
Enable bool `json:"enable"`
|
||||
Nick string `json:"nick"`
|
||||
User string `json:"user"`
|
||||
Host string `json:"host"`
|
||||
Realname string `json:"realname"`
|
||||
ExpireTime int `json:"expire_time"` // Days until unused channels expire
|
||||
MaxChannels int `json:"max_channels"` // Max channels per user
|
||||
AutoDeop bool `json:"auto_deop"` // Auto-deop users without access
|
||||
}
|
||||
|
||||
type OperServConfig struct {
|
||||
Enable bool `json:"enable"`
|
||||
Nick string `json:"nick"`
|
||||
User string `json:"user"`
|
||||
Host string `json:"host"`
|
||||
Realname string `json:"realname"`
|
||||
}
|
||||
|
||||
type MemoServConfig struct {
|
||||
Enable bool `json:"enable"`
|
||||
Nick string `json:"nick"`
|
||||
User string `json:"user"`
|
||||
Host string `json:"host"`
|
||||
Realname string `json:"realname"`
|
||||
MaxMemos int `json:"max_memos"`
|
||||
MemoExpire int `json:"memo_expire"` // Days until memos expire
|
||||
}
|
||||
|
||||
// NewServicesManager creates a new services manager
|
||||
func NewServicesManager(server *Server, config *ServicesConfig) *ServicesManager {
|
||||
sm := &ServicesManager{
|
||||
server: server,
|
||||
config: config,
|
||||
enabled: config.Enable,
|
||||
}
|
||||
|
||||
if config.NickServ.Enable {
|
||||
sm.nickServ = &NickServ{
|
||||
manager: sm,
|
||||
nick: config.NickServ.Nick,
|
||||
accounts: make(map[string]*NickAccount),
|
||||
}
|
||||
}
|
||||
|
||||
if config.ChanServ.Enable {
|
||||
sm.chanServ = &ChanServ{
|
||||
manager: sm,
|
||||
nick: config.ChanServ.Nick,
|
||||
channels: make(map[string]*RegisteredChannel),
|
||||
}
|
||||
}
|
||||
|
||||
if config.OperServ.Enable {
|
||||
sm.operServ = &OperServ{
|
||||
manager: sm,
|
||||
nick: config.OperServ.Nick,
|
||||
}
|
||||
}
|
||||
|
||||
if config.MemoServ.Enable {
|
||||
sm.memoServ = &MemoServ{
|
||||
manager: sm,
|
||||
nick: config.MemoServ.Nick,
|
||||
memos: make(map[string][]*Memo),
|
||||
}
|
||||
}
|
||||
|
||||
return sm
|
||||
}
|
||||
|
||||
// Start initializes and starts all enabled services
|
||||
func (sm *ServicesManager) Start() error {
|
||||
if !sm.enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Println("Starting TechIRCd Services...")
|
||||
|
||||
// Create service clients
|
||||
if sm.nickServ != nil {
|
||||
sm.createServiceClient(sm.config.NickServ.Nick, sm.config.NickServ.User,
|
||||
sm.config.NickServ.Host, sm.config.NickServ.Realname)
|
||||
}
|
||||
|
||||
if sm.chanServ != nil {
|
||||
sm.createServiceClient(sm.config.ChanServ.Nick, sm.config.ChanServ.User,
|
||||
sm.config.ChanServ.Host, sm.config.ChanServ.Realname)
|
||||
}
|
||||
|
||||
if sm.operServ != nil {
|
||||
sm.createServiceClient(sm.config.OperServ.Nick, sm.config.OperServ.User,
|
||||
sm.config.OperServ.Host, sm.config.OperServ.Realname)
|
||||
}
|
||||
|
||||
if sm.memoServ != nil {
|
||||
sm.createServiceClient(sm.config.MemoServ.Nick, sm.config.MemoServ.User,
|
||||
sm.config.MemoServ.Host, sm.config.MemoServ.Realname)
|
||||
}
|
||||
|
||||
log.Println("Services started successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
// createServiceClient creates a virtual client for a service
|
||||
func (sm *ServicesManager) createServiceClient(nick, user, host, realname string) {
|
||||
// Create a virtual service client that appears as a regular user
|
||||
serviceClient := &Client{
|
||||
nick: nick,
|
||||
user: user,
|
||||
host: host,
|
||||
realname: realname,
|
||||
server: sm.server,
|
||||
modes: make(map[rune]bool),
|
||||
channels: make(map[string]*Channel),
|
||||
capabilities: make(map[string]bool),
|
||||
}
|
||||
|
||||
// Set service modes
|
||||
serviceClient.modes['S'] = true // Service mode
|
||||
serviceClient.modes['o'] = true // Operator mode
|
||||
|
||||
// Add to server
|
||||
sm.server.clients[strings.ToLower(nick)] = serviceClient
|
||||
|
||||
// Send introduction to network
|
||||
sm.server.BroadcastToServers(fmt.Sprintf(":%s NICK %s", sm.server.config.Server.Name, nick))
|
||||
}
|
||||
|
||||
// HandleMessage processes messages directed to services
|
||||
func (sm *ServicesManager) HandleMessage(from *Client, target, message string) bool {
|
||||
if !sm.enabled {
|
||||
return false
|
||||
}
|
||||
|
||||
lowerTarget := strings.ToLower(target)
|
||||
parts := strings.Fields(message)
|
||||
if len(parts) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
command := strings.ToUpper(parts[0])
|
||||
|
||||
switch lowerTarget {
|
||||
case strings.ToLower(sm.config.NickServ.Nick):
|
||||
return sm.handleNickServCommand(from, command, parts[1:])
|
||||
case strings.ToLower(sm.config.ChanServ.Nick):
|
||||
return sm.handleChanServCommand(from)
|
||||
case strings.ToLower(sm.config.OperServ.Nick):
|
||||
return sm.handleOperServCommand(from)
|
||||
case strings.ToLower(sm.config.MemoServ.Nick):
|
||||
return sm.handleMemoServCommand(from)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// NickServ command handlers
|
||||
func (sm *ServicesManager) handleNickServCommand(from *Client, command string, args []string) bool {
|
||||
switch command {
|
||||
case "REGISTER":
|
||||
return sm.handleNickServRegister(from, args)
|
||||
case "IDENTIFY":
|
||||
return sm.handleNickServIdentify(from, args)
|
||||
case "INFO":
|
||||
return sm.handleNickServInfo(from, args)
|
||||
case "DROP":
|
||||
return sm.handleNickServDrop(from, args)
|
||||
case "SET":
|
||||
return sm.handleNickServSet(from)
|
||||
case "ACCESS":
|
||||
return sm.handleNickServAccess(from)
|
||||
case "HELP":
|
||||
return sm.handleNickServHelp(from)
|
||||
default:
|
||||
sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
|
||||
fmt.Sprintf("Unknown command %s. Type /msg %s HELP for help.", command, sm.config.NickServ.Nick))
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func (sm *ServicesManager) handleNickServRegister(from *Client, args []string) bool {
|
||||
if len(args) < 2 {
|
||||
sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
|
||||
"Syntax: REGISTER <password> <email>")
|
||||
return true
|
||||
}
|
||||
|
||||
nick := strings.ToLower(from.nick)
|
||||
password := args[0]
|
||||
email := args[1]
|
||||
|
||||
// Check if nick is already registered
|
||||
if _, exists := sm.nickServ.accounts[nick]; exists {
|
||||
sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
|
||||
"This nickname is already registered.")
|
||||
return true
|
||||
}
|
||||
|
||||
// Hash password
|
||||
hash := sha256.Sum256([]byte(password))
|
||||
passwordHash := hex.EncodeToString(hash[:])
|
||||
|
||||
// Create account
|
||||
account := &NickAccount{
|
||||
Nick: from.nick,
|
||||
PasswordHash: passwordHash,
|
||||
Email: email,
|
||||
RegisterTime: time.Now(),
|
||||
LastSeen: time.Now(),
|
||||
Settings: make(map[string]string),
|
||||
AccessList: []string{},
|
||||
Flags: []string{},
|
||||
}
|
||||
|
||||
sm.nickServ.accounts[nick] = account
|
||||
|
||||
// Set user as identified
|
||||
from.account = from.nick
|
||||
|
||||
sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
|
||||
fmt.Sprintf("Nickname %s has been registered successfully.", from.nick))
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (sm *ServicesManager) handleNickServIdentify(from *Client, args []string) bool {
|
||||
if len(args) < 1 {
|
||||
sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
|
||||
"Syntax: IDENTIFY <password>")
|
||||
return true
|
||||
}
|
||||
|
||||
nick := strings.ToLower(from.nick)
|
||||
password := args[0]
|
||||
|
||||
account, exists := sm.nickServ.accounts[nick]
|
||||
if !exists {
|
||||
sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
|
||||
"This nickname is not registered.")
|
||||
return true
|
||||
}
|
||||
|
||||
// Check password
|
||||
hash := sha256.Sum256([]byte(password))
|
||||
passwordHash := hex.EncodeToString(hash[:])
|
||||
|
||||
if account.PasswordHash != passwordHash {
|
||||
sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
|
||||
"Invalid password.")
|
||||
return true
|
||||
}
|
||||
|
||||
// Set user as identified
|
||||
from.account = from.nick
|
||||
account.LastSeen = time.Now()
|
||||
|
||||
sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
|
||||
fmt.Sprintf("You are now identified for %s.", from.nick))
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (sm *ServicesManager) handleNickServInfo(from *Client, args []string) bool {
|
||||
var targetNick string
|
||||
if len(args) > 0 {
|
||||
targetNick = args[0]
|
||||
} else {
|
||||
targetNick = from.nick
|
||||
}
|
||||
|
||||
nick := strings.ToLower(targetNick)
|
||||
account, exists := sm.nickServ.accounts[nick]
|
||||
if !exists {
|
||||
sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
|
||||
fmt.Sprintf("The nickname %s is not registered.", targetNick))
|
||||
return true
|
||||
}
|
||||
|
||||
sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
|
||||
fmt.Sprintf("Information for %s:", account.Nick))
|
||||
sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
|
||||
fmt.Sprintf("Registered: %s", account.RegisterTime.Format("Jan 02, 2006 15:04:05 MST")))
|
||||
sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
|
||||
fmt.Sprintf("Last seen: %s", account.LastSeen.Format("Jan 02, 2006 15:04:05 MST")))
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (sm *ServicesManager) handleNickServDrop(from *Client, _ []string) bool {
|
||||
nick := strings.ToLower(from.nick)
|
||||
|
||||
if from.account != from.nick {
|
||||
sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
|
||||
"You must be identified to drop your nickname.")
|
||||
return true
|
||||
}
|
||||
|
||||
delete(sm.nickServ.accounts, nick)
|
||||
from.account = ""
|
||||
|
||||
sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
|
||||
fmt.Sprintf("Nickname %s has been dropped.", from.nick))
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (sm *ServicesManager) handleNickServSet(from *Client) bool {
|
||||
// Handle SET commands (EMAIL, PASSWORD, etc.)
|
||||
sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
|
||||
"SET command not yet implemented.")
|
||||
return true
|
||||
}
|
||||
|
||||
func (sm *ServicesManager) handleNickServAccess(from *Client) bool {
|
||||
// Handle ACCESS commands (ADD, DEL, LIST)
|
||||
sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick,
|
||||
"ACCESS command not yet implemented.")
|
||||
return true
|
||||
}
|
||||
|
||||
func (sm *ServicesManager) handleNickServHelp(from *Client) bool {
|
||||
help := []string{
|
||||
"*** NickServ Help ***",
|
||||
"REGISTER <password> <email> - Register your nickname",
|
||||
"IDENTIFY <password> - Identify to your nickname",
|
||||
"INFO [nick] - Show information about a nickname",
|
||||
"DROP - Drop your nickname registration",
|
||||
"SET - Change settings",
|
||||
"ACCESS - Manage access list",
|
||||
"HELP - Show this help",
|
||||
}
|
||||
|
||||
for _, line := range help {
|
||||
sm.sendServiceNotice(sm.config.NickServ.Nick, from.nick, line)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// ChanServ command handlers (placeholder)
|
||||
func (sm *ServicesManager) handleChanServCommand(from *Client) bool {
|
||||
sm.sendServiceNotice(sm.config.ChanServ.Nick, from.nick,
|
||||
"ChanServ commands not yet implemented.")
|
||||
return true
|
||||
}
|
||||
|
||||
// OperServ command handlers (placeholder)
|
||||
func (sm *ServicesManager) handleOperServCommand(from *Client) bool {
|
||||
sm.sendServiceNotice(sm.config.OperServ.Nick, from.nick,
|
||||
"OperServ commands not yet implemented.")
|
||||
return true
|
||||
}
|
||||
|
||||
// MemoServ command handlers (placeholder)
|
||||
func (sm *ServicesManager) handleMemoServCommand(from *Client) bool {
|
||||
sm.sendServiceNotice(sm.config.MemoServ.Nick, from.nick,
|
||||
"MemoServ commands not yet implemented.")
|
||||
return true
|
||||
}
|
||||
|
||||
// sendServiceNotice sends a notice from a service to a user
|
||||
func (sm *ServicesManager) sendServiceNotice(from, to, message string) {
|
||||
if client := sm.server.GetClient(to); client != nil {
|
||||
client.SendMessage(fmt.Sprintf(":%s NOTICE %s :%s", from, to, message))
|
||||
}
|
||||
}
|
||||
|
||||
// IsServiceNick checks if a nick is a service
|
||||
func (sm *ServicesManager) IsServiceNick(nick string) bool {
|
||||
if !sm.enabled {
|
||||
return false
|
||||
}
|
||||
|
||||
lowerNick := strings.ToLower(nick)
|
||||
return lowerNick == strings.ToLower(sm.config.NickServ.Nick) ||
|
||||
lowerNick == strings.ToLower(sm.config.ChanServ.Nick) ||
|
||||
lowerNick == strings.ToLower(sm.config.OperServ.Nick) ||
|
||||
lowerNick == strings.ToLower(sm.config.MemoServ.Nick)
|
||||
}
|
||||
|
||||
// GetUserAccount returns the account for a nick
|
||||
func (sm *ServicesManager) GetUserAccount(nick string) *NickAccount {
|
||||
if sm.nickServ == nil {
|
||||
return nil
|
||||
}
|
||||
return sm.nickServ.accounts[strings.ToLower(nick)]
|
||||
}
|
||||
|
||||
// IsIdentified checks if a user is identified
|
||||
func (sm *ServicesManager) IsIdentified(nick string) bool {
|
||||
if client := sm.server.GetClient(nick); client != nil {
|
||||
return client.account != ""
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user