package door import ( "bufio" "flag" "fmt" "os" "strconv" "strings" "syscall" "time" ) const CRNL = "\r\n" /* door32.sys: 0 Line 1 : Comm type (0=local, 1=serial, 2=telnet) 0 Line 2 : Comm or socket handle 38400 Line 3 : Baud rate Mystic 1.07 Line 4 : BBSID (software name and version) 1 Line 5 : User record position (1-based) James Coyle Line 6 : User's real name g00r00 Line 7 : User's handle/alias 255 Line 8 : User's security level 58 Line 9 : User's time left (in minutes) 1 Line 10: Emulation *See Below 1 Line 11: Current node number */ func Color(arg ...int) string { var result string = "\x1b[" for i := range arg { result += fmt.Sprintf("%d;", arg[i]) } result = result[:len(result)-1] result += "m" return result } func ColorText(color string) string { // split on spaces, uppercase, match first 3 letter var result []int var bg bool result = append(result, 0) parts := strings.Fields(strings.ToUpper(color)) for _, part := range parts { switch part { case "BLACK", "BLA": if bg { result = append(result, 40) } else { result = append(result, 30) } case "RED": if bg { result = append(result, 41) } else { result = append(result, 31) } case "GREEN", "GRE": if bg { result = append(result, 42) } else { result = append(result, 32) } case "BROWN", "BRO": if bg { result = append(result, 43) } else { result = append(result, 33) } case "YELLOW", "YEL": if bg { result = append(result, 43) } else { result = append(result, 33) } case "BLUE", "BLU": if bg { result = append(result, 44) } else { result = append(result, 34) } case "MAGENTA", "MAG": if bg { result = append(result, 45) } else { result = append(result, 35) } case "CYAN", "CYA": if bg { result = append(result, 46) } else { result = append(result, 36) } case "WHITE", "WHI": if bg { result = append(result, 47) } else { result = append(result, 37) } case "BOLD", "BOL", "BRIGHT", "BRI": result = append(result, 1) case "ON": bg = true case "BLINK", "BLI": result = append(result, 5) case "INVERT", "INVERSE", "INV": result = append(result, 7) default: fmt.Println("ColorText Unknown:", part) } } // fmt.Println("ColorText:", result) return Color(result...) } var Reset string = Color(0) var READFD int var WRITEFD int type DropfileConfig struct { comm_type int comm_handle int baudrate int BBSID string user_number int real_name string handle string security_level int time_left int emulation int node_number int } type Door struct { config DropfileConfig READFD int WRITEFD int } func (d *Door) ReadDropfile(filename string) { file, err := os.Open(filename) if err != nil { fmt.Printf("Open(%s): %s\n", filename, err) os.Exit(2) } defer file.Close() var lines []string // read line by line // The scanner handles DOS and linux file endings. scanner := bufio.NewScanner(file) for scanner.Scan() { line := scanner.Text() lines = append(lines, line) // fmt.Printf("[%s]\n", line) } d.config.comm_type, err = strconv.Atoi(lines[0]) d.config.comm_handle, err = strconv.Atoi(lines[1]) d.config.baudrate, err = strconv.Atoi(lines[2]) d.config.BBSID = lines[3] d.config.user_number, err = strconv.Atoi(lines[4]) d.config.real_name = lines[5] d.config.handle = lines[6] d.config.security_level, err = strconv.Atoi(lines[7]) d.config.time_left, err = strconv.Atoi(lines[8]) d.config.emulation, err = strconv.Atoi(lines[9]) d.config.node_number, err = strconv.Atoi(lines[10]) d.READFD = d.config.comm_handle //if d.READFD == 0 { // d.WRITEFD = 1 //} else { d.WRITEFD = d.config.comm_handle //} } func (d *Door) HasKey() bool { var fdsetRead = syscall.FdSet{} clearAll(&fdsetRead) set(&fdsetRead, d.READFD) timeout := syscall.Timeval{Sec: 0, Usec: 1} v, _ := syscall.Select(d.READFD+1, &fdsetRead, nil, nil, &timeout) if v == -1 { return false } if v == 0 { return false } return true } func (d *Door) detect() { // if d.config.comm_handle == 0 { // d.Write("\377\375\042\377\373\001") // fix telnet client // } d.Write("\x1b[0;30;40m\x1b[2J\x1b[H") // black on black, clrscr, go home d.Write("\x03\x04\x1b[6n") // hearts and diamonds does CP437 work? d.Write(CRNL + "\u2615\x1b[6n") d.Write("\x1b[999C\x1b[999B\x1b[6n" + Reset + "\x1b[2J\x1b[H") // goto end of screen + cursor pos // time.Sleep(50 * time.Millisecond) time.Sleep(250 * time.Millisecond) // read everything // telnet term isn't in RAW mode, so keys are buffer until if true { // d.HasKey() { buffer := make([]byte, 100) r, err := syscall.Read(d.READFD, buffer) results := string(buffer[:r]) results = strings.Replace(results, "\x1b", "^", -1) fmt.Println("DETECT:", r, err, results) } else { // local telnet echos the reply :() fmt.Println("DETECT: Nothing received.") } } func (d *Door) Init() { var dropfile string flag.StringVar(&dropfile, "d", "", "Path to dropfile") flag.Parse() if len(dropfile) == 0 { flag.PrintDefaults() os.Exit(2) } fmt.Printf("Loading: %s\n", dropfile) d.ReadDropfile(dropfile) fmt.Printf("BBS %s, User %s / Handle %s / File %d\n", d.config.BBSID, d.config.real_name, d.config.handle, d.READFD) d.detect() } func (d *Door) Write(output string) { buffer := []byte(output) n, err := syscall.Write(d.WRITEFD, buffer) if err != nil { fmt.Println("Write error/HANGUP?", n) } // No, this isn't it. The # of bytes in buffer == bytes written. if n != len(buffer) { fmt.Printf("Write fail: %d != %d\n", len(buffer), n) } } /* func write(output string, config *DropfileConfig) { buffer := []byte(output) n, err := syscall.Write(config.comm_handle, buffer) if err != nil { fmt.Println("Write error/HANGUP?", n) } } */ // from: https://github.com/yubo/dea_ng // https://github.com/yubo/dea_ng/blob/master/go/src/directoryserver/streaming.go func set(fdSetPtr *syscall.FdSet, fd int) { (*fdSetPtr).Bits[fd/64] |= 1 << uint64(fd%64) } func isSet(fdSetPtr *syscall.FdSet, fd int) bool { return ((*fdSetPtr).Bits[fd/64] & (1 << uint64(fd%64))) != 0 } func clearAll(fdSetPtr *syscall.FdSet) { for index, _ := range (*fdSetPtr).Bits { (*fdSetPtr).Bits[index] = 0 } } func (d *Door) SleepKey(sleep int64) int { // var fdsetRead, fdsetWrite, fdsete syscall.FdSet var fdsetRead syscall.FdSet // fdsetWrite := syscall.FdSet clearAll(&fdsetRead) // clearAll(&fdsetWrite) // clearAll(&fdsete) set(&fdsetRead, d.READFD) // timeout := syscall.Timeval{Sec: 0, Usec: 100} timeout := syscall.Timeval{Sec: sleep, Usec: 0} v, err := syscall.Select(d.READFD+1, &fdsetRead, nil, nil, &timeout) if v == -1 { fmt.Println("-1 : ", err) // hangup ?! return -2 } if v == 0 { // timeout return -1 } // var buffer []byte -- 0 byte buffer. doh! buffer := make([]byte, 1) r, err := syscall.Read(d.READFD, buffer) if r != 1 { fmt.Printf("Read said ready, but didn't read a character %d %v.", r, err) // hangup return -2 } return int(buffer[0]) } func (d *Door) Getch() int { // var fdsetRead, fdsetWrite, fdsete syscall.FdSet var fdsetRead syscall.FdSet // fdsetWrite := syscall.FdSet clearAll(&fdsetRead) // clearAll(&fdsetWrite) // clearAll(&fdsete) set(&fdsetRead, d.READFD) // timeout := syscall.Timeval{Sec: 0, Usec: 100} timeout := syscall.Timeval{Sec: 120, Usec: 0} v, err := syscall.Select(d.READFD+1, &fdsetRead, nil, nil, &timeout) if v == -1 { fmt.Println("-1 : ", err) // hangup ?! return -2 } if v == 0 { // timeout return -1 } // var buffer []byte -- 0 byte buffer. doh! buffer := make([]byte, 1) r, err := syscall.Read(d.READFD, buffer) if r != 1 { fmt.Printf("Read said ready, but didn't read a character %d %v.", r, err) // hangup return -2 } return int(buffer[0]) } /* func sleep_key(config *DropfileConfig, secs int) int { // var fdsetRead, fdsetWrite, fdsete syscall.FdSet var fdsetRead = syscall.FdSet{} // fdsetWrite := syscall.FdSet clearAll(&fdsetRead) // clearAll(&fdsetWrite) // clearAll(&fdsete) set(&fdsetRead, config.comm_handle) timeout := syscall.Timeval{Sec: int64(secs), Usec: 0} // v, err := syscall.Select(config.comm_handle+1, &fdsetRead, &fdsetWrite, &fdsete, &timeout) v, err := syscall.Select(config.comm_handle+1, &fdsetRead, nil, nil, &timeout) fmt.Println("v:", v, "err:", err) if v == -1 { fmt.Println("-1 : ", err) // hangup ?! return -2 } if v == 0 { // timeout return -1 } // var buffer []byte buffer := make([]byte, 1) // var buffer [1]byte r, err := syscall.Read(config.comm_handle, buffer) if r != 1 { fmt.Printf("Read said ready, but didn't read a character %d %v ?\n", r, err) // hangup return -2 } return int(buffer[0]) } */ /* func main() { fmt.Println("doorgo") var dropfile string flag.StringVar(&dropfile, "dropfile", "", "Dropfile to use") flag.Parse() if len(dropfile) == 0 { flag.PrintDefaults() os.Exit(2) } fmt.Printf("Loading: %s\n", dropfile) var config DropfileConfig read_dropfile(dropfile, &config) fmt.Printf("BBS %s, User %s / Handle %s\n", config.BBSID, config.real_name, config.handle) message := "Welcome BBS User!\n\r" // buffer := []byte(message) // n, err := syscall.Write(config.comm_handle, buffer) write(message, &config) write("Press a key...", &config) key := sleep_key(&config, 20) write("\n\r", &config) message = fmt.Sprintf("Key %d / %x\n\r", key, key) write(message, &config) write("\n\rReturning to BBS.\n\r", &config) fmt.Println("Done.") // fmt.Println(n, err) } */