WIP: mail publish
This commit is contained in:
		
							parent
							
								
									43a2acb756
								
							
						
					
					
						commit
						3001e57bcc
					
				
					 4 changed files with 130 additions and 0 deletions
				
			
		
							
								
								
									
										2
									
								
								go.mod
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
										
									
									
									
								
							| 
						 | 
					@ -8,6 +8,7 @@ require (
 | 
				
			||||||
	firebase.google.com/go v3.13.0+incompatible
 | 
						firebase.google.com/go v3.13.0+incompatible
 | 
				
			||||||
	github.com/BurntSushi/toml v0.4.1 // indirect
 | 
						github.com/BurntSushi/toml v0.4.1 // indirect
 | 
				
			||||||
	github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
 | 
						github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
 | 
				
			||||||
 | 
						github.com/emersion/go-smtp v0.15.0
 | 
				
			||||||
	github.com/mattn/go-sqlite3 v1.14.9
 | 
						github.com/mattn/go-sqlite3 v1.14.9
 | 
				
			||||||
	github.com/olebedev/when v0.0.0-20211212231525-59bd4edcf9d6
 | 
						github.com/olebedev/when v0.0.0-20211212231525-59bd4edcf9d6
 | 
				
			||||||
	github.com/stretchr/testify v1.7.0
 | 
						github.com/stretchr/testify v1.7.0
 | 
				
			||||||
| 
						 | 
					@ -26,6 +27,7 @@ require (
 | 
				
			||||||
	github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 // indirect
 | 
						github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 // indirect
 | 
				
			||||||
	github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490 // indirect
 | 
						github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490 // indirect
 | 
				
			||||||
	github.com/davecgh/go-spew v1.1.1 // indirect
 | 
						github.com/davecgh/go-spew v1.1.1 // indirect
 | 
				
			||||||
 | 
						github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 // indirect
 | 
				
			||||||
	github.com/envoyproxy/go-control-plane v0.10.1 // indirect
 | 
						github.com/envoyproxy/go-control-plane v0.10.1 // indirect
 | 
				
			||||||
	github.com/envoyproxy/protoc-gen-validate v0.6.2 // indirect
 | 
						github.com/envoyproxy/protoc-gen-validate v0.6.2 // indirect
 | 
				
			||||||
	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 | 
						github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										4
									
								
								go.sum
									
										
									
									
									
								
							
							
						
						
									
										4
									
								
								go.sum
									
										
									
									
									
								
							| 
						 | 
					@ -89,6 +89,10 @@ github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
 | 
				
			||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
					github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
				
			||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
					github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
				
			||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
					github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
				
			||||||
 | 
					github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 h1:OJyUGMJTzHTd1XQp98QTaHernxMYzRaOasRir9hUlFQ=
 | 
				
			||||||
 | 
					github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
 | 
				
			||||||
 | 
					github.com/emersion/go-smtp v0.15.0 h1:3+hMGMGrqP/lqd7qoxZc1hTU8LY8gHV9RFGWlqSDmP8=
 | 
				
			||||||
 | 
					github.com/emersion/go-smtp v0.15.0/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
 | 
				
			||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 | 
					github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 | 
				
			||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 | 
					github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 | 
				
			||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
 | 
					github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										102
									
								
								server/mailserver.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								server/mailserver.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,102 @@
 | 
				
			||||||
 | 
					package server
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"github.com/emersion/go-smtp"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/http/httptest"
 | 
				
			||||||
 | 
						"net/mail"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// mailBackend implements SMTP server methods.
 | 
				
			||||||
 | 
					type mailBackend struct {
 | 
				
			||||||
 | 
						s *Server
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *mailBackend) Login(state *smtp.ConnectionState, username, password string) (smtp.Session, error) {
 | 
				
			||||||
 | 
						return &Session{s: b.s}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *mailBackend) AnonymousLogin(state *smtp.ConnectionState) (smtp.Session, error) {
 | 
				
			||||||
 | 
						return &Session{s: b.s}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Session is returned after EHLO.
 | 
				
			||||||
 | 
					type Session struct {
 | 
				
			||||||
 | 
						s        *Server
 | 
				
			||||||
 | 
						from, to string
 | 
				
			||||||
 | 
						mu       sync.Mutex
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *Session) AuthPlain(username, password string) error {
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *Session) Mail(from string, opts smtp.MailOptions) error {
 | 
				
			||||||
 | 
						s.mu.Lock()
 | 
				
			||||||
 | 
						defer s.mu.Unlock()
 | 
				
			||||||
 | 
						s.from = from
 | 
				
			||||||
 | 
						log.Println("Mail from:", from)
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *Session) Rcpt(to string) error {
 | 
				
			||||||
 | 
						s.mu.Lock()
 | 
				
			||||||
 | 
						defer s.mu.Unlock()
 | 
				
			||||||
 | 
						s.to = to
 | 
				
			||||||
 | 
						log.Println("Rcpt to:", to)
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *Session) Data(r io.Reader) error {
 | 
				
			||||||
 | 
						s.mu.Lock()
 | 
				
			||||||
 | 
						defer s.mu.Unlock()
 | 
				
			||||||
 | 
						b, err := ioutil.ReadAll(r)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.Println("Data:", string(b))
 | 
				
			||||||
 | 
						msg, err := mail.ReadMessage(bytes.NewReader(b))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						body, err := io.ReadAll(msg.Body)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						topic := strings.TrimSuffix(s.to, "@ntfy.sh")
 | 
				
			||||||
 | 
						url := fmt.Sprintf("%s/%s", s.s.config.BaseURL, topic)
 | 
				
			||||||
 | 
						req, err := http.NewRequest("PUT", url, bytes.NewReader(body))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						subject := msg.Header.Get("Subject")
 | 
				
			||||||
 | 
						if subject != "" {
 | 
				
			||||||
 | 
							req.Header.Set("Title", subject)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						rr := httptest.NewRecorder()
 | 
				
			||||||
 | 
						s.s.handle(rr, req)
 | 
				
			||||||
 | 
						if rr.Code != http.StatusOK {
 | 
				
			||||||
 | 
							return errors.New("error: " + rr.Body.String())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *Session) Reset() {
 | 
				
			||||||
 | 
						s.mu.Lock()
 | 
				
			||||||
 | 
						s.from = ""
 | 
				
			||||||
 | 
						s.to = ""
 | 
				
			||||||
 | 
						s.mu.Unlock()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *Session) Logout() error {
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -8,6 +8,7 @@ import (
 | 
				
			||||||
	firebase "firebase.google.com/go"
 | 
						firebase "firebase.google.com/go"
 | 
				
			||||||
	"firebase.google.com/go/messaging"
 | 
						"firebase.google.com/go/messaging"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"github.com/emersion/go-smtp"
 | 
				
			||||||
	"google.golang.org/api/option"
 | 
						"google.golang.org/api/option"
 | 
				
			||||||
	"heckel.io/ntfy/util"
 | 
						"heckel.io/ntfy/util"
 | 
				
			||||||
	"html/template"
 | 
						"html/template"
 | 
				
			||||||
| 
						 | 
					@ -238,10 +239,16 @@ func (s *Server) Run() error {
 | 
				
			||||||
			errChan <- s.httpsServer.ListenAndServeTLS(s.config.CertFile, s.config.KeyFile)
 | 
								errChan <- s.httpsServer.ListenAndServeTLS(s.config.CertFile, s.config.KeyFile)
 | 
				
			||||||
		}()
 | 
							}()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if true {
 | 
				
			||||||
 | 
							go func() {
 | 
				
			||||||
 | 
								errChan <- s.mailserver()
 | 
				
			||||||
 | 
							}()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	s.mu.Unlock()
 | 
						s.mu.Unlock()
 | 
				
			||||||
	go s.runManager()
 | 
						go s.runManager()
 | 
				
			||||||
	go s.runAtSender()
 | 
						go s.runAtSender()
 | 
				
			||||||
	go s.runFirebaseKeepliver()
 | 
						go s.runFirebaseKeepliver()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return <-errChan
 | 
						return <-errChan
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -722,6 +729,21 @@ func (s *Server) updateStatsAndPrune() {
 | 
				
			||||||
		s.messages, len(s.topics), subscribers, messages, len(s.visitors))
 | 
							s.messages, len(s.topics), subscribers, messages, len(s.visitors))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *Server) mailserver() error {
 | 
				
			||||||
 | 
						ms := smtp.NewServer(&mailBackend{s})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ms.Addr = ":1025"
 | 
				
			||||||
 | 
						ms.Domain = "localhost"
 | 
				
			||||||
 | 
						ms.ReadTimeout = 10 * time.Second
 | 
				
			||||||
 | 
						ms.WriteTimeout = 10 * time.Second
 | 
				
			||||||
 | 
						ms.MaxMessageBytes = 1024 * 1024
 | 
				
			||||||
 | 
						ms.MaxRecipients = 50
 | 
				
			||||||
 | 
						ms.AllowInsecureAuth = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.Println("Starting server at", ms.Addr)
 | 
				
			||||||
 | 
						return ms.ListenAndServe()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *Server) runManager() {
 | 
					func (s *Server) runManager() {
 | 
				
			||||||
	for {
 | 
						for {
 | 
				
			||||||
		select {
 | 
							select {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue