aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristine Dodrill <xena@yolo-swag.com>2015-06-30 20:25:22 -0700
committerChristine Dodrill <xena@yolo-swag.com>2015-06-30 20:25:22 -0700
commite338c8e86cab00ad4ec13ce4521f77aa32a0f4c5 (patch)
tree72115c104c2940eb6020c66444f480ec4a8f4594
parent7e32458face5dbe95279c0255dc0c9631399db11 (diff)
downloadx-e338c8e86cab00ad4ec13ce4521f77aa32a0f4c5.tar.xz
x-e338c8e86cab00ad4ec13ce4521f77aa32a0f4c5.zip
Add amerge
-rw-r--r--irc/amerge/README.md4
-rw-r--r--irc/amerge/database.go576
-rw-r--r--irc/amerge/main.go16
3 files changed, 596 insertions, 0 deletions
diff --git a/irc/amerge/README.md b/irc/amerge/README.md
new file mode 100644
index 0000000..0f35625
--- /dev/null
+++ b/irc/amerge/README.md
@@ -0,0 +1,4 @@
+amerge
+======
+
+Utility for scraping and (later) merging atheme databases.
diff --git a/irc/amerge/database.go b/irc/amerge/database.go
new file mode 100644
index 0000000..f97cf4d
--- /dev/null
+++ b/irc/amerge/database.go
@@ -0,0 +1,576 @@
+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
+
+ 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],
+
+ 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 "GRVER", "XL", "FIM", "FAQ", "RW", "RR", "BE", "CF", "GDBV", "GFA", "CFDBV", "CFCHAN", "CFOP", "BOT-COUNT", "CLONES-DBV", "CLONES-CK", "CLONES-CD", "CLONES-GR":
+
+ 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
+}
+
+type Line struct {
+ Verb string
+ Args []string
+}
+
+type Account struct {
+ Name string
+ Email string
+ Flags string
+ Kind string
+ UID string
+ Password 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
+}
diff --git a/irc/amerge/main.go b/irc/amerge/main.go
new file mode 100644
index 0000000..7fc9ede
--- /dev/null
+++ b/irc/amerge/main.go
@@ -0,0 +1,16 @@
+package main
+
+import "flag"
+
+var (
+ fname = flag.String("file", "./services.db", "database to read from")
+)
+
+func main() {
+ flag.Parse()
+
+ _, err := NewDatabase(*fname)
+ if err != nil {
+ panic(err)
+ }
+}