route/vendor/github.com/Xe/x/irc/amerge/database.go

592 lines
12 KiB
Go

package main
import (
"bufio"
"errors"
"fmt"
"log"
"os"
"strconv"
"strings"
)
var (
NoSuchAcctErr = errors.New("There is no such account by that name")
NoSuchChanErr = errors.New("There is no such channel by that name")
NoSuchGroupErr = errors.New("There is no such group by that name")
)
type Database struct {
Version string
ModuleDeps []*Line
LastUID string
LastKID int
LastXID int
LastQID int
Accounts map[string]*Account
Channels map[string]*Channel
Bots map[string]*Bot
Groups map[string]*Group
Names map[string]*Name
Badwords []Badword
Klines []Kline
ConnectInfos []ConnectInfo
HostOffers []HostOffer
HostRequests []HostRequest
ClonesExemptions []ClonesExemption
Rwatches []Line
lines []*Line
file *bufio.Scanner
}
func NewDatabase(fname string) (db *Database, err error) {
fin, err := os.Open(fname)
if err != nil {
return
}
db = &Database{
Accounts: make(map[string]*Account),
Channels: make(map[string]*Channel),
Bots: make(map[string]*Bot),
Groups: make(map[string]*Group),
Names: make(map[string]*Name),
}
db.file = bufio.NewScanner(fin)
for db.file.Scan() {
rawline := db.file.Text()
l := &Line{}
split := strings.Split(rawline, " ")
l.Verb = split[0]
l.Args = split[1:]
db.lines = append(db.lines, l)
switch l.Verb {
case "DBV": // Database version
db.Version = l.Args[0]
case "MDEP": // Module dependency
db.ModuleDeps = append(db.ModuleDeps, l)
case "LUID": // Last used UID for accounts
db.LastUID = l.Args[0]
case "MU": // Create a user account
a := &Account{
Name: l.Args[1],
UID: l.Args[0],
Email: l.Args[3],
Password: l.Args[2],
Regtime: l.Args[4],
LastSeenTime: l.Args[5],
Metadata: make(map[string]string),
}
db.Accounts[strings.ToUpper(a.Name)] = a
case "MDU": // User metadata
account, err := db.GetAccount(l.Args[0])
if err != nil {
log.Panicf("Tried to read account %s but got %#v???", l.Args[0], err)
}
account.Metadata[l.Args[1]] = strings.Join(l.Args[2:], " ")
case "AC": // Account access rule (prevents nickserv protections for a mask)
account, err := db.GetAccount(l.Args[0])
if err != nil {
log.Panicf("Tried to read account %s but got %#v???", l.Args[0], err)
}
ac := Access{
AccountName: l.Args[0],
Mask: l.Args[1],
}
account.AccessList = append(account.AccessList, ac)
case "MI": // MemoServ IGNORE for a user
account, err := db.GetAccount(l.Args[0])
if err != nil {
log.Panicf("Tried to read account %s but got %#v???", l.Args[0], err)
}
account.Ignores = append(account.Ignores, l.Args[1])
case "MN": // Account nickname in nick group
account, err := db.GetAccount(l.Args[0])
if err != nil {
log.Panicf("Tried to read account %s but got %#v???", l.Args[0], err)
}
gn := GroupedNick{
Account: l.Args[0],
Name: l.Args[1],
Regtime: l.Args[2],
Seentime: l.Args[3],
}
account.Nicks = append(account.Nicks, gn)
case "MCFP": // Certificate Fingerprint
account, err := db.GetAccount(l.Args[0])
if err != nil {
log.Panicf("Tried to read account %s but got %#v???", l.Args[0], err)
}
account.CertFP = append(account.CertFP, l.Args[1])
case "ME": // Memo in user's inbox
account, err := db.GetAccount(l.Args[0])
if err != nil {
log.Panicf("Tried to read account %s but got %#v???", l.Args[0], err)
}
m := Memo{
Inbox: l.Args[0],
From: l.Args[1],
Time: l.Args[2],
Read: l.Args[3] == "1",
Contents: strings.Join(l.Args[4:], " "),
}
account.Memos = append(account.Memos, m)
case "MC": // Create a channel
mlockon, err := strconv.ParseInt(l.Args[4], 16, 0)
if err != nil {
panic(err)
}
c := &Channel{
Name: l.Args[0],
Regtime: l.Args[1],
Seentime: l.Args[2],
Flags: l.Args[3],
MlockOn: int(mlockon),
Metadata: make(map[string]string),
AccessMetadata: make(map[string]AccessMetadata),
}
db.Channels[strings.ToUpper(l.Args[0])] = c
case "CA": // ChanAcs
c, err := db.GetChannel(l.Args[0])
if err != nil {
log.Panicf("Tried to read channel %s but got %#v???", l.Args[0], err)
}
ca := ChanAc{
Channel: l.Args[0],
Account: l.Args[1],
FlagSet: l.Args[2],
DateGranted: l.Args[3],
WhoGranted: l.Args[4],
}
c.Access = append(c.Access, ca)
case "MDC": // Channel metadata
c, err := db.GetChannel(l.Args[0])
if err != nil {
log.Panicf("Tried to read channel %s but got %#v???", l.Args[0], err)
}
c.Metadata[l.Args[1]] = strings.Join(l.Args[2:], " ")
case "MDA": // Channel-based entity key->value
c, err := db.GetChannel(l.Args[0])
if err != nil {
log.Panicf("Tried to read channel %s but got %#v???", l.Args[0], err)
}
amd := AccessMetadata{
ChannelName: l.Args[0],
Entity: l.Args[1],
Key: l.Args[2],
Value: l.Args[3],
}
c.AccessMetadata[strings.ToUpper(amd.Key)] = amd
case "NAM":
nam := &Name{
Name: l.Args[0],
Metadata: make(map[string]string),
}
db.Names[strings.ToUpper(nam.Name)] = nam
case "MDN":
nam, ok := db.Names[strings.ToUpper(l.Args[0])]
if !ok {
panic("Atheme is broken with things")
}
nam.Metadata[l.Args[1]] = strings.Join(l.Args[2:], " ")
case "KID": // Biggest kline id used
kid, err := strconv.ParseInt(l.Args[0], 10, 0)
if err != nil {
panic("atheme is broken with KID " + l.Args[0])
}
db.LastKID = int(kid)
case "XID": // Biggest xline id used
xid, err := strconv.ParseInt(l.Args[0], 10, 0)
if err != nil {
panic("atheme is broken with XID " + l.Args[0])
}
db.LastXID = int(xid)
case "QID": // Biggest qline id used
qid, err := strconv.ParseInt(l.Args[0], 10, 0)
if err != nil {
panic("atheme is broken with QID " + l.Args[0])
}
db.LastQID = int(qid)
case "KL": // kline
id, err := strconv.ParseInt(l.Args[0], 10, 0)
if err != nil {
panic(err)
}
kl := Kline{
ID: int(id),
User: l.Args[1],
Host: l.Args[2],
Duration: l.Args[3],
DateSet: l.Args[4],
WhoSet: l.Args[5],
Reason: strings.Join(l.Args[6:], " "),
}
db.Klines = append(db.Klines, kl)
case "BOT": // BotServ bot
bot := &Bot{
Nick: l.Args[0],
User: l.Args[1],
Host: l.Args[2],
IsPrivate: l.Args[3] == "1",
CreationDate: l.Args[4],
Gecos: l.Args[5],
}
db.Bots[strings.ToUpper(bot.Nick)] = bot
case "BW": // BADWORDS entry
bw := Badword{
Mask: l.Args[0],
TimeSet: l.Args[1],
Setter: l.Args[2],
}
if len(l.Args) == 5 {
bw.Channel = l.Args[3]
bw.Action = l.Args[4]
} else {
bw.Setter = bw.Setter + " " + l.Args[3]
bw.Channel = l.Args[4]
bw.Action = l.Args[5]
}
db.Badwords = append(db.Badwords, bw) // TODO: move this to Channel?
case "GRP": // Group
g := &Group{
UID: l.Args[0],
Name: l.Args[1],
CreationDate: l.Args[2],
Flags: l.Args[3],
Metadata: make(map[string]string),
}
db.Groups[strings.ToUpper(l.Args[1])] = g
case "GACL": // Group access list
g, err := db.GetGroup(l.Args[0])
if err != nil {
log.Panicf("Tried to read group %s but got %#v???", l.Args[0], err)
}
gacl := GroupACL{
GroupName: l.Args[0],
AccountName: l.Args[1],
Flags: l.Args[2],
}
g.ACL = append(g.ACL, gacl)
case "MDG": // Group Metadata
g, err := db.GetGroup(l.Args[0])
if err != nil {
log.Panicf("Tried to read group %s but got %#v???", l.Args[0], err)
}
g.Metadata[l.Args[1]] = strings.Join(l.Args[2:], " ")
case "CLONES-EX": // CLONES exemptions
ce := ClonesExemption{
IP: l.Args[0],
Min: l.Args[1],
Max: l.Args[2],
Expiry: l.Args[3],
Reason: strings.Join(l.Args[4:], " "),
}
db.ClonesExemptions = append(db.ClonesExemptions, ce)
case "LI": // InfoServ INFO posts
ci := ConnectInfo{
Creator: l.Args[0],
Topic: l.Args[1],
CreationDate: l.Args[2],
Body: strings.Join(l.Args[3:], " "),
}
db.ConnectInfos = append(db.ConnectInfos, ci)
case "HO": // Vhost offer
var ho HostOffer
if len(l.Args) == 3 {
ho = HostOffer{
Vhost: l.Args[0],
CreationDate: l.Args[1],
Creator: l.Args[2],
}
} else {
ho = HostOffer{
Group: l.Args[0],
Vhost: l.Args[1],
CreationDate: l.Args[2],
Creator: l.Args[3],
}
}
db.HostOffers = append(db.HostOffers, ho)
case "HR": // Vhost request
hr := HostRequest{
Account: l.Args[0],
Vhost: l.Args[1],
RequestTime: l.Args[2],
}
db.HostRequests = append(db.HostRequests, hr)
// Verbs to ignore
case "":
default:
fmt.Printf("%#v\n", l)
}
}
return
}
func (db *Database) GetAccount(name string) (*Account, error) {
account, ok := db.Accounts[strings.ToUpper(name)]
if !ok {
return nil, NoSuchAcctErr
}
return account, nil
}
func (db *Database) GetChannel(name string) (*Channel, error) {
channel, ok := db.Channels[strings.ToUpper(name)]
if !ok {
return nil, NoSuchChanErr
}
return channel, nil
}
func (db *Database) GetGroup(name string) (*Group, error) {
group, ok := db.Groups[strings.ToUpper(name)]
if !ok {
return nil, NoSuchGroupErr
}
return group, nil
}
func (db *Database) GetBot(name string) (*Bot, error) {
group, ok := db.Bots[strings.ToUpper(name)]
if !ok {
return nil, NoSuchGroupErr
}
return group, nil
}
type Line struct {
Verb string
Args []string
}
type Account struct {
Name string
Email string
Flags string
Kind string
UID string
Password string
Regtime string
LastSeenTime string
Metadata map[string]string
Nicks []GroupedNick
Memos []Memo
CertFP []string
AccessList []Access
Ignores []string
}
type Access struct {
AccountName string
Mask string
}
type Name struct {
Name string
Metadata map[string]string
}
type GroupedNick struct {
Account string
Name string
Regtime string
Seentime string
}
type Memo struct {
Inbox string
From string // an account name
Time string
Read bool
Contents string
}
type Channel struct {
Name string
Regtime string
Seentime string
Flags string
MlockOn int
MlockOff int
MlockLimit int
MlockKey string
Access []ChanAc
Metadata map[string]string
AccessMetadata map[string]AccessMetadata
}
type AccessMetadata struct {
ChannelName string
Entity string
Key string
Value string
}
type ChanAc struct {
Channel string
Account string
FlagSet string
DateGranted string
WhoGranted string
}
type Kline struct {
ID int
User string
Host string
Duration string
DateSet string
WhoSet string
Reason string
}
type Bot struct {
Nick string
User string
Host string
IsPrivate bool
CreationDate string
Gecos string
}
type Badword struct {
Mask string
TimeSet string
Setter string // can be Foo or Foo (Bar)
Channel string
Action string
}
type Group struct {
UID string
Name string
CreationDate string
Flags string
ACL []GroupACL
Metadata map[string]string
}
type GroupACL struct {
GroupName string
AccountName string
Flags string
}
type ConnectInfo struct {
Creator string
Topic string
CreationDate string
Body string
}
type HostOffer struct { // if args number is 3 no group
Group string
Vhost string
CreationDate string
Creator string
}
type HostRequest struct {
Account string
Vhost string
RequestTime string
}
type ClonesExemption struct {
IP string
Min string
Max string
Expiry string
Reason string
}