door.go 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  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. var Reset string = Color(0)
  28. var READFD int
  29. var WRITEFD int
  30. type DropfileConfig struct {
  31. comm_type int
  32. comm_handle int
  33. baudrate int
  34. BBSID string
  35. user_number int
  36. real_name string
  37. handle string
  38. security_level int
  39. time_left int
  40. emulation int
  41. node_number int
  42. }
  43. type Door struct {
  44. config DropfileConfig
  45. READFD int
  46. WRITEFD int
  47. }
  48. func (d *Door) ReadDropfile(filename string) {
  49. file, err := os.Open(filename)
  50. if err != nil {
  51. fmt.Printf("Open(%s): %s\n", filename, err)
  52. os.Exit(2)
  53. }
  54. defer file.Close()
  55. var lines []string
  56. // read line by line
  57. // The scanner handles DOS and linux file endings.
  58. scanner := bufio.NewScanner(file)
  59. for scanner.Scan() {
  60. line := scanner.Text()
  61. lines = append(lines, line)
  62. // fmt.Printf("[%s]\n", line)
  63. }
  64. d.config.comm_type, err = strconv.Atoi(lines[0])
  65. d.config.comm_handle, err = strconv.Atoi(lines[1])
  66. d.config.baudrate, err = strconv.Atoi(lines[2])
  67. d.config.BBSID = lines[3]
  68. d.config.user_number, err = strconv.Atoi(lines[4])
  69. d.config.real_name = lines[5]
  70. d.config.handle = lines[6]
  71. d.config.security_level, err = strconv.Atoi(lines[7])
  72. d.config.time_left, err = strconv.Atoi(lines[8])
  73. d.config.emulation, err = strconv.Atoi(lines[9])
  74. d.config.node_number, err = strconv.Atoi(lines[10])
  75. d.READFD = d.config.comm_handle
  76. //if d.READFD == 0 {
  77. // d.WRITEFD = 1
  78. //} else {
  79. d.WRITEFD = d.config.comm_handle
  80. //}
  81. }
  82. func (d *Door) HasKey() bool {
  83. var fdsetRead = syscall.FdSet{}
  84. clearAll(&fdsetRead)
  85. set(&fdsetRead, d.READFD)
  86. timeout := syscall.Timeval{Sec: 0, Usec: 1}
  87. v, _ := syscall.Select(d.READFD+1, &fdsetRead, nil, nil, &timeout)
  88. if v == -1 {
  89. return false
  90. }
  91. if v == 0 {
  92. return false
  93. }
  94. return true
  95. }
  96. var Unicode bool
  97. var CP437 bool
  98. var Full_CP437 bool
  99. var Height int
  100. var Width int
  101. func (d *Door) detect() {
  102. // if d.config.comm_handle == 0 {
  103. // d.Write("\377\375\042\377\373\001") // fix telnet client
  104. // }
  105. d.Write("\x1b[0;30;40m\x1b[2J\x1b[H") // black on black, clrscr, go home
  106. d.Write("\x03\x04\x1b[6n") // hearts and diamonds does CP437 work?
  107. d.Write(CRNL + "\u2615\x1b[6n")
  108. d.Write("\x1b[999C\x1b[999B\x1b[6n" + Reset + "\x1b[2J\x1b[H") // goto end of screen + cursor pos
  109. // time.Sleep(50 * time.Millisecond)
  110. time.Sleep(250 * time.Millisecond)
  111. // read everything
  112. // telnet term isn't in RAW mode, so keys are buffer until <CR>
  113. var results string
  114. if true { // d.HasKey() {
  115. buffer := make([]byte, 100)
  116. r, err := syscall.Read(d.READFD, buffer)
  117. results = string(buffer[:r])
  118. output := strings.Replace(results, "\x1b", "^[", -1)
  119. fmt.Println("DETECT:", r, err, output)
  120. } else {
  121. // local telnet echos the reply :()
  122. fmt.Println("DETECT: Nothing received.")
  123. return
  124. }
  125. if ((strings.Index(results, "1;1R") != -1) ||
  126. (strings.Index(results, "1;3R") != -1)) &&
  127. ((strings.Index(results, "2:2R") != -1) ||
  128. (strings.Index(results, "2;3R") != -1)) {
  129. Unicode = true
  130. } else {
  131. Unicode = false
  132. CP437 = true
  133. }
  134. if strings.Index(results, "1;3R") != -1 {
  135. Full_CP437 = true
  136. }
  137. // get screen size
  138. var err error
  139. pos := strings.LastIndex(results, "\x1b")
  140. if pos != -1 {
  141. pos++
  142. if results[pos] == '[' {
  143. pos++
  144. results = results[pos:]
  145. pos = strings.Index(results, ";")
  146. if pos != -1 {
  147. height := results[:pos]
  148. // Height, err = strconv.Atoi(results)
  149. Height, err = strconv.Atoi(height)
  150. pos++
  151. results = results[pos:]
  152. pos = strings.Index(results, "R")
  153. if pos != -1 {
  154. width := results[:pos]
  155. Width, err = strconv.Atoi(width)
  156. fmt.Printf("Width: %s, %d, %v\n", results, Width, err)
  157. }
  158. } else {
  159. Height = 0
  160. Width = 0
  161. }
  162. }
  163. }
  164. fmt.Printf("Unicode %v Screen: %d X %d\n", Unicode, Width, Height)
  165. }
  166. func (d *Door) Init() {
  167. var dropfile string
  168. flag.StringVar(&dropfile, "d", "", "Path to dropfile")
  169. flag.Parse()
  170. if len(dropfile) == 0 {
  171. flag.PrintDefaults()
  172. os.Exit(2)
  173. }
  174. fmt.Printf("Loading: %s\n", dropfile)
  175. d.ReadDropfile(dropfile)
  176. fmt.Printf("BBS %s, User %s / Handle %s / File %d\n", d.config.BBSID, d.config.real_name, d.config.handle, d.READFD)
  177. d.detect()
  178. }
  179. func (d *Door) Write(output string) {
  180. buffer := []byte(output)
  181. n, err := syscall.Write(d.WRITEFD, buffer)
  182. if err != nil {
  183. fmt.Println("Write error/HANGUP?", n)
  184. }
  185. // No, this isn't it. The # of bytes in buffer == bytes written.
  186. if n != len(buffer) {
  187. fmt.Printf("Write fail: %d != %d\n", len(buffer), n)
  188. }
  189. }
  190. /*
  191. func write(output string, config *DropfileConfig) {
  192. buffer := []byte(output)
  193. n, err := syscall.Write(config.comm_handle, buffer)
  194. if err != nil {
  195. fmt.Println("Write error/HANGUP?", n)
  196. }
  197. }
  198. */
  199. // from: https://github.com/yubo/dea_ng
  200. // https://github.com/yubo/dea_ng/blob/master/go/src/directoryserver/streaming.go
  201. func set(fdSetPtr *syscall.FdSet, fd int) {
  202. (*fdSetPtr).Bits[fd/64] |= 1 << uint64(fd%64)
  203. }
  204. func isSet(fdSetPtr *syscall.FdSet, fd int) bool {
  205. return ((*fdSetPtr).Bits[fd/64] & (1 << uint64(fd%64))) != 0
  206. }
  207. func clearAll(fdSetPtr *syscall.FdSet) {
  208. for index, _ := range (*fdSetPtr).Bits {
  209. (*fdSetPtr).Bits[index] = 0
  210. }
  211. }
  212. func (d *Door) SleepKey(sleep int64) int {
  213. // var fdsetRead, fdsetWrite, fdsete syscall.FdSet
  214. var fdsetRead syscall.FdSet
  215. // fdsetWrite := syscall.FdSet
  216. clearAll(&fdsetRead)
  217. // clearAll(&fdsetWrite)
  218. // clearAll(&fdsete)
  219. set(&fdsetRead, d.READFD)
  220. // timeout := syscall.Timeval{Sec: 0, Usec: 100}
  221. timeout := syscall.Timeval{Sec: sleep, Usec: 0}
  222. v, err := syscall.Select(d.READFD+1, &fdsetRead, nil, nil, &timeout)
  223. if v == -1 {
  224. fmt.Println("-1 : ", err)
  225. // hangup ?!
  226. return -2
  227. }
  228. if v == 0 {
  229. // timeout
  230. return -1
  231. }
  232. // var buffer []byte -- 0 byte buffer. doh!
  233. buffer := make([]byte, 1)
  234. r, err := syscall.Read(d.READFD, buffer)
  235. if r != 1 {
  236. fmt.Printf("Read said ready, but didn't read a character %d %v.", r, err)
  237. // hangup
  238. return -2
  239. }
  240. return int(buffer[0])
  241. }
  242. func (d *Door) Getch() int {
  243. // var fdsetRead, fdsetWrite, fdsete syscall.FdSet
  244. var fdsetRead syscall.FdSet
  245. // fdsetWrite := syscall.FdSet
  246. clearAll(&fdsetRead)
  247. // clearAll(&fdsetWrite)
  248. // clearAll(&fdsete)
  249. set(&fdsetRead, d.READFD)
  250. // timeout := syscall.Timeval{Sec: 0, Usec: 100}
  251. timeout := syscall.Timeval{Sec: 120, Usec: 0}
  252. v, err := syscall.Select(d.READFD+1, &fdsetRead, nil, nil, &timeout)
  253. if v == -1 {
  254. fmt.Println("-1 : ", err)
  255. // hangup ?!
  256. return -2
  257. }
  258. if v == 0 {
  259. // timeout
  260. return -1
  261. }
  262. // var buffer []byte -- 0 byte buffer. doh!
  263. buffer := make([]byte, 1)
  264. r, err := syscall.Read(d.READFD, buffer)
  265. if r != 1 {
  266. fmt.Printf("Read said ready, but didn't read a character %d %v.", r, err)
  267. // hangup
  268. return -2
  269. }
  270. return int(buffer[0])
  271. }
  272. /*
  273. func sleep_key(config *DropfileConfig, secs int) int {
  274. // var fdsetRead, fdsetWrite, fdsete syscall.FdSet
  275. var fdsetRead = syscall.FdSet{}
  276. // fdsetWrite := syscall.FdSet
  277. clearAll(&fdsetRead)
  278. // clearAll(&fdsetWrite)
  279. // clearAll(&fdsete)
  280. set(&fdsetRead, config.comm_handle)
  281. timeout := syscall.Timeval{Sec: int64(secs), Usec: 0}
  282. // v, err := syscall.Select(config.comm_handle+1, &fdsetRead, &fdsetWrite, &fdsete, &timeout)
  283. v, err := syscall.Select(config.comm_handle+1, &fdsetRead, nil, nil, &timeout)
  284. fmt.Println("v:", v, "err:", err)
  285. if v == -1 {
  286. fmt.Println("-1 : ", err)
  287. // hangup ?!
  288. return -2
  289. }
  290. if v == 0 {
  291. // timeout
  292. return -1
  293. }
  294. // var buffer []byte
  295. buffer := make([]byte, 1)
  296. // var buffer [1]byte
  297. r, err := syscall.Read(config.comm_handle, buffer)
  298. if r != 1 {
  299. fmt.Printf("Read said ready, but didn't read a character %d %v ?\n", r, err)
  300. // hangup
  301. return -2
  302. }
  303. return int(buffer[0])
  304. }
  305. */
  306. /*
  307. func main() {
  308. fmt.Println("doorgo")
  309. var dropfile string
  310. flag.StringVar(&dropfile, "dropfile", "", "Dropfile to use")
  311. flag.Parse()
  312. if len(dropfile) == 0 {
  313. flag.PrintDefaults()
  314. os.Exit(2)
  315. }
  316. fmt.Printf("Loading: %s\n", dropfile)
  317. var config DropfileConfig
  318. read_dropfile(dropfile, &config)
  319. fmt.Printf("BBS %s, User %s / Handle %s\n", config.BBSID, config.real_name, config.handle)
  320. message := "Welcome BBS User!\n\r"
  321. // buffer := []byte(message)
  322. // n, err := syscall.Write(config.comm_handle, buffer)
  323. write(message, &config)
  324. write("Press a key...", &config)
  325. key := sleep_key(&config, 20)
  326. write("\n\r", &config)
  327. message = fmt.Sprintf("Key %d / %x\n\r", key, key)
  328. write(message, &config)
  329. write("\n\rReturning to BBS.\n\r", &config)
  330. fmt.Println("Done.")
  331. // fmt.Println(n, err)
  332. }
  333. */