diff options
| author | Christine Dodrill <xena@yolo-swag.com> | 2015-06-30 20:25:22 -0700 |
|---|---|---|
| committer | Christine Dodrill <xena@yolo-swag.com> | 2015-06-30 20:25:22 -0700 |
| commit | e338c8e86cab00ad4ec13ce4521f77aa32a0f4c5 (patch) | |
| tree | 72115c104c2940eb6020c66444f480ec4a8f4594 | |
| parent | 7e32458face5dbe95279c0255dc0c9631399db11 (diff) | |
| download | x-e338c8e86cab00ad4ec13ce4521f77aa32a0f4c5.tar.xz x-e338c8e86cab00ad4ec13ce4521f77aa32a0f4c5.zip | |
Add amerge
| -rw-r--r-- | irc/amerge/README.md | 4 | ||||
| -rw-r--r-- | irc/amerge/database.go | 576 | ||||
| -rw-r--r-- | irc/amerge/main.go | 16 |
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) + } +} |
