door.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. package door
  2. import (
  3. "bufio"
  4. "flag"
  5. "fmt"
  6. "os"
  7. "strconv"
  8. "strings"
  9. "syscall"
  10. "time"
  11. )
  12. const CRNL = "\r\n"
  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 = result[:len(result)-1]
  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. type BoxStyle struct {
  114. top_left string
  115. top_right string
  116. top string
  117. side string
  118. bottom_left string
  119. bottom_right string
  120. middle_left string
  121. middle_right string
  122. }
  123. var boxes = [4]BoxStyle{
  124. /*
  125. ┌──┐
  126. │ │
  127. ├──┤
  128. └──┘
  129. */
  130. BoxStyle{"\xda", "\xbf", "\xc4", "\xb3", "\xc0", "\xd9", "\xc3", "\xb4"},
  131. /*
  132. ╔══╗
  133. ║ ║
  134. ╠══╣
  135. ╚══╝
  136. */
  137. BoxStyle{"\xc9", "\xbb", "\xcd", "\xba", "\xc8", "\xbc", "\xcc", "\xb9"},
  138. /*
  139. ╒══╕
  140. │ │
  141. ╞══╡
  142. ╘══╛
  143. */
  144. BoxStyle{"\xd6", "\xb8", "\xcd", "\xb3", "\xd4", "\xbe", "\xc6", "\xb5"},
  145. /*
  146. ╓──╖
  147. ║ ║
  148. ╟──╢
  149. ╙──╜
  150. */
  151. BoxStyle{"\xd6", "\xb7", "\xc4", "\xba", "\xd3", "\xbd", "\xc7", "\xb6"}}
  152. var unicode_boxes = []BoxStyle{
  153. BoxStyle{"\u250c", "\u2510", "\u2500", "\u2502", "\u2514", "\u2518", "\u251c", "\u2524"},
  154. BoxStyle{"\u2554", "\u2557", "\u2550", "\u2551", "\u255a", "\u255d", "\u2560", "\u2563"},
  155. BoxStyle{"\u2553", "\u2556", "\u2500", "\u2551", "\u2559", "\u255c", "\u255f", "\u2562"},
  156. BoxStyle{"\u2552", "\u2555", "\u2550", "\u2502", "\u2558", "\u255b", "\u255e", "\u2561"},
  157. }
  158. type Box struct {
  159. Width int
  160. Style int
  161. }
  162. func (b *Box) Top() string {
  163. var style *BoxStyle
  164. if Unicode {
  165. style = &unicode_boxes[b.Style]
  166. } else {
  167. style = &boxes[b.Style]
  168. }
  169. return style.top_left + strings.Repeat(style.top, b.Width) + style.top_right
  170. }
  171. func (b *Box) Middle(text string) string {
  172. var style *BoxStyle
  173. if Unicode {
  174. style = &unicode_boxes[b.Style]
  175. } else {
  176. style = &boxes[b.Style]
  177. }
  178. return style.side + text + style.side
  179. }
  180. func (b *Box) Bottom() string {
  181. var style *BoxStyle
  182. if Unicode {
  183. style = &unicode_boxes[b.Style]
  184. } else {
  185. style = &boxes[b.Style]
  186. }
  187. return style.bottom_left + strings.Repeat(style.top, b.Width) + style.bottom_right
  188. }
  189. var Reset string = Color(0)
  190. var READFD int
  191. var WRITEFD int
  192. type DropfileConfig struct {
  193. comm_type int
  194. comm_handle int
  195. baudrate int
  196. BBSID string
  197. user_number int
  198. real_name string
  199. handle string
  200. security_level int
  201. time_left int
  202. emulation int
  203. node_number int
  204. }
  205. type Door struct {
  206. config DropfileConfig
  207. READFD int
  208. WRITEFD int
  209. }
  210. func (d *Door) ReadDropfile(filename string) {
  211. file, err := os.Open(filename)
  212. if err != nil {
  213. fmt.Printf("Open(%s): %s\n", filename, err)
  214. os.Exit(2)
  215. }
  216. defer file.Close()
  217. var lines []string
  218. // read line by line
  219. // The scanner handles DOS and linux file endings.
  220. scanner := bufio.NewScanner(file)
  221. for scanner.Scan() {
  222. line := scanner.Text()
  223. lines = append(lines, line)
  224. // fmt.Printf("[%s]\n", line)
  225. }
  226. d.config.comm_type, err = strconv.Atoi(lines[0])
  227. d.config.comm_handle, err = strconv.Atoi(lines[1])
  228. d.config.baudrate, err = strconv.Atoi(lines[2])
  229. d.config.BBSID = lines[3]
  230. d.config.user_number, err = strconv.Atoi(lines[4])
  231. d.config.real_name = lines[5]
  232. d.config.handle = lines[6]
  233. d.config.security_level, err = strconv.Atoi(lines[7])
  234. d.config.time_left, err = strconv.Atoi(lines[8])
  235. d.config.emulation, err = strconv.Atoi(lines[9])
  236. d.config.node_number, err = strconv.Atoi(lines[10])
  237. d.READFD = d.config.comm_handle
  238. //if d.READFD == 0 {
  239. // d.WRITEFD = 1
  240. //} else {
  241. d.WRITEFD = d.config.comm_handle
  242. //}
  243. }
  244. func (d *Door) HasKey() bool {
  245. var fdsetRead = syscall.FdSet{}
  246. clearAll(&fdsetRead)
  247. set(&fdsetRead, d.READFD)
  248. timeout := syscall.Timeval{Sec: 0, Usec: 1}
  249. v, _ := syscall.Select(d.READFD+1, &fdsetRead, nil, nil, &timeout)
  250. if v == -1 {
  251. return false
  252. }
  253. if v == 0 {
  254. return false
  255. }
  256. return true
  257. }
  258. var Unicode bool
  259. var CP437 bool
  260. var Full_CP437 bool
  261. var Height int
  262. var Width int
  263. func (d *Door) detect() {
  264. // if d.config.comm_handle == 0 {
  265. // d.Write("\377\375\042\377\373\001") // fix telnet client
  266. // }
  267. d.Write("\x1b[0;30;40m\x1b[2J\x1b[H") // black on black, clrscr, go home
  268. d.Write("\x03\x04\x1b[6n") // hearts and diamonds does CP437 work?
  269. d.Write(CRNL + "\u2615\x1b[6n")
  270. d.Write("\x1b[999C\x1b[999B\x1b[6n" + Reset + "\x1b[2J\x1b[H") // goto end of screen + cursor pos
  271. // time.Sleep(50 * time.Millisecond)
  272. time.Sleep(250 * time.Millisecond)
  273. // read everything
  274. // telnet term isn't in RAW mode, so keys are buffer until <CR>
  275. var results string
  276. if true { // d.HasKey() {
  277. buffer := make([]byte, 100)
  278. r, err := syscall.Read(d.READFD, buffer)
  279. results = string(buffer[:r])
  280. output := strings.Replace(results, "\x1b", "^[", -1)
  281. fmt.Println("DETECT:", r, err, output)
  282. } else {
  283. // local telnet echos the reply :()
  284. fmt.Println("DETECT: Nothing received.")
  285. return
  286. }
  287. if ((strings.Index(results, "1;1R") != -1) ||
  288. (strings.Index(results, "1;3R") != -1)) &&
  289. ((strings.Index(results, "2:2R") != -1) ||
  290. (strings.Index(results, "2;3R") != -1)) {
  291. Unicode = true
  292. } else {
  293. Unicode = false
  294. CP437 = true
  295. }
  296. if strings.Index(results, "1;3R") != -1 {
  297. Full_CP437 = true
  298. }
  299. // get screen size
  300. var err error
  301. pos := strings.LastIndex(results, "\x1b")
  302. if pos != -1 {
  303. pos++
  304. if results[pos] == '[' {
  305. pos++
  306. results = results[pos:]
  307. pos = strings.Index(results, ";")
  308. if pos != -1 {
  309. height := results[:pos]
  310. // Height, err = strconv.Atoi(results)
  311. Height, err = strconv.Atoi(height)
  312. pos++
  313. results = results[pos:]
  314. pos = strings.Index(results, "R")
  315. if pos != -1 {
  316. width := results[:pos]
  317. Width, err = strconv.Atoi(width)
  318. fmt.Printf("Width: %s, %d, %v\n", results, Width, err)
  319. }
  320. } else {
  321. Height = 0
  322. Width = 0
  323. }
  324. }
  325. }
  326. fmt.Printf("Unicode %v Screen: %d X %d\n", Unicode, Width, Height)
  327. }
  328. func (d *Door) Init() {
  329. var dropfile string
  330. flag.StringVar(&dropfile, "d", "", "Path to dropfile")
  331. flag.Parse()
  332. if len(dropfile) == 0 {
  333. flag.PrintDefaults()
  334. os.Exit(2)
  335. }
  336. fmt.Printf("Loading: %s\n", dropfile)
  337. d.ReadDropfile(dropfile)
  338. fmt.Printf("BBS %s, User %s / Handle %s / File %d\n", d.config.BBSID, d.config.real_name, d.config.handle, d.READFD)
  339. d.detect()
  340. }
  341. func (d *Door) Write(output string) {
  342. buffer := []byte(output)
  343. n, err := syscall.Write(d.WRITEFD, buffer)
  344. if err != nil {
  345. fmt.Println("Write error/HANGUP?", n)
  346. }
  347. // No, this isn't it. The # of bytes in buffer == bytes written.
  348. if n != len(buffer) {
  349. fmt.Printf("Write fail: %d != %d\n", len(buffer), n)
  350. }
  351. }
  352. /*
  353. func write(output string, config *DropfileConfig) {
  354. buffer := []byte(output)
  355. n, err := syscall.Write(config.comm_handle, buffer)
  356. if err != nil {
  357. fmt.Println("Write error/HANGUP?", n)
  358. }
  359. }
  360. */
  361. // from: https://github.com/yubo/dea_ng
  362. // https://github.com/yubo/dea_ng/blob/master/go/src/directoryserver/streaming.go
  363. func set(fdSetPtr *syscall.FdSet, fd int) {
  364. (*fdSetPtr).Bits[fd/64] |= 1 << uint64(fd%64)
  365. }
  366. func isSet(fdSetPtr *syscall.FdSet, fd int) bool {
  367. return ((*fdSetPtr).Bits[fd/64] & (1 << uint64(fd%64))) != 0
  368. }
  369. func clearAll(fdSetPtr *syscall.FdSet) {
  370. for index, _ := range (*fdSetPtr).Bits {
  371. (*fdSetPtr).Bits[index] = 0
  372. }
  373. }
  374. func (d *Door) SleepKey(sleep int64) int {
  375. // var fdsetRead, fdsetWrite, fdsete syscall.FdSet
  376. var fdsetRead syscall.FdSet
  377. // fdsetWrite := syscall.FdSet
  378. clearAll(&fdsetRead)
  379. // clearAll(&fdsetWrite)
  380. // clearAll(&fdsete)
  381. set(&fdsetRead, d.READFD)
  382. // timeout := syscall.Timeval{Sec: 0, Usec: 100}
  383. timeout := syscall.Timeval{Sec: sleep, Usec: 0}
  384. v, err := syscall.Select(d.READFD+1, &fdsetRead, nil, nil, &timeout)
  385. if v == -1 {
  386. fmt.Println("-1 : ", err)
  387. // hangup ?!
  388. return -2
  389. }
  390. if v == 0 {
  391. // timeout
  392. return -1
  393. }
  394. // var buffer []byte -- 0 byte buffer. doh!
  395. buffer := make([]byte, 1)
  396. r, err := syscall.Read(d.READFD, buffer)
  397. if r != 1 {
  398. fmt.Printf("Read said ready, but didn't read a character %d %v.", r, err)
  399. // hangup
  400. return -2
  401. }
  402. return int(buffer[0])
  403. }
  404. func (d *Door) Getch() int {
  405. // var fdsetRead, fdsetWrite, fdsete syscall.FdSet
  406. var fdsetRead syscall.FdSet
  407. // fdsetWrite := syscall.FdSet
  408. clearAll(&fdsetRead)
  409. // clearAll(&fdsetWrite)
  410. // clearAll(&fdsete)
  411. set(&fdsetRead, d.READFD)
  412. // timeout := syscall.Timeval{Sec: 0, Usec: 100}
  413. timeout := syscall.Timeval{Sec: 120, Usec: 0}
  414. v, err := syscall.Select(d.READFD+1, &fdsetRead, nil, nil, &timeout)
  415. if v == -1 {
  416. fmt.Println("-1 : ", err)
  417. // hangup ?!
  418. return -2
  419. }
  420. if v == 0 {
  421. // timeout
  422. return -1
  423. }
  424. // var buffer []byte -- 0 byte buffer. doh!
  425. buffer := make([]byte, 1)
  426. r, err := syscall.Read(d.READFD, buffer)
  427. if r != 1 {
  428. fmt.Printf("Read said ready, but didn't read a character %d %v.", r, err)
  429. // hangup
  430. return -2
  431. }
  432. return int(buffer[0])
  433. }
  434. /*
  435. func sleep_key(config *DropfileConfig, secs int) int {
  436. // var fdsetRead, fdsetWrite, fdsete syscall.FdSet
  437. var fdsetRead = syscall.FdSet{}
  438. // fdsetWrite := syscall.FdSet
  439. clearAll(&fdsetRead)
  440. // clearAll(&fdsetWrite)
  441. // clearAll(&fdsete)
  442. set(&fdsetRead, config.comm_handle)
  443. timeout := syscall.Timeval{Sec: int64(secs), Usec: 0}
  444. // v, err := syscall.Select(config.comm_handle+1, &fdsetRead, &fdsetWrite, &fdsete, &timeout)
  445. v, err := syscall.Select(config.comm_handle+1, &fdsetRead, nil, nil, &timeout)
  446. fmt.Println("v:", v, "err:", err)
  447. if v == -1 {
  448. fmt.Println("-1 : ", err)
  449. // hangup ?!
  450. return -2
  451. }
  452. if v == 0 {
  453. // timeout
  454. return -1
  455. }
  456. // var buffer []byte
  457. buffer := make([]byte, 1)
  458. // var buffer [1]byte
  459. r, err := syscall.Read(config.comm_handle, buffer)
  460. if r != 1 {
  461. fmt.Printf("Read said ready, but didn't read a character %d %v ?\n", r, err)
  462. // hangup
  463. return -2
  464. }
  465. return int(buffer[0])
  466. }
  467. */
  468. /*
  469. func main() {
  470. fmt.Println("doorgo")
  471. var dropfile string
  472. flag.StringVar(&dropfile, "dropfile", "", "Dropfile to use")
  473. flag.Parse()
  474. if len(dropfile) == 0 {
  475. flag.PrintDefaults()
  476. os.Exit(2)
  477. }
  478. fmt.Printf("Loading: %s\n", dropfile)
  479. var config DropfileConfig
  480. read_dropfile(dropfile, &config)
  481. fmt.Printf("BBS %s, User %s / Handle %s\n", config.BBSID, config.real_name, config.handle)
  482. message := "Welcome BBS User!\n\r"
  483. // buffer := []byte(message)
  484. // n, err := syscall.Write(config.comm_handle, buffer)
  485. write(message, &config)
  486. write("Press a key...", &config)
  487. key := sleep_key(&config, 20)
  488. write("\n\r", &config)
  489. message = fmt.Sprintf("Key %d / %x\n\r", key, key)
  490. write(message, &config)
  491. write("\n\rReturning to BBS.\n\r", &config)
  492. fmt.Println("Done.")
  493. // fmt.Println(n, err)
  494. }
  495. */