package main import ( //"bufio" //"bytes" "fmt" "log" //"io" //"io/ioutil" "net" //"math/rand" "net/textproto" //"regexp" "strings" "time" //"unicode" "os" "os/exec" ) const ( VERSION = "0.1.2" ) var ( autocreate bool tcpmode = "tcp4" ) func main() { if len(os.Args) < 5 { log.Printf("usage: %s [user:pass@]news.remote.com:119 [user:pass@]localhost:119 [\"\"|gmane.*] (autocreate:)[true|false] (tcpmode:)[tcp|tcp4|tcp6]", os.Args[0]) /* ./LISTnewgroup news.gmane.io:119 localhost:119 gmane.* false ./LISTnewgroup news.gmane.io:119 localhost:119 gwene.* false * ./LISTnewgroup news.gmane.io:119 10.10.3.1:119 gmane.* false ./LISTnewgroup news.gmane.io:119 10.10.3.2:119 gwene.* false * * * ./LISTnewgroup news.gmane.io:119 read.newsdeef.eu:119 "" true ./LISTnewgroup news.gmane.io:119 read.newsdeef.eu:119 "" true */ os.Exit(10) } if os.Args[1] == "--version" { log.Printf(VERSION) os.Exit(0) } src_srv := os.Args[1] dst_srv := os.Args[2] aprefix := os.Args[3] prefix := strings.Split(aprefix, "*")[0] if os.Args[4] == "true" { autocreate = true } if len(os.Args) >= 6 { tcpmode = os.Args[5] } var newgroups []string ignored := 0 blacklistP := []string{"gmane.", "gwene.", "alt.sex", "alt.bina", "alt.bain", "alt.bian", "alt.bani", "alt.banar"} blacklistC := []string{"alt-bin", "alt.bin", "alt.bit", "alt.sex", ".bina", ".bain", "pictures", ".binries", "mp3", "cd.image", "files.images", "music.bin"} if src_list, _, err := doLIST(src_srv, prefix); err == nil { if _, dst_lmap, err := doLIST(dst_srv, prefix); err == nil { log.Printf("main() got src_srv=%s src_list=%d dst_srv=%s dst_lmap=%d", src_srv, len(src_list), dst_srv, len(dst_lmap)) looplist: for _, line := range src_list { group := strings.ToLower(strings.Split(line, " ")[0]) if strings.HasPrefix(group, "alt.") { if strings.Contains(group, "sex") { ignored++ continue looplist } } for _, bl := range blacklistP { if strings.HasPrefix(group, bl) { ignored++ continue looplist } } for _, bl := range blacklistC { if strings.Contains(group, bl) { ignored++ continue looplist } } if prefix != "" { if !strings.HasPrefix(group, prefix) { //log.Printf("src ignore agroup=%s", agroup) ignored++ continue } } if !dst_lmap[group] { newgroups = append(newgroups, group) } } // end for src_list if len(newgroups) > 0 { log.Printf("newgroups=%d", len(newgroups)) i, t := 1, len(newgroups) for _, newgroup := range newgroups { if autocreate { cmd := exec.Command("ctlinnd", "newgroup", newgroup) err := cmd.Run() if err != nil { log.Printf("failed ctlinnd newgroup %s err='%v'", newgroup, err) } else { log.Printf("exec (%06d/%06d): newgroup %s", i, t, newgroup) } } else { fmt.Println("ctlinnd newgroup " + newgroup) } i++ } } else { fmt.Println("No New NewsGroups") } } // end if dst_list } // end if src_list } // end func main func doLIST(nntpserv string, prefix string) ([]string, map[string]bool, error) { srvtp, conn, err := connectBackend(nntpserv) defer conn.Close() if err != nil { //fixme set done here log.Printf("ERROR nntpserv=%s err='%v'", nntpserv, err) return nil, nil, err } else if srvtp != nil { log.Printf("connected %s", nntpserv) if list, err := getLIST(nntpserv, srvtp, prefix); list == nil { return nil, nil, err } else { lmap := make(map[string]bool) for _, line := range list { group := strings.ToLower(strings.Split(line, " ")[0]) lmap[group] = true } log.Printf("nntpserv=%s got LIST=%d LMAP=%d", nntpserv, len(list), len(lmap)) return list, lmap, nil } } return nil, nil, err } // end func doLIST func connectBackend(nntpserv string) (*textproto.Conn, net.Conn, error) { needsauth, username, password := false, "", "" if strings.Contains(nntpserv, "@") { data := strings.Split(nntpserv, "@") if len(data) == 2 && strings.Contains(data[0], ":") { username = strings.Split(data[0], ":")[0] password = strings.Split(data[0], ":")[1] } needsauth = true } conn, err := net.Dial(tcpmode, nntpserv) if err != nil { log.Printf("error net.Dial err='%v' retry", err) time.Sleep(1000 * time.Millisecond) return connectBackend(nntpserv) } srvtp := textproto.NewConn(conn) code, msg, err := srvtp.ReadCodeLine(20) if code != 200 && code != 201 { log.Printf("connectBackend %s code=%d msg='%s' err='%v'", nntpserv, code, msg, err) os.Exit(1) //return nil, nil, err } if needsauth { if authed, err := auth(nntpserv, srvtp, username, password); err != nil { return nil, nil, err } else if !authed { log.Printf("ERROR: nntpserv=%s needsauth=%t authed=%t err=%v", nntpserv, needsauth, authed, err) return nil, nil, err } } return srvtp, conn, err } // func connectBackend func auth(nntpserv string, srvtp *textproto.Conn, username string, password string) (bool, error) { if err := srvtp.PrintfLine("AUTHINFO USER %s", username); err != nil { log.Printf("ERROR: 'AUTHINFO USER' returned err=%v nntpserv=%s", err, nntpserv) return false, err } if rcode, msg, err := srvtp.ReadCodeLine(381); err != nil { log.Printf("ERROR: 'AUTHINFO USER' ReadCodeLine returned rcode=%d, msg='%s', nntpserv=%s", rcode, msg, nntpserv) return false, err } if err := srvtp.PrintfLine("AUTHINFO PASS %s", password); err != nil { log.Printf("ERROR: 'AUTHINFO PASS' returned err=%v nntpserv=%s", err, nntpserv) return false, err } if rcode, msg, err := srvtp.ReadCodeLine(281); err != nil { log.Printf("ERROR: 'AUTHINFO PASS' ReadCodeLine returned rcode=%d, msg='%s', nntpserv=%s", rcode, msg, nntpserv) return false, err } log.Printf("AUTHED nntpserv=%s", nntpserv) return true, nil } // end func auth func getLIST(nntpserv string, srvtp *textproto.Conn, prefix string) ([]string, error) { var err error var id uint code, msg := -1, "" islocalhost := false if strings.HasPrefix(nntpserv, "localhost:") { islocalhost = true } if islocalhost || prefix == "" { id, err = srvtp.Cmd("LIST") } else { cmd := fmt.Sprintf("LIST COUNTS %s*", prefix) log.Printf("nntpserv=%s cmd='%s'", nntpserv, cmd) id, err = srvtp.Cmd(cmd) } srvtp.StartResponse(id) if code, msg, err = srvtp.ReadCodeLine(215); err != nil { srvtp.EndResponse(id) log.Printf("getLIST() ERROR nntpserv=%s code=%d msg='%s'", nntpserv, code, msg) return nil, err } log.Printf("getLIST() nntpserv=%s code=%d msg='%s'", nntpserv, code, msg) srvtp.EndResponse(id) if list, err := srvtp.ReadDotLines(); err != nil { return nil, err } else { return list, err } return nil, err } // end func getLIST