592 lines
12 KiB
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
|
||
|
}
|