Simple telnet server for write.as http://nerds.write.as
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

221 line
4.7 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 bytes.IndexRune(b[0:n], '\n') > -1 {
  101. break
  102. }
  103. if err != nil || n == 0 {
  104. c.Close()
  105. break
  106. }
  107. }
  108. output(c, fmt.Sprintf("Enter anything you like.\nPress %sCtrl-D%s to publish and quit.\n%s\n", colBold, noCol, hr))
  109. readInput(c)
  110. }
  111. func checkExit(b []byte, n int) bool {
  112. return n > 0 && bytes.IndexRune(b[0:n], '\n') == -1
  113. }
  114. func readInput(c net.Conn) {
  115. defer c.Close()
  116. b := make([]byte, 4096)
  117. var post bytes.Buffer
  118. for {
  119. n, err := c.Read(b)
  120. post.Write(b[0:n])
  121. if debugging {
  122. fmt.Print(b[0:n])
  123. fmt.Printf("\n%d: %s\n", n, b[0:n])
  124. }
  125. if checkExit(b, n) {
  126. file, err := savePost(post.Bytes())
  127. if err != nil {
  128. fmt.Printf("There was an error saving: %s\n", err)
  129. output(c, "Something went terribly wrong, sorry. Try again later?\n\n")
  130. break
  131. }
  132. output(c, fmt.Sprintf("\n%s\nPosted to %shttp://nerds.write.as/%s%s\nPosting to secure site...", hr, colBlue, file, noCol))
  133. if rsyncHost != "" {
  134. exec.Command("rsync", "-ptgou", outDir + "/" + file, rsyncHost + ":").Run()
  135. output(c, fmt.Sprintf("\nPosted! View at %shttps://write.as/%s%s", colBlue, file, noCol))
  136. }
  137. output(c, "\nSee you later.\n\n")
  138. break
  139. }
  140. if err != nil || n == 0 {
  141. break
  142. }
  143. }
  144. }
  145. func savePost(post []byte) (string, error) {
  146. filename := generateFileName()
  147. f, err := os.Create(outDir + "/" + filename)
  148. defer f.Close()
  149. if err != nil {
  150. fmt.Println(err)
  151. }
  152. var decodedPost bytes.Buffer
  153. // Decode UTF-8
  154. for len(post) > 0 {
  155. r, size := utf8.DecodeRune(post)
  156. decodedPost.WriteRune(r)
  157. post = post[size:]
  158. }
  159. _, err = io.WriteString(f, stripCtlAndExtFromUTF8(string(decodedPost.Bytes())))
  160. return filename, err
  161. }
  162. func generateFileName() string {
  163. c := nameLen
  164. var dictionary string = "0123456789abcdefghijklmnopqrstuvwxyz"
  165. var bytes = make([]byte, c)
  166. rand.Read(bytes)
  167. for k, v := range bytes {
  168. bytes[k] = dictionary[v%byte(len(dictionary))]
  169. }
  170. return string(bytes)
  171. }
  172. func stripCtlAndExtFromUTF8(str string) string {
  173. return strings.Map(func(r rune) rune {
  174. if r == 10 || r == 13 || r >= 32 {
  175. return r
  176. }
  177. return -1
  178. }, str)
  179. }