server.go 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560
  1. package server
  2. import (
  3. "bytes"
  4. "context"
  5. "crypto/sha256"
  6. "embed"
  7. "encoding/base64"
  8. "encoding/json"
  9. "errors"
  10. "fmt"
  11. "io"
  12. "net"
  13. "net/http"
  14. "net/netip"
  15. "net/url"
  16. "os"
  17. "path"
  18. "path/filepath"
  19. "regexp"
  20. "sort"
  21. "strconv"
  22. "strings"
  23. "sync"
  24. "time"
  25. "unicode/utf8"
  26. "heckel.io/ntfy/log"
  27. "github.com/emersion/go-smtp"
  28. "github.com/gorilla/websocket"
  29. "golang.org/x/sync/errgroup"
  30. "heckel.io/ntfy/auth"
  31. "heckel.io/ntfy/util"
  32. )
  33. /*
  34. TODO
  35. persist user stats in user table
  36. expire tokens
  37. auto-refresh tokens from UI
  38. reserve topics
  39. rate limit for signup (2 per 24h)
  40. handle invalid session token
  41. update disallowed topics
  42. purge accounts that were not logged into in X
  43. sync subscription display name
  44. Pages:
  45. - Home
  46. - Password reset
  47. - Pricing
  48. - change email
  49. -
  50. Polishing:
  51. aria-label for everything
  52. */
  53. // Server is the main server, providing the UI and API for ntfy
  54. type Server struct {
  55. config *Config
  56. httpServer *http.Server
  57. httpsServer *http.Server
  58. unixListener net.Listener
  59. smtpServer *smtp.Server
  60. smtpServerBackend *smtpBackend
  61. smtpSender mailer
  62. topics map[string]*topic
  63. visitors map[string]*visitor // ip:<ip> or user:<user>
  64. firebaseClient *firebaseClient
  65. messages int64
  66. auth auth.Manager
  67. messageCache *messageCache
  68. fileCache *fileCache
  69. closeChan chan bool
  70. mu sync.Mutex
  71. }
  72. // handleFunc extends the normal http.HandlerFunc to be able to easily return errors
  73. type handleFunc func(http.ResponseWriter, *http.Request, *visitor) error
  74. var (
  75. // If changed, don't forget to update Android App and auth_sqlite.go
  76. topicRegex = regexp.MustCompile(`^[-_A-Za-z0-9]{1,64}$`) // No /!
  77. topicPathRegex = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}$`) // Regex must match JS & Android app!
  78. externalTopicPathRegex = regexp.MustCompile(`^/[^/]+\.[^/]+/[-_A-Za-z0-9]{1,64}$`) // Extended topic path, for web-app, e.g. /example.com/mytopic
  79. jsonPathRegex = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}(,[-_A-Za-z0-9]{1,64})*/json$`)
  80. ssePathRegex = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}(,[-_A-Za-z0-9]{1,64})*/sse$`)
  81. rawPathRegex = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}(,[-_A-Za-z0-9]{1,64})*/raw$`)
  82. wsPathRegex = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}(,[-_A-Za-z0-9]{1,64})*/ws$`)
  83. authPathRegex = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}(,[-_A-Za-z0-9]{1,64})*/auth$`)
  84. publishPathRegex = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}/(publish|send|trigger)$`)
  85. webConfigPath = "/config.js"
  86. accountPath = "/v1/account"
  87. accountTokenPath = "/v1/account/token"
  88. accountPasswordPath = "/v1/account/password"
  89. accountSettingsPath = "/v1/account/settings"
  90. accountSubscriptionPath = "/v1/account/subscription"
  91. accountSubscriptionSingleRegex = regexp.MustCompile(`^/v1/account/subscription/([-_A-Za-z0-9]{16})$`)
  92. matrixPushPath = "/_matrix/push/v1/notify"
  93. staticRegex = regexp.MustCompile(`^/static/.+`)
  94. docsRegex = regexp.MustCompile(`^/docs(|/.*)$`)
  95. fileRegex = regexp.MustCompile(`^/file/([-_A-Za-z0-9]{1,64})(?:\.[A-Za-z0-9]{1,16})?$`)
  96. disallowedTopics = []string{"docs", "static", "file", "app", "settings"} // If updated, also update in Android app
  97. urlRegex = regexp.MustCompile(`^https?://`)
  98. //go:embed site
  99. webFs embed.FS
  100. webFsCached = &util.CachingEmbedFS{ModTime: time.Now(), FS: webFs}
  101. webSiteDir = "/site"
  102. webHomeIndex = "/home.html" // Landing page, only if "web-root: home"
  103. webAppIndex = "/app.html" // React app
  104. //go:embed docs
  105. docsStaticFs embed.FS
  106. docsStaticCached = &util.CachingEmbedFS{ModTime: time.Now(), FS: docsStaticFs}
  107. )
  108. const (
  109. firebaseControlTopic = "~control" // See Android if changed
  110. firebasePollTopic = "~poll" // See iOS if changed
  111. emptyMessageBody = "triggered" // Used if message body is empty
  112. newMessageBody = "New message" // Used in poll requests as generic message
  113. defaultAttachmentMessage = "You received a file: %s" // Used if message body is empty, and there is an attachment
  114. encodingBase64 = "base64"
  115. )
  116. // WebSocket constants
  117. const (
  118. wsWriteWait = 2 * time.Second
  119. wsBufferSize = 1024
  120. wsReadLimit = 64 // We only ever receive PINGs
  121. wsPongWait = 15 * time.Second
  122. )
  123. // New instantiates a new Server. It creates the cache and adds a Firebase
  124. // subscriber (if configured).
  125. func New(conf *Config) (*Server, error) {
  126. var mailer mailer
  127. if conf.SMTPSenderAddr != "" {
  128. mailer = &smtpSender{config: conf}
  129. }
  130. messageCache, err := createMessageCache(conf)
  131. if err != nil {
  132. return nil, err
  133. }
  134. topics, err := messageCache.Topics()
  135. if err != nil {
  136. return nil, err
  137. }
  138. var fileCache *fileCache
  139. if conf.AttachmentCacheDir != "" {
  140. fileCache, err = newFileCache(conf.AttachmentCacheDir, conf.AttachmentTotalSizeLimit, conf.AttachmentFileSizeLimit)
  141. if err != nil {
  142. return nil, err
  143. }
  144. }
  145. var auther auth.Manager
  146. if conf.AuthFile != "" {
  147. auther, err = auth.NewSQLiteAuthManager(conf.AuthFile, conf.AuthDefaultRead, conf.AuthDefaultWrite)
  148. if err != nil {
  149. return nil, err
  150. }
  151. }
  152. var firebaseClient *firebaseClient
  153. if conf.FirebaseKeyFile != "" {
  154. sender, err := newFirebaseSender(conf.FirebaseKeyFile)
  155. if err != nil {
  156. return nil, err
  157. }
  158. firebaseClient = newFirebaseClient(sender, auther)
  159. }
  160. return &Server{
  161. config: conf,
  162. messageCache: messageCache,
  163. fileCache: fileCache,
  164. firebaseClient: firebaseClient,
  165. smtpSender: mailer,
  166. topics: topics,
  167. auth: auther,
  168. visitors: make(map[string]*visitor),
  169. }, nil
  170. }
  171. func createMessageCache(conf *Config) (*messageCache, error) {
  172. if conf.CacheDuration == 0 {
  173. return newNopCache()
  174. } else if conf.CacheFile != "" {
  175. return newSqliteCache(conf.CacheFile, conf.CacheStartupQueries, conf.CacheBatchSize, conf.CacheBatchTimeout, false)
  176. }
  177. return newMemCache()
  178. }
  179. // Run executes the main server. It listens on HTTP (+ HTTPS, if configured), and starts
  180. // a manager go routine to print stats and prune messages.
  181. func (s *Server) Run() error {
  182. var listenStr string
  183. if s.config.ListenHTTP != "" {
  184. listenStr += fmt.Sprintf(" %s[http]", s.config.ListenHTTP)
  185. }
  186. if s.config.ListenHTTPS != "" {
  187. listenStr += fmt.Sprintf(" %s[https]", s.config.ListenHTTPS)
  188. }
  189. if s.config.ListenUnix != "" {
  190. listenStr += fmt.Sprintf(" %s[unix]", s.config.ListenUnix)
  191. }
  192. if s.config.SMTPServerListen != "" {
  193. listenStr += fmt.Sprintf(" %s[smtp]", s.config.SMTPServerListen)
  194. }
  195. log.Info("Listening on%s, ntfy %s, log level is %s", listenStr, s.config.Version, log.CurrentLevel().String())
  196. mux := http.NewServeMux()
  197. mux.HandleFunc("/", s.handle)
  198. errChan := make(chan error)
  199. s.mu.Lock()
  200. s.closeChan = make(chan bool)
  201. if s.config.ListenHTTP != "" {
  202. s.httpServer = &http.Server{Addr: s.config.ListenHTTP, Handler: mux}
  203. go func() {
  204. errChan <- s.httpServer.ListenAndServe()
  205. }()
  206. }
  207. if s.config.ListenHTTPS != "" {
  208. s.httpsServer = &http.Server{Addr: s.config.ListenHTTPS, Handler: mux}
  209. go func() {
  210. errChan <- s.httpsServer.ListenAndServeTLS(s.config.CertFile, s.config.KeyFile)
  211. }()
  212. }
  213. if s.config.ListenUnix != "" {
  214. go func() {
  215. var err error
  216. s.mu.Lock()
  217. os.Remove(s.config.ListenUnix)
  218. s.unixListener, err = net.Listen("unix", s.config.ListenUnix)
  219. if err != nil {
  220. s.mu.Unlock()
  221. errChan <- err
  222. return
  223. }
  224. defer s.unixListener.Close()
  225. if s.config.ListenUnixMode > 0 {
  226. if err := os.Chmod(s.config.ListenUnix, s.config.ListenUnixMode); err != nil {
  227. s.mu.Unlock()
  228. errChan <- err
  229. return
  230. }
  231. }
  232. s.mu.Unlock()
  233. httpServer := &http.Server{Handler: mux}
  234. errChan <- httpServer.Serve(s.unixListener)
  235. }()
  236. }
  237. if s.config.SMTPServerListen != "" {
  238. go func() {
  239. errChan <- s.runSMTPServer()
  240. }()
  241. }
  242. s.mu.Unlock()
  243. go s.runManager()
  244. go s.runDelayedSender()
  245. go s.runFirebaseKeepaliver()
  246. return <-errChan
  247. }
  248. // Stop stops HTTP (+HTTPS) server and all managers
  249. func (s *Server) Stop() {
  250. s.mu.Lock()
  251. defer s.mu.Unlock()
  252. if s.httpServer != nil {
  253. s.httpServer.Close()
  254. }
  255. if s.httpsServer != nil {
  256. s.httpsServer.Close()
  257. }
  258. if s.unixListener != nil {
  259. s.unixListener.Close()
  260. }
  261. if s.smtpServer != nil {
  262. s.smtpServer.Close()
  263. }
  264. close(s.closeChan)
  265. }
  266. func (s *Server) handle(w http.ResponseWriter, r *http.Request) {
  267. v, err := s.visitor(r) // Note: Always returns v, even when error is returned
  268. if err == nil {
  269. log.Debug("%s Dispatching request", logHTTPPrefix(v, r))
  270. if log.IsTrace() {
  271. log.Trace("%s Entire request (headers and body):\n%s", logHTTPPrefix(v, r), renderHTTPRequest(r))
  272. }
  273. err = s.handleInternal(w, r, v)
  274. }
  275. if err != nil {
  276. if websocket.IsWebSocketUpgrade(r) {
  277. isNormalError := strings.Contains(err.Error(), "i/o timeout")
  278. if isNormalError {
  279. log.Debug("%s WebSocket error (this error is okay, it happens a lot): %s", logHTTPPrefix(v, r), err.Error())
  280. } else {
  281. log.Info("%s WebSocket error: %s", logHTTPPrefix(v, r), err.Error())
  282. }
  283. return // Do not attempt to write to upgraded connection
  284. }
  285. if matrixErr, ok := err.(*errMatrix); ok {
  286. writeMatrixError(w, r, v, matrixErr)
  287. return
  288. }
  289. httpErr, ok := err.(*errHTTP)
  290. if !ok {
  291. httpErr = errHTTPInternalError
  292. }
  293. isNormalError := httpErr.HTTPCode == http.StatusNotFound || httpErr.HTTPCode == http.StatusBadRequest
  294. if isNormalError {
  295. log.Debug("%s Connection closed with HTTP %d (ntfy error %d): %s", logHTTPPrefix(v, r), httpErr.HTTPCode, httpErr.Code, err.Error())
  296. } else {
  297. log.Info("%s Connection closed with HTTP %d (ntfy error %d): %s", logHTTPPrefix(v, r), httpErr.HTTPCode, httpErr.Code, err.Error())
  298. }
  299. w.Header().Set("Content-Type", "application/json")
  300. w.Header().Set("Access-Control-Allow-Origin", "*") // CORS, allow cross-origin requests
  301. w.WriteHeader(httpErr.HTTPCode)
  302. io.WriteString(w, httpErr.JSON()+"\n")
  303. }
  304. }
  305. func (s *Server) handleInternal(w http.ResponseWriter, r *http.Request, v *visitor) error {
  306. if r.Method == http.MethodGet && r.URL.Path == "/" {
  307. return s.ensureWebEnabled(s.handleHome)(w, r, v)
  308. } else if r.Method == http.MethodHead && r.URL.Path == "/" {
  309. return s.ensureWebEnabled(s.handleEmpty)(w, r, v)
  310. } else if r.Method == http.MethodGet && r.URL.Path == webConfigPath {
  311. return s.ensureWebEnabled(s.handleWebConfig)(w, r, v)
  312. } else if r.Method == http.MethodPost && r.URL.Path == accountPath {
  313. return s.handleAccountCreate(w, r, v)
  314. } else if r.Method == http.MethodGet && r.URL.Path == accountPath {
  315. return s.handleAccountGet(w, r, v)
  316. } else if r.Method == http.MethodDelete && r.URL.Path == accountPath {
  317. return s.handleAccountDelete(w, r, v)
  318. } else if r.Method == http.MethodPost && r.URL.Path == accountPasswordPath {
  319. return s.handleAccountPasswordChange(w, r, v)
  320. } else if r.Method == http.MethodGet && r.URL.Path == accountTokenPath {
  321. return s.handleAccountTokenGet(w, r, v)
  322. } else if r.Method == http.MethodDelete && r.URL.Path == accountTokenPath {
  323. return s.handleAccountTokenDelete(w, r, v)
  324. } else if r.Method == http.MethodPost && r.URL.Path == accountSettingsPath {
  325. return s.handleAccountSettingsChange(w, r, v)
  326. } else if r.Method == http.MethodPost && r.URL.Path == accountSubscriptionPath {
  327. return s.handleAccountSubscriptionAdd(w, r, v)
  328. } else if r.Method == http.MethodDelete && accountSubscriptionSingleRegex.MatchString(r.URL.Path) {
  329. return s.handleAccountSubscriptionDelete(w, r, v)
  330. } else if r.Method == http.MethodGet && r.URL.Path == matrixPushPath {
  331. return s.handleMatrixDiscovery(w)
  332. } else if r.Method == http.MethodGet && staticRegex.MatchString(r.URL.Path) {
  333. return s.ensureWebEnabled(s.handleStatic)(w, r, v)
  334. } else if r.Method == http.MethodGet && docsRegex.MatchString(r.URL.Path) {
  335. return s.ensureWebEnabled(s.handleDocs)(w, r, v)
  336. } else if (r.Method == http.MethodGet || r.Method == http.MethodHead) && fileRegex.MatchString(r.URL.Path) && s.config.AttachmentCacheDir != "" {
  337. return s.limitRequests(s.handleFile)(w, r, v)
  338. } else if r.Method == http.MethodOptions {
  339. return s.ensureWebEnabled(s.handleOptions)(w, r, v)
  340. } else if (r.Method == http.MethodPut || r.Method == http.MethodPost) && r.URL.Path == "/" {
  341. return s.limitRequests(s.transformBodyJSON(s.authorizeTopicWrite(s.handlePublish)))(w, r, v)
  342. } else if r.Method == http.MethodPost && r.URL.Path == matrixPushPath {
  343. return s.limitRequests(s.transformMatrixJSON(s.authorizeTopicWrite(s.handlePublishMatrix)))(w, r, v)
  344. } else if (r.Method == http.MethodPut || r.Method == http.MethodPost) && topicPathRegex.MatchString(r.URL.Path) {
  345. return s.limitRequests(s.authorizeTopicWrite(s.handlePublish))(w, r, v)
  346. } else if r.Method == http.MethodGet && publishPathRegex.MatchString(r.URL.Path) {
  347. return s.limitRequests(s.authorizeTopicWrite(s.handlePublish))(w, r, v)
  348. } else if r.Method == http.MethodGet && jsonPathRegex.MatchString(r.URL.Path) {
  349. return s.limitRequests(s.authorizeTopicRead(s.handleSubscribeJSON))(w, r, v)
  350. } else if r.Method == http.MethodGet && ssePathRegex.MatchString(r.URL.Path) {
  351. return s.limitRequests(s.authorizeTopicRead(s.handleSubscribeSSE))(w, r, v)
  352. } else if r.Method == http.MethodGet && rawPathRegex.MatchString(r.URL.Path) {
  353. return s.limitRequests(s.authorizeTopicRead(s.handleSubscribeRaw))(w, r, v)
  354. } else if r.Method == http.MethodGet && wsPathRegex.MatchString(r.URL.Path) {
  355. return s.limitRequests(s.authorizeTopicRead(s.handleSubscribeWS))(w, r, v)
  356. } else if r.Method == http.MethodGet && authPathRegex.MatchString(r.URL.Path) {
  357. return s.limitRequests(s.authorizeTopicRead(s.handleTopicAuth))(w, r, v)
  358. } else if r.Method == http.MethodGet && (topicPathRegex.MatchString(r.URL.Path) || externalTopicPathRegex.MatchString(r.URL.Path)) {
  359. return s.ensureWebEnabled(s.handleTopic)(w, r, v)
  360. }
  361. return errHTTPNotFound
  362. }
  363. func (s *Server) handleHome(w http.ResponseWriter, r *http.Request, v *visitor) error {
  364. if s.config.WebRootIsApp {
  365. r.URL.Path = webAppIndex
  366. } else {
  367. r.URL.Path = webHomeIndex
  368. }
  369. return s.handleStatic(w, r, v)
  370. }
  371. func (s *Server) handleTopic(w http.ResponseWriter, r *http.Request, v *visitor) error {
  372. unifiedpush := readBoolParam(r, false, "x-unifiedpush", "unifiedpush", "up") // see PUT/POST too!
  373. if unifiedpush {
  374. w.Header().Set("Content-Type", "application/json")
  375. w.Header().Set("Access-Control-Allow-Origin", "*") // CORS, allow cross-origin requests
  376. _, err := io.WriteString(w, `{"unifiedpush":{"version":1}}`+"\n")
  377. return err
  378. }
  379. r.URL.Path = webAppIndex
  380. return s.handleStatic(w, r, v)
  381. }
  382. func (s *Server) handleEmpty(_ http.ResponseWriter, _ *http.Request, _ *visitor) error {
  383. return nil
  384. }
  385. func (s *Server) handleTopicAuth(w http.ResponseWriter, _ *http.Request, _ *visitor) error {
  386. w.Header().Set("Content-Type", "application/json")
  387. w.Header().Set("Access-Control-Allow-Origin", "*") // CORS, allow cross-origin requests
  388. _, err := io.WriteString(w, `{"success":true}`+"\n")
  389. return err
  390. }
  391. func (s *Server) handleWebConfig(w http.ResponseWriter, _ *http.Request, _ *visitor) error {
  392. appRoot := "/"
  393. if !s.config.WebRootIsApp {
  394. appRoot = "/app"
  395. }
  396. disallowedTopicsStr := `"` + strings.Join(disallowedTopics, `", "`) + `"`
  397. w.Header().Set("Content-Type", "text/javascript")
  398. _, err := io.WriteString(w, fmt.Sprintf(`// Generated server configuration
  399. var config = {
  400. appRoot: "%s",
  401. disallowedTopics: [%s]
  402. };`, appRoot, disallowedTopicsStr))
  403. return err
  404. }
  405. func (s *Server) handleStatic(w http.ResponseWriter, r *http.Request, _ *visitor) error {
  406. r.URL.Path = webSiteDir + r.URL.Path
  407. util.Gzip(http.FileServer(http.FS(webFsCached))).ServeHTTP(w, r)
  408. return nil
  409. }
  410. func (s *Server) handleDocs(w http.ResponseWriter, r *http.Request, _ *visitor) error {
  411. util.Gzip(http.FileServer(http.FS(docsStaticCached))).ServeHTTP(w, r)
  412. return nil
  413. }
  414. func (s *Server) handleFile(w http.ResponseWriter, r *http.Request, v *visitor) error {
  415. if s.config.AttachmentCacheDir == "" {
  416. return errHTTPInternalError
  417. }
  418. matches := fileRegex.FindStringSubmatch(r.URL.Path)
  419. if len(matches) != 2 {
  420. return errHTTPInternalErrorInvalidFilePath
  421. }
  422. messageID := matches[1]
  423. file := filepath.Join(s.config.AttachmentCacheDir, messageID)
  424. stat, err := os.Stat(file)
  425. if err != nil {
  426. return errHTTPNotFound
  427. }
  428. if r.Method == http.MethodGet {
  429. if err := v.BandwidthLimiter().Allow(stat.Size()); err != nil {
  430. return errHTTPTooManyRequestsAttachmentBandwidthLimit
  431. }
  432. }
  433. w.Header().Set("Content-Length", fmt.Sprintf("%d", stat.Size()))
  434. w.Header().Set("Access-Control-Allow-Origin", "*") // CORS, allow cross-origin requests
  435. if r.Method == http.MethodGet {
  436. f, err := os.Open(file)
  437. if err != nil {
  438. return err
  439. }
  440. defer f.Close()
  441. _, err = io.Copy(util.NewContentTypeWriter(w, r.URL.Path), f)
  442. return err
  443. }
  444. return nil
  445. }
  446. func (s *Server) handleMatrixDiscovery(w http.ResponseWriter) error {
  447. if s.config.BaseURL == "" {
  448. return errHTTPInternalErrorMissingBaseURL
  449. }
  450. return writeMatrixDiscoveryResponse(w)
  451. }
  452. func (s *Server) handlePublishWithoutResponse(r *http.Request, v *visitor) (*message, error) {
  453. t, err := s.topicFromPath(r.URL.Path)
  454. if err != nil {
  455. return nil, err
  456. }
  457. body, err := util.Peek(r.Body, s.config.MessageLimit)
  458. if err != nil {
  459. return nil, err
  460. }
  461. m := newDefaultMessage(t.ID, "")
  462. cache, firebase, email, unifiedpush, err := s.parsePublishParams(r, v, m)
  463. if err != nil {
  464. return nil, err
  465. }
  466. if m.PollID != "" {
  467. m = newPollRequestMessage(t.ID, m.PollID)
  468. }
  469. if v.user != nil {
  470. log.Info("user is %s", v.user.Name)
  471. m.User = v.user.Name
  472. }
  473. if err := s.handlePublishBody(r, v, m, body, unifiedpush); err != nil {
  474. return nil, err
  475. }
  476. if m.Message == "" {
  477. m.Message = emptyMessageBody
  478. }
  479. delayed := m.Time > time.Now().Unix()
  480. log.Debug("%s Received message: event=%s, user=%s, body=%d byte(s), delayed=%t, firebase=%t, cache=%t, up=%t, email=%s",
  481. logMessagePrefix(v, m), m.Event, m.User, len(m.Message), delayed, firebase, cache, unifiedpush, email)
  482. if log.IsTrace() {
  483. log.Trace("%s Message body: %s", logMessagePrefix(v, m), util.MaybeMarshalJSON(m))
  484. }
  485. if !delayed {
  486. if err := t.Publish(v, m); err != nil {
  487. return nil, err
  488. }
  489. if s.firebaseClient != nil && firebase {
  490. go s.sendToFirebase(v, m)
  491. }
  492. if s.smtpSender != nil && email != "" {
  493. v.IncrEmails()
  494. go s.sendEmail(v, m, email)
  495. }
  496. if s.config.UpstreamBaseURL != "" {
  497. go s.forwardPollRequest(v, m)
  498. }
  499. } else {
  500. log.Debug("%s Message delayed, will process later", logMessagePrefix(v, m))
  501. }
  502. if cache {
  503. log.Debug("%s Adding message to cache", logMessagePrefix(v, m))
  504. if err := s.messageCache.AddMessage(m); err != nil {
  505. return nil, err
  506. }
  507. }
  508. v.IncrMessages()
  509. s.mu.Lock()
  510. s.messages++
  511. s.mu.Unlock()
  512. return m, nil
  513. }
  514. func (s *Server) handlePublish(w http.ResponseWriter, r *http.Request, v *visitor) error {
  515. m, err := s.handlePublishWithoutResponse(r, v)
  516. if err != nil {
  517. return err
  518. }
  519. w.Header().Set("Content-Type", "application/json")
  520. w.Header().Set("Access-Control-Allow-Origin", "*") // CORS, allow cross-origin requests
  521. if err := json.NewEncoder(w).Encode(m); err != nil {
  522. return err
  523. }
  524. return nil
  525. }
  526. func (s *Server) handlePublishMatrix(w http.ResponseWriter, r *http.Request, v *visitor) error {
  527. _, err := s.handlePublishWithoutResponse(r, v)
  528. if err != nil {
  529. return &errMatrix{pushKey: r.Header.Get(matrixPushKeyHeader), err: err}
  530. }
  531. return writeMatrixSuccess(w)
  532. }
  533. func (s *Server) sendToFirebase(v *visitor, m *message) {
  534. log.Debug("%s Publishing to Firebase", logMessagePrefix(v, m))
  535. if err := s.firebaseClient.Send(v, m); err != nil {
  536. if err == errFirebaseTemporarilyBanned {
  537. log.Debug("%s Unable to publish to Firebase: %v", logMessagePrefix(v, m), err.Error())
  538. } else {
  539. log.Warn("%s Unable to publish to Firebase: %v", logMessagePrefix(v, m), err.Error())
  540. }
  541. }
  542. }
  543. func (s *Server) sendEmail(v *visitor, m *message, email string) {
  544. log.Debug("%s Sending email to %s", logMessagePrefix(v, m), email)
  545. if err := s.smtpSender.Send(v, m, email); err != nil {
  546. log.Warn("%s Unable to send email to %s: %v", logMessagePrefix(v, m), email, err.Error())
  547. }
  548. }
  549. func (s *Server) forwardPollRequest(v *visitor, m *message) {
  550. topicURL := fmt.Sprintf("%s/%s", s.config.BaseURL, m.Topic)
  551. topicHash := fmt.Sprintf("%x", sha256.Sum256([]byte(topicURL)))
  552. forwardURL := fmt.Sprintf("%s/%s", s.config.UpstreamBaseURL, topicHash)
  553. log.Debug("%s Publishing poll request to %s", logMessagePrefix(v, m), forwardURL)
  554. req, err := http.NewRequest("POST", forwardURL, strings.NewReader(""))
  555. if err != nil {
  556. log.Warn("%s Unable to publish poll request: %v", logMessagePrefix(v, m), err.Error())
  557. return
  558. }
  559. req.Header.Set("X-Poll-ID", m.ID)
  560. var httpClient = &http.Client{
  561. Timeout: time.Second * 10,
  562. }
  563. response, err := httpClient.Do(req)
  564. if err != nil {
  565. log.Warn("%s Unable to publish poll request: %v", logMessagePrefix(v, m), err.Error())
  566. return
  567. } else if response.StatusCode != http.StatusOK {
  568. log.Warn("%s Unable to publish poll request, unexpected HTTP status: %d", logMessagePrefix(v, m), response.StatusCode)
  569. return
  570. }
  571. }
  572. func (s *Server) parsePublishParams(r *http.Request, v *visitor, m *message) (cache bool, firebase bool, email string, unifiedpush bool, err error) {
  573. cache = readBoolParam(r, true, "x-cache", "cache")
  574. firebase = readBoolParam(r, true, "x-firebase", "firebase")
  575. m.Title = readParam(r, "x-title", "title", "t")
  576. m.Click = readParam(r, "x-click", "click")
  577. icon := readParam(r, "x-icon", "icon")
  578. filename := readParam(r, "x-filename", "filename", "file", "f")
  579. attach := readParam(r, "x-attach", "attach", "a")
  580. if attach != "" || filename != "" {
  581. m.Attachment = &attachment{}
  582. }
  583. if filename != "" {
  584. m.Attachment.Name = filename
  585. }
  586. if attach != "" {
  587. if !urlRegex.MatchString(attach) {
  588. return false, false, "", false, errHTTPBadRequestAttachmentURLInvalid
  589. }
  590. m.Attachment.URL = attach
  591. if m.Attachment.Name == "" {
  592. u, err := url.Parse(m.Attachment.URL)
  593. if err == nil {
  594. m.Attachment.Name = path.Base(u.Path)
  595. if m.Attachment.Name == "." || m.Attachment.Name == "/" {
  596. m.Attachment.Name = ""
  597. }
  598. }
  599. }
  600. if m.Attachment.Name == "" {
  601. m.Attachment.Name = "attachment"
  602. }
  603. }
  604. if icon != "" {
  605. if !urlRegex.MatchString(icon) {
  606. return false, false, "", false, errHTTPBadRequestIconURLInvalid
  607. }
  608. m.Icon = icon
  609. }
  610. email = readParam(r, "x-email", "x-e-mail", "email", "e-mail", "mail", "e")
  611. if email != "" {
  612. if err := v.EmailAllowed(); err != nil {
  613. return false, false, "", false, errHTTPTooManyRequestsLimitEmails
  614. }
  615. }
  616. if s.smtpSender == nil && email != "" {
  617. return false, false, "", false, errHTTPBadRequestEmailDisabled
  618. }
  619. messageStr := strings.ReplaceAll(readParam(r, "x-message", "message", "m"), "\\n", "\n")
  620. if messageStr != "" {
  621. m.Message = messageStr
  622. }
  623. m.Priority, err = util.ParsePriority(readParam(r, "x-priority", "priority", "prio", "p"))
  624. if err != nil {
  625. return false, false, "", false, errHTTPBadRequestPriorityInvalid
  626. }
  627. tagsStr := readParam(r, "x-tags", "tags", "tag", "ta")
  628. if tagsStr != "" {
  629. m.Tags = make([]string, 0)
  630. for _, s := range util.SplitNoEmpty(tagsStr, ",") {
  631. m.Tags = append(m.Tags, strings.TrimSpace(s))
  632. }
  633. }
  634. delayStr := readParam(r, "x-delay", "delay", "x-at", "at", "x-in", "in")
  635. if delayStr != "" {
  636. if !cache {
  637. return false, false, "", false, errHTTPBadRequestDelayNoCache
  638. }
  639. if email != "" {
  640. return false, false, "", false, errHTTPBadRequestDelayNoEmail // we cannot store the email address (yet)
  641. }
  642. delay, err := util.ParseFutureTime(delayStr, time.Now())
  643. if err != nil {
  644. return false, false, "", false, errHTTPBadRequestDelayCannotParse
  645. } else if delay.Unix() < time.Now().Add(s.config.MinDelay).Unix() {
  646. return false, false, "", false, errHTTPBadRequestDelayTooSmall
  647. } else if delay.Unix() > time.Now().Add(s.config.MaxDelay).Unix() {
  648. return false, false, "", false, errHTTPBadRequestDelayTooLarge
  649. }
  650. m.Time = delay.Unix()
  651. m.Sender = v.ip // Important for rate limiting
  652. }
  653. actionsStr := readParam(r, "x-actions", "actions", "action")
  654. if actionsStr != "" {
  655. m.Actions, err = parseActions(actionsStr)
  656. if err != nil {
  657. return false, false, "", false, wrapErrHTTP(errHTTPBadRequestActionsInvalid, err.Error())
  658. }
  659. }
  660. unifiedpush = readBoolParam(r, false, "x-unifiedpush", "unifiedpush", "up") // see GET too!
  661. if unifiedpush {
  662. firebase = false
  663. unifiedpush = true
  664. }
  665. m.PollID = readParam(r, "x-poll-id", "poll-id")
  666. if m.PollID != "" {
  667. unifiedpush = false
  668. cache = false
  669. email = ""
  670. }
  671. return cache, firebase, email, unifiedpush, nil
  672. }
  673. // handlePublishBody consumes the PUT/POST body and decides whether the body is an attachment or the message.
  674. //
  675. // 1. curl -X POST -H "Poll: 1234" ntfy.sh/...
  676. // If a message is flagged as poll request, the body does not matter and is discarded
  677. // 2. curl -T somebinarydata.bin "ntfy.sh/mytopic?up=1"
  678. // If body is binary, encode as base64, if not do not encode
  679. // 3. curl -H "Attach: http://example.com/file.jpg" ntfy.sh/mytopic
  680. // Body must be a message, because we attached an external URL
  681. // 4. curl -T short.txt -H "Filename: short.txt" ntfy.sh/mytopic
  682. // Body must be attachment, because we passed a filename
  683. // 5. curl -T file.txt ntfy.sh/mytopic
  684. // If file.txt is <= 4096 (message limit) and valid UTF-8, treat it as a message
  685. // 6. curl -T file.txt ntfy.sh/mytopic
  686. // If file.txt is > message limit, treat it as an attachment
  687. func (s *Server) handlePublishBody(r *http.Request, v *visitor, m *message, body *util.PeekedReadCloser, unifiedpush bool) error {
  688. if m.Event == pollRequestEvent { // Case 1
  689. return s.handleBodyDiscard(body)
  690. } else if unifiedpush {
  691. return s.handleBodyAsMessageAutoDetect(m, body) // Case 2
  692. } else if m.Attachment != nil && m.Attachment.URL != "" {
  693. return s.handleBodyAsTextMessage(m, body) // Case 3
  694. } else if m.Attachment != nil && m.Attachment.Name != "" {
  695. return s.handleBodyAsAttachment(r, v, m, body) // Case 4
  696. } else if !body.LimitReached && utf8.Valid(body.PeekedBytes) {
  697. return s.handleBodyAsTextMessage(m, body) // Case 5
  698. }
  699. return s.handleBodyAsAttachment(r, v, m, body) // Case 6
  700. }
  701. func (s *Server) handleBodyDiscard(body *util.PeekedReadCloser) error {
  702. _, err := io.Copy(io.Discard, body)
  703. _ = body.Close()
  704. return err
  705. }
  706. func (s *Server) handleBodyAsMessageAutoDetect(m *message, body *util.PeekedReadCloser) error {
  707. if utf8.Valid(body.PeekedBytes) {
  708. m.Message = string(body.PeekedBytes) // Do not trim
  709. } else {
  710. m.Message = base64.StdEncoding.EncodeToString(body.PeekedBytes)
  711. m.Encoding = encodingBase64
  712. }
  713. return nil
  714. }
  715. func (s *Server) handleBodyAsTextMessage(m *message, body *util.PeekedReadCloser) error {
  716. if !utf8.Valid(body.PeekedBytes) {
  717. return errHTTPBadRequestMessageNotUTF8
  718. }
  719. if len(body.PeekedBytes) > 0 { // Empty body should not override message (publish via GET!)
  720. m.Message = strings.TrimSpace(string(body.PeekedBytes)) // Truncates the message to the peek limit if required
  721. }
  722. if m.Attachment != nil && m.Attachment.Name != "" && m.Message == "" {
  723. m.Message = fmt.Sprintf(defaultAttachmentMessage, m.Attachment.Name)
  724. }
  725. return nil
  726. }
  727. func (s *Server) handleBodyAsAttachment(r *http.Request, v *visitor, m *message, body *util.PeekedReadCloser) error {
  728. if s.fileCache == nil || s.config.BaseURL == "" || s.config.AttachmentCacheDir == "" {
  729. return errHTTPBadRequestAttachmentsDisallowed
  730. } else if m.Time > time.Now().Add(s.config.AttachmentExpiryDuration).Unix() {
  731. return errHTTPBadRequestAttachmentsExpiryBeforeDelivery
  732. }
  733. visitorStats, err := v.Stats()
  734. if err != nil {
  735. return err
  736. }
  737. contentLengthStr := r.Header.Get("Content-Length")
  738. if contentLengthStr != "" { // Early "do-not-trust" check, hard limit see below
  739. contentLength, err := strconv.ParseInt(contentLengthStr, 10, 64)
  740. if err == nil && (contentLength > visitorStats.AttachmentTotalSizeRemaining || contentLength > s.config.AttachmentFileSizeLimit) {
  741. return errHTTPEntityTooLargeAttachmentTooLarge
  742. }
  743. }
  744. if m.Attachment == nil {
  745. m.Attachment = &attachment{}
  746. }
  747. var ext string
  748. m.Sender = v.ip // Important for attachment rate limiting
  749. m.Attachment.Expires = time.Now().Add(s.config.AttachmentExpiryDuration).Unix()
  750. m.Attachment.Type, ext = util.DetectContentType(body.PeekedBytes, m.Attachment.Name)
  751. m.Attachment.URL = fmt.Sprintf("%s/file/%s%s", s.config.BaseURL, m.ID, ext)
  752. if m.Attachment.Name == "" {
  753. m.Attachment.Name = fmt.Sprintf("attachment%s", ext)
  754. }
  755. if m.Message == "" {
  756. m.Message = fmt.Sprintf(defaultAttachmentMessage, m.Attachment.Name)
  757. }
  758. m.Attachment.Size, err = s.fileCache.Write(m.ID, body, v.BandwidthLimiter(), util.NewFixedLimiter(visitorStats.AttachmentTotalSizeRemaining))
  759. if err == util.ErrLimitReached {
  760. return errHTTPEntityTooLargeAttachmentTooLarge
  761. } else if err != nil {
  762. return err
  763. }
  764. return nil
  765. }
  766. func (s *Server) handleSubscribeJSON(w http.ResponseWriter, r *http.Request, v *visitor) error {
  767. encoder := func(msg *message) (string, error) {
  768. var buf bytes.Buffer
  769. if err := json.NewEncoder(&buf).Encode(&msg); err != nil {
  770. return "", err
  771. }
  772. return buf.String(), nil
  773. }
  774. return s.handleSubscribeHTTP(w, r, v, "application/x-ndjson", encoder)
  775. }
  776. func (s *Server) handleSubscribeSSE(w http.ResponseWriter, r *http.Request, v *visitor) error {
  777. encoder := func(msg *message) (string, error) {
  778. var buf bytes.Buffer
  779. if err := json.NewEncoder(&buf).Encode(&msg); err != nil {
  780. return "", err
  781. }
  782. if msg.Event != messageEvent {
  783. return fmt.Sprintf("event: %s\ndata: %s\n", msg.Event, buf.String()), nil // Browser's .onmessage() does not fire on this!
  784. }
  785. return fmt.Sprintf("data: %s\n", buf.String()), nil
  786. }
  787. return s.handleSubscribeHTTP(w, r, v, "text/event-stream", encoder)
  788. }
  789. func (s *Server) handleSubscribeRaw(w http.ResponseWriter, r *http.Request, v *visitor) error {
  790. encoder := func(msg *message) (string, error) {
  791. if msg.Event == messageEvent { // only handle default events
  792. return strings.ReplaceAll(msg.Message, "\n", " ") + "\n", nil
  793. }
  794. return "\n", nil // "keepalive" and "open" events just send an empty line
  795. }
  796. return s.handleSubscribeHTTP(w, r, v, "text/plain", encoder)
  797. }
  798. func (s *Server) handleSubscribeHTTP(w http.ResponseWriter, r *http.Request, v *visitor, contentType string, encoder messageEncoder) error {
  799. log.Debug("%s HTTP stream connection opened", logHTTPPrefix(v, r))
  800. defer log.Debug("%s HTTP stream connection closed", logHTTPPrefix(v, r))
  801. if err := v.SubscriptionAllowed(); err != nil {
  802. return errHTTPTooManyRequestsLimitSubscriptions
  803. }
  804. defer v.RemoveSubscription()
  805. topics, topicsStr, err := s.topicsFromPath(r.URL.Path)
  806. if err != nil {
  807. return err
  808. }
  809. poll, since, scheduled, filters, err := parseSubscribeParams(r)
  810. if err != nil {
  811. return err
  812. }
  813. var wlock sync.Mutex
  814. defer func() {
  815. // Hack: This is the fix for a horrible data race that I have not been able to figure out in quite some time.
  816. // It appears to be happening when the Go HTTP code reads from the socket when closing the request (i.e. AFTER
  817. // this function returns), and causes a data race with the ResponseWriter. Locking wlock here silences the
  818. // data race detector. See https://github.com/binwiederhier/ntfy/issues/338#issuecomment-1163425889.
  819. wlock.TryLock()
  820. }()
  821. sub := func(v *visitor, msg *message) error {
  822. if !filters.Pass(msg) {
  823. return nil
  824. }
  825. m, err := encoder(msg)
  826. if err != nil {
  827. return err
  828. }
  829. wlock.Lock()
  830. defer wlock.Unlock()
  831. if _, err := w.Write([]byte(m)); err != nil {
  832. return err
  833. }
  834. if fl, ok := w.(http.Flusher); ok {
  835. fl.Flush()
  836. }
  837. return nil
  838. }
  839. w.Header().Set("Access-Control-Allow-Origin", "*") // CORS, allow cross-origin requests
  840. w.Header().Set("Content-Type", contentType+"; charset=utf-8") // Android/Volley client needs charset!
  841. if poll {
  842. return s.sendOldMessages(topics, since, scheduled, v, sub)
  843. }
  844. subscriberIDs := make([]int, 0)
  845. for _, t := range topics {
  846. subscriberIDs = append(subscriberIDs, t.Subscribe(sub))
  847. }
  848. defer func() {
  849. for i, subscriberID := range subscriberIDs {
  850. topics[i].Unsubscribe(subscriberID) // Order!
  851. }
  852. }()
  853. if err := sub(v, newOpenMessage(topicsStr)); err != nil { // Send out open message
  854. return err
  855. }
  856. if err := s.sendOldMessages(topics, since, scheduled, v, sub); err != nil {
  857. return err
  858. }
  859. for {
  860. select {
  861. case <-r.Context().Done():
  862. return nil
  863. case <-time.After(s.config.KeepaliveInterval):
  864. log.Trace("%s Sending keepalive message", logHTTPPrefix(v, r))
  865. v.Keepalive()
  866. if err := sub(v, newKeepaliveMessage(topicsStr)); err != nil { // Send keepalive message
  867. return err
  868. }
  869. }
  870. }
  871. }
  872. func (s *Server) handleSubscribeWS(w http.ResponseWriter, r *http.Request, v *visitor) error {
  873. if strings.ToLower(r.Header.Get("Upgrade")) != "websocket" {
  874. return errHTTPBadRequestWebSocketsUpgradeHeaderMissing
  875. }
  876. if err := v.SubscriptionAllowed(); err != nil {
  877. return errHTTPTooManyRequestsLimitSubscriptions
  878. }
  879. defer v.RemoveSubscription()
  880. log.Debug("%s WebSocket connection opened", logHTTPPrefix(v, r))
  881. defer log.Debug("%s WebSocket connection closed", logHTTPPrefix(v, r))
  882. topics, topicsStr, err := s.topicsFromPath(r.URL.Path)
  883. if err != nil {
  884. return err
  885. }
  886. poll, since, scheduled, filters, err := parseSubscribeParams(r)
  887. if err != nil {
  888. return err
  889. }
  890. upgrader := &websocket.Upgrader{
  891. ReadBufferSize: wsBufferSize,
  892. WriteBufferSize: wsBufferSize,
  893. CheckOrigin: func(r *http.Request) bool {
  894. return true // We're open for business!
  895. },
  896. }
  897. conn, err := upgrader.Upgrade(w, r, nil)
  898. if err != nil {
  899. return err
  900. }
  901. defer conn.Close()
  902. var wlock sync.Mutex
  903. g, ctx := errgroup.WithContext(context.Background())
  904. g.Go(func() error {
  905. pongWait := s.config.KeepaliveInterval + wsPongWait
  906. conn.SetReadLimit(wsReadLimit)
  907. if err := conn.SetReadDeadline(time.Now().Add(pongWait)); err != nil {
  908. return err
  909. }
  910. conn.SetPongHandler(func(appData string) error {
  911. log.Trace("%s Received WebSocket pong", logHTTPPrefix(v, r))
  912. return conn.SetReadDeadline(time.Now().Add(pongWait))
  913. })
  914. for {
  915. _, _, err := conn.NextReader()
  916. if err != nil {
  917. return err
  918. }
  919. }
  920. })
  921. g.Go(func() error {
  922. ping := func() error {
  923. wlock.Lock()
  924. defer wlock.Unlock()
  925. if err := conn.SetWriteDeadline(time.Now().Add(wsWriteWait)); err != nil {
  926. return err
  927. }
  928. log.Trace("%s Sending WebSocket ping", logHTTPPrefix(v, r))
  929. return conn.WriteMessage(websocket.PingMessage, nil)
  930. }
  931. for {
  932. select {
  933. case <-ctx.Done():
  934. return nil
  935. case <-time.After(s.config.KeepaliveInterval):
  936. v.Keepalive()
  937. if err := ping(); err != nil {
  938. return err
  939. }
  940. }
  941. }
  942. })
  943. sub := func(v *visitor, msg *message) error {
  944. if !filters.Pass(msg) {
  945. return nil
  946. }
  947. wlock.Lock()
  948. defer wlock.Unlock()
  949. if err := conn.SetWriteDeadline(time.Now().Add(wsWriteWait)); err != nil {
  950. return err
  951. }
  952. return conn.WriteJSON(msg)
  953. }
  954. w.Header().Set("Access-Control-Allow-Origin", "*") // CORS, allow cross-origin requests
  955. if poll {
  956. return s.sendOldMessages(topics, since, scheduled, v, sub)
  957. }
  958. subscriberIDs := make([]int, 0)
  959. for _, t := range topics {
  960. subscriberIDs = append(subscriberIDs, t.Subscribe(sub))
  961. }
  962. defer func() {
  963. for i, subscriberID := range subscriberIDs {
  964. topics[i].Unsubscribe(subscriberID) // Order!
  965. }
  966. }()
  967. if err := sub(v, newOpenMessage(topicsStr)); err != nil { // Send out open message
  968. return err
  969. }
  970. if err := s.sendOldMessages(topics, since, scheduled, v, sub); err != nil {
  971. return err
  972. }
  973. err = g.Wait()
  974. if err != nil && websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
  975. log.Trace("%s WebSocket connection closed: %s", logHTTPPrefix(v, r), err.Error())
  976. return nil // Normal closures are not errors; note: "1006 (abnormal closure)" is treated as normal, because people disconnect a lot
  977. }
  978. return err
  979. }
  980. func parseSubscribeParams(r *http.Request) (poll bool, since sinceMarker, scheduled bool, filters *queryFilter, err error) {
  981. poll = readBoolParam(r, false, "x-poll", "poll", "po")
  982. scheduled = readBoolParam(r, false, "x-scheduled", "scheduled", "sched")
  983. since, err = parseSince(r, poll)
  984. if err != nil {
  985. return
  986. }
  987. filters, err = parseQueryFilters(r)
  988. if err != nil {
  989. return
  990. }
  991. return
  992. }
  993. // sendOldMessages selects old messages from the messageCache and calls sub for each of them. It uses since as the
  994. // marker, returning only messages that are newer than the marker.
  995. func (s *Server) sendOldMessages(topics []*topic, since sinceMarker, scheduled bool, v *visitor, sub subscriber) error {
  996. if since.IsNone() {
  997. return nil
  998. }
  999. messages := make([]*message, 0)
  1000. for _, t := range topics {
  1001. topicMessages, err := s.messageCache.Messages(t.ID, since, scheduled)
  1002. if err != nil {
  1003. return err
  1004. }
  1005. messages = append(messages, topicMessages...)
  1006. }
  1007. sort.Slice(messages, func(i, j int) bool {
  1008. return messages[i].Time < messages[j].Time
  1009. })
  1010. for _, m := range messages {
  1011. if err := sub(v, m); err != nil {
  1012. return err
  1013. }
  1014. }
  1015. return nil
  1016. }
  1017. // parseSince returns a timestamp identifying the time span from which cached messages should be received.
  1018. //
  1019. // Values in the "since=..." parameter can be either a unix timestamp or a duration (e.g. 12h), or
  1020. // "all" for all messages.
  1021. func parseSince(r *http.Request, poll bool) (sinceMarker, error) {
  1022. since := readParam(r, "x-since", "since", "si")
  1023. // Easy cases (empty, all, none)
  1024. if since == "" {
  1025. if poll {
  1026. return sinceAllMessages, nil
  1027. }
  1028. return sinceNoMessages, nil
  1029. } else if since == "all" {
  1030. return sinceAllMessages, nil
  1031. } else if since == "none" {
  1032. return sinceNoMessages, nil
  1033. }
  1034. // ID, timestamp, duration
  1035. if validMessageID(since) {
  1036. return newSinceID(since), nil
  1037. } else if s, err := strconv.ParseInt(since, 10, 64); err == nil {
  1038. return newSinceTime(s), nil
  1039. } else if d, err := time.ParseDuration(since); err == nil {
  1040. return newSinceTime(time.Now().Add(-1 * d).Unix()), nil
  1041. }
  1042. return sinceNoMessages, errHTTPBadRequestSinceInvalid
  1043. }
  1044. func (s *Server) handleOptions(w http.ResponseWriter, _ *http.Request, _ *visitor) error {
  1045. w.Header().Set("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE")
  1046. w.Header().Set("Access-Control-Allow-Origin", "*") // CORS, allow cross-origin requests
  1047. w.Header().Set("Access-Control-Allow-Headers", "*") // CORS, allow auth via JS // FIXME is this terrible?
  1048. return nil
  1049. }
  1050. func (s *Server) topicFromPath(path string) (*topic, error) {
  1051. parts := strings.Split(path, "/")
  1052. if len(parts) < 2 {
  1053. return nil, errHTTPBadRequestTopicInvalid
  1054. }
  1055. topics, err := s.topicsFromIDs(parts[1])
  1056. if err != nil {
  1057. return nil, err
  1058. }
  1059. return topics[0], nil
  1060. }
  1061. func (s *Server) topicsFromPath(path string) ([]*topic, string, error) {
  1062. parts := strings.Split(path, "/")
  1063. if len(parts) < 2 {
  1064. return nil, "", errHTTPBadRequestTopicInvalid
  1065. }
  1066. topicIDs := util.SplitNoEmpty(parts[1], ",")
  1067. topics, err := s.topicsFromIDs(topicIDs...)
  1068. if err != nil {
  1069. return nil, "", errHTTPBadRequestTopicInvalid
  1070. }
  1071. return topics, parts[1], nil
  1072. }
  1073. func (s *Server) topicsFromIDs(ids ...string) ([]*topic, error) {
  1074. s.mu.Lock()
  1075. defer s.mu.Unlock()
  1076. topics := make([]*topic, 0)
  1077. for _, id := range ids {
  1078. if util.Contains(disallowedTopics, id) {
  1079. return nil, errHTTPBadRequestTopicDisallowed
  1080. }
  1081. if _, ok := s.topics[id]; !ok {
  1082. if len(s.topics) >= s.config.TotalTopicLimit {
  1083. return nil, errHTTPTooManyRequestsLimitTotalTopics
  1084. }
  1085. s.topics[id] = newTopic(id)
  1086. }
  1087. topics = append(topics, s.topics[id])
  1088. }
  1089. return topics, nil
  1090. }
  1091. func (s *Server) updateStatsAndPrune() {
  1092. log.Debug("Manager: Starting")
  1093. defer log.Debug("Manager: Finished")
  1094. // WARNING: Make sure to only selectively lock with the mutex, and be aware that this
  1095. // there is no mutex for the entire function.
  1096. // Expire visitors from rate visitors map
  1097. s.mu.Lock()
  1098. staleVisitors := 0
  1099. for ip, v := range s.visitors {
  1100. if v.Stale() {
  1101. log.Trace("Deleting stale visitor %s", v.ip)
  1102. delete(s.visitors, ip)
  1103. staleVisitors++
  1104. }
  1105. }
  1106. s.mu.Unlock()
  1107. log.Debug("Manager: Deleted %d stale visitor(s)", staleVisitors)
  1108. // Delete expired attachments
  1109. if s.fileCache != nil && s.config.AttachmentExpiryDuration > 0 {
  1110. olderThan := time.Now().Add(-1 * s.config.AttachmentExpiryDuration)
  1111. ids, err := s.fileCache.Expired(olderThan)
  1112. if err != nil {
  1113. log.Warn("Error retrieving expired attachments: %s", err.Error())
  1114. } else if len(ids) > 0 {
  1115. log.Debug("Manager: Deleting expired attachments: %v", ids)
  1116. if err := s.fileCache.Remove(ids...); err != nil {
  1117. log.Warn("Error deleting attachments: %s", err.Error())
  1118. }
  1119. } else {
  1120. log.Debug("Manager: No expired attachments to delete")
  1121. }
  1122. }
  1123. // Prune message cache
  1124. olderThan := time.Now().Add(-1 * s.config.CacheDuration)
  1125. log.Debug("Manager: Pruning messages older than %s", olderThan.Format("2006-01-02 15:04:05"))
  1126. if err := s.messageCache.Prune(olderThan); err != nil {
  1127. log.Warn("Manager: Error pruning cache: %s", err.Error())
  1128. }
  1129. // Message count per topic
  1130. var messages int
  1131. messageCounts, err := s.messageCache.MessageCounts()
  1132. if err != nil {
  1133. log.Warn("Manager: Cannot get message counts: %s", err.Error())
  1134. messageCounts = make(map[string]int) // Empty, so we can continue
  1135. }
  1136. for _, count := range messageCounts {
  1137. messages += count
  1138. }
  1139. // Remove subscriptions without subscribers
  1140. s.mu.Lock()
  1141. var subscribers int
  1142. for _, t := range s.topics {
  1143. subs := t.SubscribersCount()
  1144. msgs, exists := messageCounts[t.ID]
  1145. if subs == 0 && (!exists || msgs == 0) {
  1146. log.Trace("Deleting empty topic %s", t.ID)
  1147. delete(s.topics, t.ID)
  1148. continue
  1149. }
  1150. subscribers += subs
  1151. }
  1152. s.mu.Unlock()
  1153. // Mail stats
  1154. var receivedMailTotal, receivedMailSuccess, receivedMailFailure int64
  1155. if s.smtpServerBackend != nil {
  1156. receivedMailTotal, receivedMailSuccess, receivedMailFailure = s.smtpServerBackend.Counts()
  1157. }
  1158. var sentMailTotal, sentMailSuccess, sentMailFailure int64
  1159. if s.smtpSender != nil {
  1160. sentMailTotal, sentMailSuccess, sentMailFailure = s.smtpSender.Counts()
  1161. }
  1162. // Print stats
  1163. s.mu.Lock()
  1164. messagesCount, topicsCount, visitorsCount := s.messages, len(s.topics), len(s.visitors)
  1165. s.mu.Unlock()
  1166. log.Info("Stats: %d messages published, %d in cache, %d topic(s) active, %d subscriber(s), %d visitor(s), %d mails received (%d successful, %d failed), %d mails sent (%d successful, %d failed)",
  1167. messagesCount, messages, topicsCount, subscribers, visitorsCount,
  1168. receivedMailTotal, receivedMailSuccess, receivedMailFailure,
  1169. sentMailTotal, sentMailSuccess, sentMailFailure)
  1170. }
  1171. func (s *Server) runSMTPServer() error {
  1172. s.smtpServerBackend = newMailBackend(s.config, s.handle)
  1173. s.smtpServer = smtp.NewServer(s.smtpServerBackend)
  1174. s.smtpServer.Addr = s.config.SMTPServerListen
  1175. s.smtpServer.Domain = s.config.SMTPServerDomain
  1176. s.smtpServer.ReadTimeout = 10 * time.Second
  1177. s.smtpServer.WriteTimeout = 10 * time.Second
  1178. s.smtpServer.MaxMessageBytes = 1024 * 1024 // Must be much larger than message size (headers, multipart, etc.)
  1179. s.smtpServer.MaxRecipients = 1
  1180. s.smtpServer.AllowInsecureAuth = true
  1181. return s.smtpServer.ListenAndServe()
  1182. }
  1183. func (s *Server) runManager() {
  1184. for {
  1185. select {
  1186. case <-time.After(s.config.ManagerInterval):
  1187. s.updateStatsAndPrune()
  1188. case <-s.closeChan:
  1189. return
  1190. }
  1191. }
  1192. }
  1193. func (s *Server) runFirebaseKeepaliver() {
  1194. if s.firebaseClient == nil {
  1195. return
  1196. }
  1197. v := newVisitor(s.config, s.messageCache, netip.IPv4Unspecified(), nil) // Background process, not a real visitor, uses IP 0.0.0.0
  1198. for {
  1199. select {
  1200. case <-time.After(s.config.FirebaseKeepaliveInterval):
  1201. s.sendToFirebase(v, newKeepaliveMessage(firebaseControlTopic))
  1202. case <-time.After(s.config.FirebasePollInterval):
  1203. s.sendToFirebase(v, newKeepaliveMessage(firebasePollTopic))
  1204. case <-s.closeChan:
  1205. return
  1206. }
  1207. }
  1208. }
  1209. func (s *Server) runDelayedSender() {
  1210. for {
  1211. select {
  1212. case <-time.After(s.config.DelayedSenderInterval):
  1213. if err := s.sendDelayedMessages(); err != nil {
  1214. log.Warn("Error sending delayed messages: %s", err.Error())
  1215. }
  1216. case <-s.closeChan:
  1217. return
  1218. }
  1219. }
  1220. }
  1221. func (s *Server) sendDelayedMessages() error {
  1222. messages, err := s.messageCache.MessagesDue()
  1223. if err != nil {
  1224. return err
  1225. }
  1226. for _, m := range messages {
  1227. v := s.visitorFromID(fmt.Sprintf("ip:%s", m.Sender.String()), m.Sender, nil) // FIXME: This is wrong wrong wrong
  1228. if err := s.sendDelayedMessage(v, m); err != nil {
  1229. log.Warn("%s Error sending delayed message: %s", logMessagePrefix(v, m), err.Error())
  1230. }
  1231. }
  1232. return nil
  1233. }
  1234. func (s *Server) sendDelayedMessage(v *visitor, m *message) error {
  1235. log.Debug("%s Sending delayed message", logMessagePrefix(v, m))
  1236. s.mu.Lock()
  1237. t, ok := s.topics[m.Topic] // If no subscribers, just mark message as published
  1238. s.mu.Unlock()
  1239. if ok {
  1240. go func() {
  1241. // We do not rate-limit messages here, since we've rate limited them in the PUT/POST handler
  1242. if err := t.Publish(v, m); err != nil {
  1243. log.Warn("%s Unable to publish message: %v", logMessagePrefix(v, m), err.Error())
  1244. }
  1245. }()
  1246. }
  1247. if s.firebaseClient != nil { // Firebase subscribers may not show up in topics map
  1248. go s.sendToFirebase(v, m)
  1249. }
  1250. if s.config.UpstreamBaseURL != "" {
  1251. go s.forwardPollRequest(v, m)
  1252. }
  1253. if err := s.messageCache.MarkPublished(m); err != nil {
  1254. return err
  1255. }
  1256. return nil
  1257. }
  1258. func (s *Server) limitRequests(next handleFunc) handleFunc {
  1259. return func(w http.ResponseWriter, r *http.Request, v *visitor) error {
  1260. if util.ContainsIP(s.config.VisitorRequestExemptIPAddrs, v.ip) {
  1261. return next(w, r, v)
  1262. } else if err := v.RequestAllowed(); err != nil {
  1263. return errHTTPTooManyRequestsLimitRequests
  1264. }
  1265. return next(w, r, v)
  1266. }
  1267. }
  1268. func (s *Server) ensureWebEnabled(next handleFunc) handleFunc {
  1269. return func(w http.ResponseWriter, r *http.Request, v *visitor) error {
  1270. if !s.config.EnableWeb {
  1271. return errHTTPNotFound
  1272. }
  1273. return next(w, r, v)
  1274. }
  1275. }
  1276. // transformBodyJSON peeks the request body, reads the JSON, and converts it to headers
  1277. // before passing it on to the next handler. This is meant to be used in combination with handlePublish.
  1278. func (s *Server) transformBodyJSON(next handleFunc) handleFunc {
  1279. return func(w http.ResponseWriter, r *http.Request, v *visitor) error {
  1280. body, err := util.Peek(r.Body, s.config.MessageLimit)
  1281. if err != nil {
  1282. return err
  1283. }
  1284. defer r.Body.Close()
  1285. var m publishMessage
  1286. if err := json.NewDecoder(body).Decode(&m); err != nil {
  1287. return errHTTPBadRequestJSONInvalid
  1288. }
  1289. if !topicRegex.MatchString(m.Topic) {
  1290. return errHTTPBadRequestTopicInvalid
  1291. }
  1292. if m.Message == "" {
  1293. m.Message = emptyMessageBody
  1294. }
  1295. r.URL.Path = "/" + m.Topic
  1296. r.Body = io.NopCloser(strings.NewReader(m.Message))
  1297. if m.Title != "" {
  1298. r.Header.Set("X-Title", m.Title)
  1299. }
  1300. if m.Priority != 0 {
  1301. r.Header.Set("X-Priority", fmt.Sprintf("%d", m.Priority))
  1302. }
  1303. if m.Tags != nil && len(m.Tags) > 0 {
  1304. r.Header.Set("X-Tags", strings.Join(m.Tags, ","))
  1305. }
  1306. if m.Attach != "" {
  1307. r.Header.Set("X-Attach", m.Attach)
  1308. }
  1309. if m.Filename != "" {
  1310. r.Header.Set("X-Filename", m.Filename)
  1311. }
  1312. if m.Click != "" {
  1313. r.Header.Set("X-Click", m.Click)
  1314. }
  1315. if m.Icon != "" {
  1316. r.Header.Set("X-Icon", m.Icon)
  1317. }
  1318. if len(m.Actions) > 0 {
  1319. actionsStr, err := json.Marshal(m.Actions)
  1320. if err != nil {
  1321. return errHTTPBadRequestJSONInvalid
  1322. }
  1323. r.Header.Set("X-Actions", string(actionsStr))
  1324. }
  1325. if m.Email != "" {
  1326. r.Header.Set("X-Email", m.Email)
  1327. }
  1328. if m.Delay != "" {
  1329. r.Header.Set("X-Delay", m.Delay)
  1330. }
  1331. return next(w, r, v)
  1332. }
  1333. }
  1334. func (s *Server) transformMatrixJSON(next handleFunc) handleFunc {
  1335. return func(w http.ResponseWriter, r *http.Request, v *visitor) error {
  1336. newRequest, err := newRequestFromMatrixJSON(r, s.config.BaseURL, s.config.MessageLimit)
  1337. if err != nil {
  1338. return err
  1339. }
  1340. if err := next(w, newRequest, v); err != nil {
  1341. return &errMatrix{pushKey: newRequest.Header.Get(matrixPushKeyHeader), err: err}
  1342. }
  1343. return nil
  1344. }
  1345. }
  1346. func (s *Server) authorizeTopicWrite(next handleFunc) handleFunc {
  1347. return s.autorizeTopic(next, auth.PermissionWrite)
  1348. }
  1349. func (s *Server) authorizeTopicRead(next handleFunc) handleFunc {
  1350. return s.autorizeTopic(next, auth.PermissionRead)
  1351. }
  1352. func (s *Server) autorizeTopic(next handleFunc, perm auth.Permission) handleFunc {
  1353. return func(w http.ResponseWriter, r *http.Request, v *visitor) error {
  1354. if s.auth == nil {
  1355. return next(w, r, v)
  1356. }
  1357. topics, _, err := s.topicsFromPath(r.URL.Path)
  1358. if err != nil {
  1359. return err
  1360. }
  1361. for _, t := range topics {
  1362. if err := s.auth.Authorize(v.user, t.ID, perm); err != nil {
  1363. log.Info("unauthorized: %s", err.Error())
  1364. return errHTTPForbidden
  1365. }
  1366. }
  1367. return next(w, r, v)
  1368. }
  1369. }
  1370. // extractUserPass reads the username/password from the basic auth header (Authorization: Basic ...),
  1371. // or from the ?auth=... query param. The latter is required only to support the WebSocket JavaScript
  1372. // class, which does not support passing headers during the initial request. The auth query param
  1373. // is effectively double base64 encoded. Its format is base64(Basic base64(user:pass)).
  1374. func extractUserPass(r *http.Request) (username string, password string, ok bool) {
  1375. username, password, ok = r.BasicAuth()
  1376. if ok {
  1377. return
  1378. }
  1379. authParam := readQueryParam(r, "authorization", "auth")
  1380. if authParam != "" {
  1381. a, err := base64.RawURLEncoding.DecodeString(authParam)
  1382. if err != nil {
  1383. return
  1384. }
  1385. r.Header.Set("Authorization", string(a))
  1386. return r.BasicAuth()
  1387. }
  1388. return
  1389. }
  1390. // visitor creates or retrieves a rate.Limiter for the given visitor.
  1391. // Note that this function will always return a visitor, even if an error occurs.
  1392. func (s *Server) visitor(r *http.Request) (v *visitor, err error) {
  1393. ip := s.extractIPAddress(r)
  1394. visitorID := fmt.Sprintf("ip:%s", ip.String())
  1395. var user *auth.User // may stay nil if no auth header!
  1396. if user, err = s.authenticate(r); err != nil {
  1397. log.Debug("authentication failed: %s", err.Error())
  1398. err = errHTTPUnauthorized // Always return visitor, even when error occurs!
  1399. }
  1400. if user != nil {
  1401. visitorID = fmt.Sprintf("user:%s", user.Name)
  1402. }
  1403. v = s.visitorFromID(visitorID, ip, user)
  1404. v.user = user // Update user -- FIXME this is ugly, do "newVisitorFromUser" instead
  1405. return v, err // Always return visitor, even when error occurs!
  1406. }
  1407. func (s *Server) authenticate(r *http.Request) (user *auth.User, err error) {
  1408. value := r.Header.Get("Authorization")
  1409. queryParam := readQueryParam(r, "authorization", "auth")
  1410. if queryParam != "" {
  1411. a, err := base64.RawURLEncoding.DecodeString(queryParam)
  1412. if err != nil {
  1413. return nil, err
  1414. }
  1415. value = string(a)
  1416. }
  1417. if value == "" {
  1418. return nil, nil
  1419. }
  1420. if strings.HasPrefix(value, "Bearer") {
  1421. return s.authenticateBearerAuth(value)
  1422. }
  1423. return s.authenticateBasicAuth(r, value)
  1424. }
  1425. func (s *Server) authenticateBasicAuth(r *http.Request, value string) (user *auth.User, err error) {
  1426. r.Header.Set("Authorization", value)
  1427. username, password, ok := r.BasicAuth()
  1428. if !ok {
  1429. return nil, errors.New("invalid basic auth")
  1430. }
  1431. return s.auth.Authenticate(username, password)
  1432. }
  1433. func (s *Server) authenticateBearerAuth(value string) (user *auth.User, err error) {
  1434. token := strings.TrimSpace(strings.TrimPrefix(value, "Bearer"))
  1435. return s.auth.AuthenticateToken(token)
  1436. }
  1437. func (s *Server) visitorFromID(visitorID string, ip netip.Addr, user *auth.User) *visitor {
  1438. s.mu.Lock()
  1439. defer s.mu.Unlock()
  1440. v, exists := s.visitors[visitorID]
  1441. if !exists {
  1442. s.visitors[visitorID] = newVisitor(s.config, s.messageCache, ip, user)
  1443. return s.visitors[visitorID]
  1444. }
  1445. v.Keepalive()
  1446. return v
  1447. }
  1448. func (s *Server) extractIPAddress(r *http.Request) netip.Addr {
  1449. remoteAddr := r.RemoteAddr
  1450. addrPort, err := netip.ParseAddrPort(remoteAddr)
  1451. ip := addrPort.Addr()
  1452. if err != nil {
  1453. // This should not happen in real life; only in tests. So, using falling back to 0.0.0.0 if address unspecified
  1454. ip, err = netip.ParseAddr(remoteAddr)
  1455. if err != nil {
  1456. ip = netip.IPv4Unspecified()
  1457. log.Warn("unable to parse IP (%s), new visitor with unspecified IP (0.0.0.0) created %s", remoteAddr, err)
  1458. }
  1459. }
  1460. if s.config.BehindProxy && strings.TrimSpace(r.Header.Get("X-Forwarded-For")) != "" {
  1461. // X-Forwarded-For can contain multiple addresses (see #328). If we are behind a proxy,
  1462. // only the right-most address can be trusted (as this is the one added by our proxy server).
  1463. // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For for details.
  1464. ips := util.SplitNoEmpty(r.Header.Get("X-Forwarded-For"), ",")
  1465. realIP, err := netip.ParseAddr(strings.TrimSpace(util.LastString(ips, remoteAddr)))
  1466. if err != nil {
  1467. log.Error("invalid IP address %s received in X-Forwarded-For header: %s", ip, err.Error())
  1468. // Fall back to regular remote address if X-Forwarded-For is damaged
  1469. } else {
  1470. ip = realIP
  1471. }
  1472. }
  1473. return ip
  1474. }