exchange.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. // Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved.
  2. // Use of this source code is governed by the MIT-license that can be
  3. // found in the LICENSE file.
  4. package main
  5. import (
  6. "fmt"
  7. "log"
  8. "net"
  9. "os"
  10. "os/signal"
  11. "strings"
  12. "time"
  13. "github.com/la5nta/wl2k-go/fbb"
  14. )
  15. type ex struct {
  16. conn net.Conn
  17. target string
  18. master bool
  19. errors chan error
  20. }
  21. func exchangeLoop() (ce chan ex) {
  22. ce = make(chan ex)
  23. go func() {
  24. for ex := range ce {
  25. ex.errors <- sessionExchange(ex.conn, ex.target, ex.master)
  26. close(ex.errors)
  27. }
  28. }()
  29. return ce
  30. }
  31. func exchange(conn net.Conn, targetCall string, master bool) error {
  32. e := ex{
  33. conn: conn,
  34. target: targetCall,
  35. master: master,
  36. errors: make(chan error),
  37. }
  38. exchangeChan <- e
  39. return <-e.errors
  40. }
  41. type NotifyMBox struct{ fbb.MBoxHandler }
  42. func (m NotifyMBox) ProcessInbound(msgs ...*fbb.Message) error {
  43. if err := m.MBoxHandler.ProcessInbound(msgs...); err != nil {
  44. return err
  45. }
  46. for _, msg := range msgs {
  47. websocketHub.WriteJSON(struct{ Notification Notification }{
  48. Notification{
  49. Title: fmt.Sprintf("New message from %s", msg.From().Addr),
  50. Body: msg.Subject(),
  51. },
  52. })
  53. }
  54. return nil
  55. }
  56. func sessionExchange(conn net.Conn, targetCall string, master bool) error {
  57. exchangeConn = conn
  58. websocketHub.UpdateStatus()
  59. defer func() { exchangeConn = nil; websocketHub.UpdateStatus() }()
  60. // New wl2k Session
  61. targetCall = strings.Split(targetCall, ` `)[0]
  62. session := fbb.NewSession(
  63. fOptions.MyCall,
  64. targetCall,
  65. config.Locator,
  66. NotifyMBox{mbox},
  67. )
  68. session.SetUserAgent(fbb.UserAgent{
  69. Name: AppName,
  70. Version: Version,
  71. })
  72. if len(config.MOTD) > 0 {
  73. session.SetMOTD(config.MOTD...)
  74. }
  75. // Handle secure login
  76. session.SetSecureLoginHandleFunc(func() (string, error) {
  77. if config.SecureLoginPassword != "" {
  78. return config.SecureLoginPassword, nil
  79. }
  80. resp := <-promptHub.Prompt("password", "Enter secure login password")
  81. return resp.Value, resp.Err
  82. })
  83. for _, addr := range config.AuxAddrs {
  84. session.AddAuxiliaryAddress(fbb.AddressFromString(addr))
  85. }
  86. session.IsMaster(master)
  87. session.SetLogger(log.New(logWriter, "", 0))
  88. session.SetStatusUpdater(new(StatusUpdate))
  89. if fOptions.Robust {
  90. session.SetRobustMode(fbb.RobustForced)
  91. }
  92. log.Printf("Connected to %s (%s)", conn.RemoteAddr(), conn.RemoteAddr().Network())
  93. // Close connection on os.Interrupt
  94. stop := handleInterrupt()
  95. defer close(stop)
  96. startTs := time.Now()
  97. stats, err := session.Exchange(conn)
  98. if fbb.IsLoginFailure(err) {
  99. fmt.Println("NOTE: A new password scheme for Winlink is being implemented as of 2018-01-31.")
  100. fmt.Println(" Users with passwords created/changed prior to January 31, 2018 should be")
  101. fmt.Println(" aware that their password MUST be entered in ALL-UPPERCASE letters. Only")
  102. fmt.Println(" passwords created/changed/issued after January 31, 2018 should/may contain")
  103. fmt.Println(" lowercase letters. - https://github.com/la5nta/pat/issues/113")
  104. }
  105. event := map[string]interface{}{
  106. "mycall": session.Mycall(),
  107. "targetcall": session.Targetcall(),
  108. "remote_fw": session.RemoteForwarders(),
  109. "remote_sid": session.RemoteSID(),
  110. "master": master,
  111. "local_locator": config.Locator,
  112. "auxiliary_addresses": config.AuxAddrs,
  113. "network": conn.RemoteAddr().Network(),
  114. "remote_addr": conn.RemoteAddr().String(),
  115. "local_addr": conn.LocalAddr().String(),
  116. "sent": stats.Sent,
  117. "received": stats.Received,
  118. "start": startTs.Unix(),
  119. "end": time.Now().Unix(),
  120. "success": err == nil,
  121. }
  122. if err != nil {
  123. event["error"] = err.Error()
  124. }
  125. eventLog.Log("exchange", event)
  126. return err
  127. }
  128. func handleInterrupt() (stop chan struct{}) {
  129. stop = make(chan struct{})
  130. go func() {
  131. sig := make(chan os.Signal)
  132. signal.Notify(sig, os.Interrupt)
  133. defer func() { signal.Stop(sig); close(sig) }()
  134. wmDisc := false // So we can DirtyDisconnect on second interrupt
  135. adDisc := false // So we can Abort on second interrupt
  136. for {
  137. select {
  138. case <-stop:
  139. return
  140. case s := <-sig:
  141. if exchangeConn != nil {
  142. log.Printf("Got %s, disconnecting...", s)
  143. exchangeConn.Close()
  144. break
  145. }
  146. if pModem != nil {
  147. log.Println("Disconnecting pactor...")
  148. if err := pModem.Close(); err != nil {
  149. log.Println(err)
  150. }
  151. break
  152. }
  153. if wmTNC != nil && !wmTNC.Idle() {
  154. if wmDisc {
  155. log.Println("Dirty disconnecting winmor...")
  156. wmTNC.DirtyDisconnect()
  157. wmDisc = false
  158. } else {
  159. log.Println("Disconnecting winmor...")
  160. wmDisc = true
  161. go func() {
  162. if err := wmTNC.Disconnect(); err != nil {
  163. log.Println(err)
  164. } else {
  165. wmDisc = false
  166. }
  167. }()
  168. }
  169. }
  170. if adTNC != nil && !adTNC.Idle() {
  171. if adDisc {
  172. log.Println("Dirty disconnecting ardop...")
  173. adTNC.Abort()
  174. adDisc = false
  175. } else {
  176. log.Println("Disconnecting ardop...")
  177. adDisc = true
  178. go func() {
  179. if err := adTNC.Disconnect(); err != nil {
  180. log.Println(err)
  181. } else {
  182. adDisc = false
  183. }
  184. }()
  185. }
  186. }
  187. }
  188. }
  189. }()
  190. return stop
  191. }
  192. type StatusUpdate int
  193. func (s *StatusUpdate) UpdateStatus(stat fbb.Status) {
  194. var prop fbb.Proposal
  195. switch {
  196. case stat.Receiving != nil:
  197. prop = *stat.Receiving
  198. case stat.Sending != nil:
  199. prop = *stat.Sending
  200. }
  201. websocketHub.WriteProgress(Progress{
  202. MID: prop.MID(),
  203. BytesTotal: stat.BytesTotal,
  204. BytesTransferred: stat.BytesTransferred,
  205. Subject: prop.Title(),
  206. Receiving: stat.Receiving != nil,
  207. Sending: stat.Sending != nil,
  208. Done: stat.Done,
  209. })
  210. percent := float64(stat.BytesTransferred) / float64(stat.BytesTotal) * 100
  211. fmt.Printf("\r%s: %3.0f%%", prop.Title(), percent)
  212. if stat.Done {
  213. fmt.Println("")
  214. }
  215. os.Stdout.Sync()
  216. }