door.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. package door
  2. import (
  3. "bufio"
  4. "flag"
  5. "fmt"
  6. "os"
  7. "strconv"
  8. "strings"
  9. "syscall"
  10. "time"
  11. "golang.org/x/sys/unix"
  12. )
  13. /*
  14. door32.sys:
  15. 0 Line 1 : Comm type (0=local, 1=serial, 2=telnet)
  16. 0 Line 2 : Comm or socket handle
  17. 38400 Line 3 : Baud rate
  18. Mystic 1.07 Line 4 : BBSID (software name and version)
  19. 1 Line 5 : User record position (1-based)
  20. James Coyle Line 6 : User's real name
  21. g00r00 Line 7 : User's handle/alias
  22. 255 Line 8 : User's security level
  23. 58 Line 9 : User's time left (in minutes)
  24. 1 Line 10: Emulation *See Below
  25. 1 Line 11: Current node number
  26. */
  27. func Color(arg ...int) string {
  28. var result string = "\x1b["
  29. for i := range arg {
  30. result += fmt.Sprintf("%d;", arg[i])
  31. }
  32. result += "m"
  33. return result
  34. }
  35. func ColorText(color string) string {
  36. // split on spaces, uppercase, match first 3 letter
  37. var result []int
  38. var bg bool
  39. result = append(result, 0)
  40. parts := strings.Fields(strings.ToUpper(color))
  41. for _, part := range parts {
  42. switch part {
  43. case "BLACK", "BLA":
  44. if bg {
  45. result = append(result, 40)
  46. } else {
  47. result = append(result, 30)
  48. }
  49. case "RED":
  50. if bg {
  51. result = append(result, 41)
  52. } else {
  53. result = append(result, 31)
  54. }
  55. case "GREEN", "GRE":
  56. if bg {
  57. result = append(result, 42)
  58. } else {
  59. result = append(result, 32)
  60. }
  61. case "BROWN", "BRO":
  62. if bg {
  63. result = append(result, 43)
  64. } else {
  65. result = append(result, 33)
  66. }
  67. case "YELLOW", "YEL":
  68. if bg {
  69. result = append(result, 43)
  70. } else {
  71. result = append(result, 33)
  72. }
  73. case "BLUE", "BLU":
  74. if bg {
  75. result = append(result, 44)
  76. } else {
  77. result = append(result, 34)
  78. }
  79. case "MAGENTA", "MAG":
  80. if bg {
  81. result = append(result, 45)
  82. } else {
  83. result = append(result, 35)
  84. }
  85. case "CYAN", "CYA":
  86. if bg {
  87. result = append(result, 46)
  88. } else {
  89. result = append(result, 36)
  90. }
  91. case "WHITE", "WHI":
  92. if bg {
  93. result = append(result, 47)
  94. } else {
  95. result = append(result, 37)
  96. }
  97. case "BOLD", "BOL", "BRIGHT", "BRI":
  98. result = append(result, 1)
  99. case "ON":
  100. bg = true
  101. case "BLINK", "BLI":
  102. result = append(result, 5)
  103. case "INVERT", "INVERSE", "INV":
  104. result = append(result, 7)
  105. default:
  106. fmt.Println("ColorText Unknown:", part)
  107. }
  108. }
  109. // fmt.Println("ColorText:", result)
  110. return Color(result...)
  111. }
  112. var Reset string = Color(0)
  113. var READFD int
  114. var WRITEFD int
  115. type DropfileConfig struct {
  116. comm_type int
  117. comm_handle int
  118. baudrate int
  119. BBSID string
  120. user_number int
  121. real_name string
  122. handle string
  123. security_level int
  124. time_left int
  125. emulation int
  126. node_number int
  127. }
  128. type Door struct {
  129. config DropfileConfig
  130. READFD int
  131. WRITEFD int
  132. }
  133. func (d *Door) ReadDropfile(filename string) {
  134. file, err := os.Open(filename)
  135. if err != nil {
  136. fmt.Printf("Open(%s): %s\n", filename, err)
  137. os.Exit(2)
  138. }
  139. defer file.Close()
  140. var lines []string
  141. // read line by line
  142. // The scanner handles DOS and linux file endings.
  143. scanner := bufio.NewScanner(file)
  144. for scanner.Scan() {
  145. line := scanner.Text()
  146. lines = append(lines, line)
  147. // fmt.Printf("[%s]\n", line)
  148. }
  149. d.config.comm_type, err = strconv.Atoi(lines[0])
  150. d.config.comm_handle, err = strconv.Atoi(lines[1])
  151. d.config.baudrate, err = strconv.Atoi(lines[2])
  152. d.config.BBSID = lines[3]
  153. d.config.user_number, err = strconv.Atoi(lines[4])
  154. d.config.real_name = lines[5]
  155. d.config.handle = lines[6]
  156. d.config.security_level, err = strconv.Atoi(lines[7])
  157. d.config.time_left, err = strconv.Atoi(lines[8])
  158. d.config.emulation, err = strconv.Atoi(lines[9])
  159. d.config.node_number, err = strconv.Atoi(lines[10])
  160. d.READFD = d.config.comm_handle
  161. //if d.READFD == 0 {
  162. // d.WRITEFD = 1
  163. //} else {
  164. d.WRITEFD = d.config.comm_handle
  165. //}
  166. }
  167. func (d *Door) HasKey() bool {
  168. var fdsetRead = syscall.FdSet{}
  169. clearAll(&fdsetRead)
  170. set(&fdsetRead, d.READFD)
  171. timeout := syscall.Timeval{Sec: 0, Usec: 1}
  172. v, _ := syscall.Select(d.READFD+1, &fdsetRead, nil, nil, &timeout)
  173. if v == -1 {
  174. return false
  175. }
  176. if v == 0 {
  177. return false
  178. }
  179. return true
  180. }
  181. func (d *Door) detect() {
  182. // if d.config.comm_handle == 0 {
  183. // d.Write("\377\375\042\377\373\001") // fix telnet client
  184. // }
  185. d.Write("\x1b[0;30;40m\x1b[2J\x1b[H") // black on black, clrscr, go home
  186. d.Write("\x03\x04\x1b[6n") // hearts and diamonds does CP437 work?
  187. d.Write("\n\r" + "\u2615\x1b[6n")
  188. d.Write("\x1b[999C\x1b[999B\x1b[6n" + Reset + "\x1b[2J\x1b[H") // goto end of screen + cursor pos
  189. // time.Sleep(50 * time.Millisecond)
  190. time.Sleep(250 * time.Millisecond)
  191. // read everything
  192. // telnet term isn't in RAW mode, so keys are buffer until <CR>
  193. if true { // d.HasKey() {
  194. buffer := make([]byte, 100)
  195. r, err := syscall.Read(d.READFD, buffer)
  196. results := string(buffer[:r])
  197. results = strings.Replace(results, "\x1b", "^", -1)
  198. fmt.Println("DETECT:", r, err, results)
  199. } else {
  200. // local telnet echos the reply :()
  201. fmt.Println("DETECT: Nothing received.")
  202. }
  203. }
  204. func tcgetattr(fd uintptr) (*unix.Termios, error) {
  205. return unix.IoctlGetTermios(int(fd), unix.TCGETS)
  206. }
  207. func cfmakeraw(attr *unix.Termios) {
  208. attr.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.ISTRIP | unix.PARMRK |
  209. unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON
  210. attr.Oflag &^= unix.OPOST
  211. attr.Cflag &^= unix.CSIZE | unix.PARENB
  212. attr.Cflag |= unix.CS8
  213. attr.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.IEXTEN | unix.ISIG
  214. attr.Cc[unix.VMIN] = 0
  215. attr.Cc[unix.VTIME] = 1
  216. /*
  217. attr.Iflag &^= unix.BRKINT | unix.INLCR | unix.ICRNL | unix.INPCK | unix.ISTRIP | unix.IXON
  218. attr.Oflag &^= unix.OPOST
  219. attr.Cflag &^= unix.CSIZE | unix.PARENB
  220. attr.Cflag |= unix.CS8
  221. attr.Lflag &^= unix.ECHO | unix.ICANON | unix.IEXTEN | unix.ISIG
  222. attr.Cc[unix.VMIN] = 1
  223. attr.Cc[unix.VTIME] = 0
  224. */
  225. }
  226. const (
  227. TCIFLUSH = 0
  228. TCOFLUSH = 1
  229. TCIOFLUSH = 2
  230. TCSANOW = 0
  231. TCSADRAIN = 1
  232. TCSAFLUSH = 2
  233. )
  234. // Tcsetattr sets the current serial port settings.
  235. func tcsetattr(fd, action uintptr, argp *unix.Termios) error {
  236. var request uintptr
  237. switch action {
  238. case TCSANOW:
  239. request = unix.TCSETS
  240. case TCSADRAIN:
  241. request = unix.TCSETSW
  242. case TCSAFLUSH:
  243. request = unix.TCSETSF
  244. default:
  245. return unix.EINVAL
  246. }
  247. return unix.IoctlSetTermios(int(fd), uint(request), argp)
  248. }
  249. func (d *Door) Init() {
  250. var dropfile string
  251. flag.StringVar(&dropfile, "d", "", "Path to dropfile")
  252. flag.Parse()
  253. if len(dropfile) == 0 {
  254. flag.PrintDefaults()
  255. os.Exit(2)
  256. }
  257. fmt.Printf("Loading: %s\n", dropfile)
  258. d.ReadDropfile(dropfile)
  259. fmt.Printf("BBS %s, User %s / Handle %s / File %d\n", d.config.BBSID, d.config.real_name, d.config.handle, d.READFD)
  260. // putting the linux terminal into raw mode ...
  261. // requires golang.org/x/sys/unix unix.Ioctlgetermios, etc.
  262. // I'm still getting terminal echo, terminal still not in raw mode (needs CR to submit)
  263. a, _ := tcgetattr(uintptr(d.READFD))
  264. cfmakeraw(a)
  265. _ = tcsetattr(uintptr(d.READFD), TCSANOW, a)
  266. // _ = tcsetattr(uintptr(d.READFD), TCSAFLUSH, a)
  267. d.detect()
  268. }
  269. func (d *Door) Write(output string) {
  270. buffer := []byte(output)
  271. n, err := syscall.Write(d.WRITEFD, buffer)
  272. if err != nil {
  273. fmt.Println("Write error/HANGUP?", n)
  274. }
  275. // No, this isn't it. The # of bytes in buffer == bytes written.
  276. if n != len(buffer) {
  277. fmt.Printf("Write fail: %d != %d\n", len(buffer), n)
  278. }
  279. }
  280. /*
  281. func write(output string, config *DropfileConfig) {
  282. buffer := []byte(output)
  283. n, err := syscall.Write(config.comm_handle, buffer)
  284. if err != nil {
  285. fmt.Println("Write error/HANGUP?", n)
  286. }
  287. }
  288. */
  289. // from: https://github.com/yubo/dea_ng
  290. // https://github.com/yubo/dea_ng/blob/master/go/src/directoryserver/streaming.go
  291. func set(fdSetPtr *syscall.FdSet, fd int) {
  292. (*fdSetPtr).Bits[fd/64] |= 1 << uint64(fd%64)
  293. }
  294. func isSet(fdSetPtr *syscall.FdSet, fd int) bool {
  295. return ((*fdSetPtr).Bits[fd/64] & (1 << uint64(fd%64))) != 0
  296. }
  297. func clearAll(fdSetPtr *syscall.FdSet) {
  298. for index, _ := range (*fdSetPtr).Bits {
  299. (*fdSetPtr).Bits[index] = 0
  300. }
  301. }
  302. func (d *Door) SleepKey(sleep int64) int {
  303. // var fdsetRead, fdsetWrite, fdsete syscall.FdSet
  304. var fdsetRead syscall.FdSet
  305. // fdsetWrite := syscall.FdSet
  306. clearAll(&fdsetRead)
  307. // clearAll(&fdsetWrite)
  308. // clearAll(&fdsete)
  309. set(&fdsetRead, d.READFD)
  310. // timeout := syscall.Timeval{Sec: 0, Usec: 100}
  311. timeout := syscall.Timeval{Sec: sleep, Usec: 0}
  312. v, err := syscall.Select(d.READFD+1, &fdsetRead, nil, nil, &timeout)
  313. if v == -1 {
  314. fmt.Println("-1 : ", err)
  315. // hangup ?!
  316. return -2
  317. }
  318. if v == 0 {
  319. // timeout
  320. return -1
  321. }
  322. // var buffer []byte -- 0 byte buffer. doh!
  323. buffer := make([]byte, 1)
  324. r, err := syscall.Read(d.READFD, buffer)
  325. if r != 1 {
  326. fmt.Printf("Read said ready, but didn't read a character %d %v.", r, err)
  327. // hangup
  328. return -2
  329. }
  330. return int(buffer[0])
  331. }
  332. func (d *Door) Getch() int {
  333. // var fdsetRead, fdsetWrite, fdsete syscall.FdSet
  334. var fdsetRead syscall.FdSet
  335. // fdsetWrite := syscall.FdSet
  336. clearAll(&fdsetRead)
  337. // clearAll(&fdsetWrite)
  338. // clearAll(&fdsete)
  339. set(&fdsetRead, d.READFD)
  340. // timeout := syscall.Timeval{Sec: 0, Usec: 100}
  341. timeout := syscall.Timeval{Sec: 120, Usec: 0}
  342. v, err := syscall.Select(d.READFD+1, &fdsetRead, nil, nil, &timeout)
  343. if v == -1 {
  344. fmt.Println("-1 : ", err)
  345. // hangup ?!
  346. return -2
  347. }
  348. if v == 0 {
  349. // timeout
  350. return -1
  351. }
  352. // var buffer []byte -- 0 byte buffer. doh!
  353. buffer := make([]byte, 1)
  354. r, err := syscall.Read(d.READFD, buffer)
  355. if r != 1 {
  356. fmt.Printf("Read said ready, but didn't read a character %d %v.", r, err)
  357. // hangup
  358. return -2
  359. }
  360. return int(buffer[0])
  361. }
  362. func sleep_key(config *DropfileConfig, secs int) int {
  363. // var fdsetRead, fdsetWrite, fdsete syscall.FdSet
  364. var fdsetRead = syscall.FdSet{}
  365. // fdsetWrite := syscall.FdSet
  366. clearAll(&fdsetRead)
  367. // clearAll(&fdsetWrite)
  368. // clearAll(&fdsete)
  369. set(&fdsetRead, config.comm_handle)
  370. timeout := syscall.Timeval{Sec: int64(secs), Usec: 0}
  371. // v, err := syscall.Select(config.comm_handle+1, &fdsetRead, &fdsetWrite, &fdsete, &timeout)
  372. v, err := syscall.Select(config.comm_handle+1, &fdsetRead, nil, nil, &timeout)
  373. fmt.Println("v:", v, "err:", err)
  374. if v == -1 {
  375. fmt.Println("-1 : ", err)
  376. // hangup ?!
  377. return -2
  378. }
  379. if v == 0 {
  380. // timeout
  381. return -1
  382. }
  383. // var buffer []byte
  384. buffer := make([]byte, 1)
  385. // var buffer [1]byte
  386. r, err := syscall.Read(config.comm_handle, buffer)
  387. if r != 1 {
  388. fmt.Printf("Read said ready, but didn't read a character %d %v ?\n", r, err)
  389. // hangup
  390. return -2
  391. }
  392. return int(buffer[0])
  393. }
  394. /*
  395. func main() {
  396. fmt.Println("doorgo")
  397. var dropfile string
  398. flag.StringVar(&dropfile, "dropfile", "", "Dropfile to use")
  399. flag.Parse()
  400. if len(dropfile) == 0 {
  401. flag.PrintDefaults()
  402. os.Exit(2)
  403. }
  404. fmt.Printf("Loading: %s\n", dropfile)
  405. var config DropfileConfig
  406. read_dropfile(dropfile, &config)
  407. fmt.Printf("BBS %s, User %s / Handle %s\n", config.BBSID, config.real_name, config.handle)
  408. message := "Welcome BBS User!\n\r"
  409. // buffer := []byte(message)
  410. // n, err := syscall.Write(config.comm_handle, buffer)
  411. write(message, &config)
  412. write("Press a key...", &config)
  413. key := sleep_key(&config, 20)
  414. write("\n\r", &config)
  415. message = fmt.Sprintf("Key %d / %x\n\r", key, key)
  416. write(message, &config)
  417. write("\n\rReturning to BBS.\n\r", &config)
  418. fmt.Println("Done.")
  419. // fmt.Println(n, err)
  420. }
  421. */