Simple telnet server for write.as http://nerds.write.as
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

227 lines
4.8 KiB

  1. package main
  2. import (
  3. "fmt"
  4. "net"
  5. "bytes"
  6. "io/ioutil"
  7. "crypto/rand"
  8. "io"
  9. "os"
  10. "os/exec"
  11. "strings"
  12. "flag"
  13. "unicode/utf8"
  14. )
  15. var (
  16. banner []byte
  17. outDir string
  18. staticDir string
  19. debugging bool
  20. rsyncHost string
  21. )
  22. const (
  23. colBlue = "\033[0;34m"
  24. colGreen = "\033[0;32m"
  25. colBGreen = "\033[1;32m"
  26. colCyan = "\033[0;36m"
  27. colBRed = "\033[1;31m"
  28. colBold = "\033[1;37m"
  29. noCol = "\033[0m"
  30. nameLen = 12
  31. hr = "————————————————————————————————————————————————————————————————————————————————"
  32. )
  33. func main() {
  34. // Get any arguments
  35. outDirPtr := flag.String("o", "/var/write", "Directory where text files will be stored.")
  36. staticDirPtr := flag.String("s", ".", "Directory where required static files exist.")
  37. rsyncHostPtr := flag.String("h", "", "Hostname of the server to rsync saved files to.")
  38. portPtr := flag.Int("p", 2323, "Port to listen on.")
  39. debugPtr := flag.Bool("debug", false, "Enables garrulous debug logging.")
  40. flag.Parse()
  41. outDir = *outDirPtr
  42. staticDir = *staticDirPtr
  43. rsyncHost = *rsyncHostPtr
  44. debugging = *debugPtr
  45. fmt.Print("\nCONFIG:\n")
  46. fmt.Printf("Output directory : %s\n", outDir)
  47. fmt.Printf("Static directory : %s\n", staticDir)
  48. fmt.Printf("rsync host : %s\n", rsyncHost)
  49. fmt.Printf("Debugging enabled : %t\n\n", debugging)
  50. fmt.Print("Initializing...")
  51. var err error
  52. banner, err = ioutil.ReadFile(staticDir + "/banner.txt")
  53. if err != nil {
  54. fmt.Println(err)
  55. }
  56. fmt.Println("DONE")
  57. ln, err := net.Listen("tcp", fmt.Sprintf(":%d", *portPtr))
  58. if err != nil {
  59. panic(err)
  60. }
  61. fmt.Printf("Listening on localhost:%d\n", *portPtr)
  62. for {
  63. conn, err := ln.Accept()
  64. if err != nil {
  65. fmt.Println(err)
  66. continue
  67. }
  68. go handleConnection(conn)
  69. }
  70. }
  71. func output(c net.Conn, m string) bool {
  72. _, err := c.Write([]byte(m))
  73. if err != nil {
  74. c.Close()
  75. return false
  76. }
  77. return true
  78. }
  79. func outputBytes(c net.Conn, m []byte) bool {
  80. _, err := c.Write(m)
  81. if err != nil {
  82. c.Close()
  83. return false
  84. }
  85. return true
  86. }
  87. func handleConnection(c net.Conn) {
  88. outputBytes(c, banner)
  89. output(c, fmt.Sprintf("\n%sWelcome to write.as!%s\n", colBGreen, noCol))
  90. output(c, fmt.Sprintf("If this is freaking you out, you can get notified of the %sbrowser-based%s launch\ninstead at https://write.as.\n\n", colBold, noCol))
  91. waitForEnter(c)
  92. c.Close()
  93. fmt.Printf("Connection from %v closed.\n", c.RemoteAddr())
  94. }
  95. func waitForEnter(c net.Conn) {
  96. b := make([]byte, 4)
  97. output(c, fmt.Sprintf("%sPress Enter to continue...%s\n", colBRed, noCol))
  98. for {
  99. n, err := c.Read(b)
  100. if debugging {
  101. fmt.Print(b[0:n])
  102. fmt.Printf("\n%d: %s\n", n, b[0:n])
  103. }
  104. if bytes.IndexRune(b[0:n], '\n') > -1 {
  105. break
  106. }
  107. if err != nil || n == 0 {
  108. c.Close()
  109. break
  110. }
  111. }
  112. output(c, fmt.Sprintf("Enter anything you like.\nPress %sCtrl-D%s to publish and quit.\n%s\n", colBold, noCol, hr))
  113. readInput(c)
  114. }
  115. func checkExit(b []byte, n int) bool {
  116. return n > 0 && bytes.IndexRune(b[0:n], '\n') == -1
  117. }
  118. func readInput(c net.Conn) {
  119. defer c.Close()
  120. b := make([]byte, 4096)
  121. var post bytes.Buffer
  122. for {
  123. n, err := c.Read(b)
  124. post.Write(b[0:n])
  125. if debugging {
  126. fmt.Print(b[0:n])
  127. fmt.Printf("\n%d: %s\n", n, b[0:n])
  128. }
  129. if checkExit(b, n) {
  130. file, err := savePost(post.Bytes())
  131. if err != nil {
  132. fmt.Printf("There was an error saving: %s\n", err)
  133. output(c, "Something went terribly wrong, sorry. Try again later?\n\n")
  134. break
  135. }
  136. output(c, fmt.Sprintf("\n%s\nPosted to %shttp://nerds.write.as/%s%s\nPosting to secure site...", hr, colBlue, file, noCol))
  137. if rsyncHost != "" {
  138. exec.Command("rsync", "-ptgou", outDir + "/" + file, rsyncHost + ":").Run()
  139. output(c, fmt.Sprintf("\nPosted! View at %shttps://write.as/%s%s", colBlue, file, noCol))
  140. }
  141. output(c, "\nSee you later.\n\n")
  142. break
  143. }
  144. if err != nil || n == 0 {
  145. break
  146. }
  147. }
  148. }
  149. func savePost(post []byte) (string, error) {
  150. filename := generateFileName()
  151. f, err := os.Create(outDir + "/" + filename)
  152. defer f.Close()
  153. if err != nil {
  154. fmt.Println(err)
  155. }
  156. var decodedPost bytes.Buffer
  157. // Decode UTF-8
  158. for len(post) > 0 {
  159. r, size := utf8.DecodeRune(post)
  160. decodedPost.WriteRune(r)
  161. post = post[size:]
  162. }
  163. _, err = io.WriteString(f, stripCtlAndExtFromUTF8(string(decodedPost.Bytes())))
  164. return filename, err
  165. }
  166. func generateFileName() string {
  167. c := nameLen
  168. var dictionary string = "0123456789abcdefghijklmnopqrstuvwxyz"
  169. var bytes = make([]byte, c)
  170. rand.Read(bytes)
  171. for k, v := range bytes {
  172. bytes[k] = dictionary[v%byte(len(dictionary))]
  173. }
  174. return string(bytes)
  175. }
  176. func stripCtlAndExtFromUTF8(str string) string {
  177. return strings.Map(func(r rune) rune {
  178. if r == 10 || r == 13 || r >= 32 {
  179. return r
  180. }
  181. return -1
  182. }, str)
  183. }