door.go 11 KB

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