Compare commits
15 Commits
4b1b194000
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbc173a78f | ||
|
|
8e40f85166 | ||
|
|
1b74eb0d16 | ||
|
|
5a4ce70451 | ||
|
|
14036702ee | ||
|
|
13d2acdf16 | ||
|
|
cae30cc5d1 | ||
|
|
bb4b7b4da8 | ||
|
|
deb6c2d196 | ||
|
|
5191add42b | ||
|
|
e3d5a7e221 | ||
|
|
7d372adb78 | ||
|
|
6b04a324f3 | ||
|
|
48cb9bf32b | ||
|
|
2313cc3ac5 |
@@ -3,7 +3,7 @@ package contact
|
|||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"gogs.inhome.blapointe.com/local/system/sysconf"
|
"gitea.inhome.blapointe.com/local/system/sysconf"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/smtp"
|
"net/smtp"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package contact
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"gogs.inhome.blapointe.com/local/encryptor"
|
"gitea.inhome.blapointe.com/local/encryptor"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|||||||
18
cmd/go.mod
18
cmd/go.mod
@@ -1,18 +0,0 @@
|
|||||||
module gogs.inhome.blapointe.com/local-sandbox/contact
|
|
||||||
|
|
||||||
go 1.20
|
|
||||||
|
|
||||||
replace gogs.inhome.blapointe.com/local-sandbox/contact/contact => ./contact
|
|
||||||
|
|
||||||
require (
|
|
||||||
gogs.inhome.blapointe.com/local-sandbox/contact/contact v0.0.0-00010101000000-000000000000
|
|
||||||
gogs.inhome.blapointe.com/local/args v0.0.0-20230410154220-44370f257b34
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/bytbox/go-pop3 v0.0.0-20120201222208-3046caf0763e // indirect
|
|
||||||
github.com/emersion/go-imap v1.2.0 // indirect
|
|
||||||
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 // indirect
|
|
||||||
golang.org/x/text v0.3.7 // indirect
|
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
|
||||||
)
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
wcBMA1Z2i1z86bGuAQgAZ1hb8CH2dcUuKWfstpILJqaonxEsinE2KgrzqHjEtogawL8pqNCWXPYgFxPh2sj4z9zI4/7+jcAcd3wz/mXj/FdaBUpFyMaC1cV9OmD8jSTvamBpG5KI2HHxuHRv0Y19rgjcXUjtZSJcu1ZYj6yMn2+KdDTHScRBXsiapOHc5S9nKrXJ69cFF7jvQRIgnn8KNVWmcau5BXKYrXa9XnwqZtNTFYBaPgyOWb8TMnBK93T4NegMTRj/XeGQ4NAT/5sfHNRvC84mYxZQFojuUTnT3zVgdET7o6AKrtsbHstL8mzaPjM9nMPZfTotSCzsXBE07Dt1zLmPXeo4eT0VyOUtpdLgAeMsLYZM1RdSXeECzuBk4EThv3zgP+IDH09o4OzkfKlNi862TzMNh6rMpCvBGuA14x4vOzvLsDLZ4MDiagQM2eCA4LzgQeQO24U6HvCz7cdAy22h7BqG4pOs1Hfhj8oA
|
|
||||||
48
cmd/recv.go
48
cmd/recv.go
@@ -1,48 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"gogs.inhome.blapointe.com/local-sandbox/contact/contact"
|
|
||||||
"gogs.inhome.blapointe.com/local/args"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
emailer := contact.NewEmailer()
|
|
||||||
as := args.NewArgSet()
|
|
||||||
as.Append(args.STRING, "imap", "imap server:port", "imap.gmail.com:993")
|
|
||||||
as.Append(args.INT, "n", "limit (<1 for inf)", 10)
|
|
||||||
if err := as.Parse(); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
emailer.IMAP = as.Get("imap").GetString()
|
|
||||||
emailer.Limit = as.Get("n").GetInt()
|
|
||||||
|
|
||||||
msgs, err := emailer.Read()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
for msg := range msgs {
|
|
||||||
b, _ := ioutil.ReadAll(io.LimitReader(msg.Body, 1024))
|
|
||||||
s := strings.ReplaceAll(string(b), "\r\n", " ")
|
|
||||||
s = strings.ReplaceAll(string(s), "\n", " ")
|
|
||||||
s = strings.ReplaceAll(string(s), "\r", " ")
|
|
||||||
if !strings.Contains(s, " ") {
|
|
||||||
s = "..."
|
|
||||||
}
|
|
||||||
d, _ := msg.Header.Date()
|
|
||||||
d = d.In(time.Local)
|
|
||||||
fmt.Printf(
|
|
||||||
"@%+v @%+v: %+v: %s\n",
|
|
||||||
d.Format("06-01-02T15:04Z07"),
|
|
||||||
msg.Header.Get("From"),
|
|
||||||
msg.Header.Get("Subject"),
|
|
||||||
s,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
21
cmd/recv/go.mod
Normal file
21
cmd/recv/go.mod
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
module gitea.inhome.blapointe.com/contact/cmd/recv
|
||||||
|
|
||||||
|
go 1.24.0
|
||||||
|
|
||||||
|
replace gitea.inhome.blapointe.com/local-sandbox/contact => ../../
|
||||||
|
|
||||||
|
require (
|
||||||
|
gitea.inhome.blapointe.com/local-sandbox/contact v0.0.0-00010101000000-000000000000
|
||||||
|
gitea.inhome.blapointe.com/local/args v0.0.0-20231109145953-eb2e1c1b8d56
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
cloud.google.com/go/compute/metadata v0.3.0 // indirect
|
||||||
|
github.com/bytbox/go-pop3 v0.0.0-20120201222208-3046caf0763e // indirect
|
||||||
|
github.com/emersion/go-imap v1.2.0 // indirect
|
||||||
|
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
golang.org/x/oauth2 v0.35.0 // indirect
|
||||||
|
golang.org/x/text v0.3.7 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
)
|
||||||
3
cmd/recv/go.sh
Executable file
3
cmd/recv/go.sh
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#! /bin/sh
|
||||||
|
|
||||||
|
GOPRIVATE= GONOSUMDB=\* GONOPROXY=\* GOPROXY= go "$@"
|
||||||
26
cmd/recv/go.sum
Normal file
26
cmd/recv/go.sum
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
|
||||||
|
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
||||||
|
gitea.inhome.blapointe.com/local/args v0.0.0-20231109145953-eb2e1c1b8d56 h1:zTGGZ77KLFagqUvDSgTOnm0qF+iSLwQWiEtGjb2jjlY=
|
||||||
|
gitea.inhome.blapointe.com/local/args v0.0.0-20231109145953-eb2e1c1b8d56/go.mod h1:SqCOE3bE3wvrztVIQGHuyxHKfDjRKU9EWhBdkmkiwyc=
|
||||||
|
github.com/bytbox/go-pop3 v0.0.0-20120201222208-3046caf0763e h1:mQTN05gz0rDZSABqKMzAPMb5ATWcvvdMljRzEh0LjBo=
|
||||||
|
github.com/bytbox/go-pop3 v0.0.0-20120201222208-3046caf0763e/go.mod h1:alXX+s7a4cKaIprgjeEboqi4Tm7XR/HXEwUTxUV/ywU=
|
||||||
|
github.com/emersion/go-imap v1.2.0 h1:lyUQ3+EVM21/qbWE/4Ya5UG9r5+usDxlg4yfp3TgHFA=
|
||||||
|
github.com/emersion/go-imap v1.2.0/go.mod h1:Qlx1FSx2FTxjnjWpIlVNEuX+ylerZQNFE5NsmKFSejY=
|
||||||
|
github.com/emersion/go-message v0.15.0/go.mod h1:wQUEfE+38+7EW8p8aZ96ptg6bAb1iwdgej19uXASlE4=
|
||||||
|
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-sasl v0.0.0-20241020182733-b788ff22d5a6 h1:oP4q0fw+fOSWn3DfFi4EXdT+B+gTtzx8GC9xsc26Znk=
|
||||||
|
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
||||||
|
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ=
|
||||||
|
golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
85
cmd/recv/main.go
Executable file
85
cmd/recv/main.go
Executable file
@@ -0,0 +1,85 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/mail"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitea.inhome.blapointe.com/local-sandbox/contact"
|
||||||
|
"gitea.inhome.blapointe.com/local/args"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
emailer := contact.NewEmailer()
|
||||||
|
as := args.NewArgSet()
|
||||||
|
as.Append(args.STRING, "imap", "imap server:port", "imap.gmail.com:993")
|
||||||
|
as.Append(args.STRING, "pop3", "pop3 server:port", "")
|
||||||
|
as.Append(args.INT, "n", "limit (<1 for inf)", 10)
|
||||||
|
as.Append(args.STRING, "u", "username", emailer.From)
|
||||||
|
as.Append(args.STRING, "p", "password", emailer.Password)
|
||||||
|
as.Append(args.INT, "b", "dont read more than this many characters", 4096)
|
||||||
|
as.Append(args.BOOL, "json", "output as json", false)
|
||||||
|
as.Append(args.STRING, "oauth", "", emailer.OAuth)
|
||||||
|
as.Append(args.STRING, "inbox", "", emailer.Inbox)
|
||||||
|
if err := as.Parse(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
emailer.IMAP = as.Get("imap").GetString()
|
||||||
|
emailer.POP3 = as.Get("pop3").GetString()
|
||||||
|
emailer.Limit = as.Get("n").GetInt()
|
||||||
|
emailer.From = as.GetString("u")
|
||||||
|
emailer.Password = as.GetString("p")
|
||||||
|
emailer.OAuth = as.GetString("oauth")
|
||||||
|
emailer.Inbox = as.GetString("inbox")
|
||||||
|
|
||||||
|
var msgs <-chan *mail.Message
|
||||||
|
var err error
|
||||||
|
if emailer.POP3 != "" {
|
||||||
|
msgs, err = emailer.ReadPOP3()
|
||||||
|
} else {
|
||||||
|
msgs, err = emailer.Read()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
emails := make([]map[string]any, 0, emailer.Limit)
|
||||||
|
for msg := range msgs {
|
||||||
|
b, _ := ioutil.ReadAll(io.LimitReader(msg.Body, int64(as.GetInt("b"))))
|
||||||
|
s := strings.ReplaceAll(string(b), "\r\n", "\n")
|
||||||
|
s = strings.ReplaceAll(string(s), "\n", "\n")
|
||||||
|
s = strings.ReplaceAll(string(s), "\r", "\n")
|
||||||
|
if !strings.Contains(s, " ") {
|
||||||
|
s = "..."
|
||||||
|
}
|
||||||
|
d, _ := msg.Header.Date()
|
||||||
|
d = d.In(time.Local)
|
||||||
|
if as.GetBool("json") {
|
||||||
|
emails = append(emails, map[string]any{
|
||||||
|
"t": d.Format("06-01-02T15:04Z07"),
|
||||||
|
"from": msg.Header.Get("From"),
|
||||||
|
"subject": msg.Header.Get("Subject"),
|
||||||
|
"body": s,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
fmt.Printf(
|
||||||
|
"@%+v @%+v: \n\t%+v: \n\t%s\n",
|
||||||
|
d.Format("06-01-02T15:04Z07"),
|
||||||
|
msg.Header.Get("From"),
|
||||||
|
msg.Header.Get("Subject"),
|
||||||
|
s,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if as.GetBool("json") {
|
||||||
|
enc := json.NewEncoder(os.Stdout)
|
||||||
|
enc.SetIndent("", " ")
|
||||||
|
enc.Encode(emails)
|
||||||
|
}
|
||||||
|
}
|
||||||
18
cmd/send/go.mod
Normal file
18
cmd/send/go.mod
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
module gitea.inhome.blapointe.com/contact/cmd/send
|
||||||
|
|
||||||
|
go 1.21.0
|
||||||
|
|
||||||
|
replace gitea.inhome.blapointe.com/local-sandbox/contact => ../../
|
||||||
|
|
||||||
|
require (
|
||||||
|
gitea.inhome.blapointe.com/local-sandbox/contact v0.0.1
|
||||||
|
gitea.inhome.blapointe.com/local/args v0.0.0-20231109145953-eb2e1c1b8d56
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/bytbox/go-pop3 v0.0.0-20120201222208-3046caf0763e // indirect
|
||||||
|
github.com/emersion/go-imap v1.2.0 // indirect
|
||||||
|
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 // indirect
|
||||||
|
golang.org/x/text v0.3.7 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
)
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
gitea.inhome.blapointe.com/local/args v0.0.0-20231109145953-eb2e1c1b8d56 h1:zTGGZ77KLFagqUvDSgTOnm0qF+iSLwQWiEtGjb2jjlY=
|
||||||
|
gitea.inhome.blapointe.com/local/args v0.0.0-20231109145953-eb2e1c1b8d56/go.mod h1:SqCOE3bE3wvrztVIQGHuyxHKfDjRKU9EWhBdkmkiwyc=
|
||||||
github.com/bytbox/go-pop3 v0.0.0-20120201222208-3046caf0763e h1:mQTN05gz0rDZSABqKMzAPMb5ATWcvvdMljRzEh0LjBo=
|
github.com/bytbox/go-pop3 v0.0.0-20120201222208-3046caf0763e h1:mQTN05gz0rDZSABqKMzAPMb5ATWcvvdMljRzEh0LjBo=
|
||||||
github.com/bytbox/go-pop3 v0.0.0-20120201222208-3046caf0763e/go.mod h1:alXX+s7a4cKaIprgjeEboqi4Tm7XR/HXEwUTxUV/ywU=
|
github.com/bytbox/go-pop3 v0.0.0-20120201222208-3046caf0763e/go.mod h1:alXX+s7a4cKaIprgjeEboqi4Tm7XR/HXEwUTxUV/ywU=
|
||||||
github.com/emersion/go-imap v1.2.0 h1:lyUQ3+EVM21/qbWE/4Ya5UG9r5+usDxlg4yfp3TgHFA=
|
github.com/emersion/go-imap v1.2.0 h1:lyUQ3+EVM21/qbWE/4Ya5UG9r5+usDxlg4yfp3TgHFA=
|
||||||
@@ -6,8 +8,6 @@ github.com/emersion/go-message v0.15.0/go.mod h1:wQUEfE+38+7EW8p8aZ96ptg6bAb1iwd
|
|||||||
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 h1:OJyUGMJTzHTd1XQp98QTaHernxMYzRaOasRir9hUlFQ=
|
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-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
||||||
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
|
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
|
||||||
gogs.inhome.blapointe.com/local/args v0.0.0-20230410154220-44370f257b34 h1:0tuX5dfOksiOQD1vbJjVNVTVxTTIng7UrUdSLF5T+Ao=
|
|
||||||
gogs.inhome.blapointe.com/local/args v0.0.0-20230410154220-44370f257b34/go.mod h1:YG9n3Clg7683ohkVnJK2hdX8bBS9EojIsd1qPZumX0Y=
|
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gogs.inhome.blapointe.com/local-sandbox/contact/contact"
|
"gitea.inhome.blapointe.com/local-sandbox/contact"
|
||||||
"gogs.inhome.blapointe.com/local/args"
|
"gitea.inhome.blapointe.com/local/args"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
156
email.go
156
email.go
@@ -1,17 +1,30 @@
|
|||||||
package contact
|
package contact
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto/sha256"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"net/http"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
"net/smtp"
|
"net/smtp"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/bytbox/go-pop3"
|
"github.com/bytbox/go-pop3"
|
||||||
"github.com/emersion/go-imap"
|
"github.com/emersion/go-imap"
|
||||||
"github.com/emersion/go-imap/client"
|
"github.com/emersion/go-imap/client"
|
||||||
|
"github.com/emersion/go-sasl"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"golang.org/x/oauth2/authhandler"
|
||||||
|
"golang.org/x/oauth2/google"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Emailer struct {
|
type Emailer struct {
|
||||||
@@ -21,6 +34,8 @@ type Emailer struct {
|
|||||||
IMAP string
|
IMAP string
|
||||||
Password string
|
Password string
|
||||||
Limit int
|
Limit int
|
||||||
|
OAuth string
|
||||||
|
Inbox string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEmailer() *Emailer {
|
func NewEmailer() *Emailer {
|
||||||
@@ -34,6 +49,8 @@ func NewEmailer() *Emailer {
|
|||||||
From: envOr("FROM", "breellocaldev@gmail.com"),
|
From: envOr("FROM", "breellocaldev@gmail.com"),
|
||||||
SMTP: envOr("SMTP", "smtp.gmail.com:465"),
|
SMTP: envOr("SMTP", "smtp.gmail.com:465"),
|
||||||
Password: envOr("PASSWORD", "lhnjijrvqaesiufp"),
|
Password: envOr("PASSWORD", "lhnjijrvqaesiufp"),
|
||||||
|
OAuth: envOr("OAUTH", ""),
|
||||||
|
Inbox: envOr("INBOX", "INBOX"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,14 +59,116 @@ func (e *Emailer) Read() (chan *mail.Message, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *Emailer) ReadIMAP() (chan *mail.Message, error) {
|
func (e *Emailer) ReadIMAP() (chan *mail.Message, error) {
|
||||||
|
if e.OAuth != "" {
|
||||||
|
return e.oauthThenReadIMAP()
|
||||||
|
}
|
||||||
|
return e.readIMAP(e.From, e.Password)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Emailer) oauthThenReadIMAP() (chan *mail.Message, error) {
|
||||||
|
configJSON, err := e.configJSON()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if token, _ := os.ReadFile(e.OAuth + ".token"); len(token) == 0 {
|
||||||
|
state := uuid.NewString()
|
||||||
|
|
||||||
|
verifier := uuid.NewString()
|
||||||
|
s256 := sha256.Sum256([]byte(verifier))
|
||||||
|
challenge := base64.RawURLEncoding.EncodeToString(s256[:])
|
||||||
|
pkceParams := authhandler.PKCEParams{Challenge: challenge, ChallengeMethod: "S256", Verifier: verifier}
|
||||||
|
config, err := google.ConfigFromJSON([]byte(configJSON), "https://mail.google.com/")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
credentialsParams := google.CredentialsParams{Scopes: config.Scopes, State: state, AuthHandler: func(authCodeURL string) (string, string, error) {
|
||||||
|
fmt.Println(authCodeURL + "&access_type=offline&prompt=consent")
|
||||||
|
var callback string
|
||||||
|
_, err := fmt.Scan(&callback)
|
||||||
|
u, _ := url.Parse(callback)
|
||||||
|
fmt.Println()
|
||||||
|
return u.Query().Get("code"), state, err
|
||||||
|
}, PKCE: &pkceParams}
|
||||||
|
credentials, err := google.CredentialsFromJSONWithParams(context.Background(), []byte(configJSON), credentialsParams)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := credentials.TokenSource.Token()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tokenFileJSON, _ := json.Marshal(token)
|
||||||
|
os.WriteFile(e.OAuth+".token", tokenFileJSON, os.ModePerm)
|
||||||
|
log.Printf("%s", tokenFileJSON)
|
||||||
|
}
|
||||||
|
var tokenStruct struct {
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
RefreshToken string `json:"refresh_token"`
|
||||||
|
Expiry time.Time `json:"expiry"`
|
||||||
|
ExpiresIn int `json:"expires_in"`
|
||||||
|
}
|
||||||
|
token, _ := os.ReadFile(e.OAuth + ".token")
|
||||||
|
json.Unmarshal(token, &tokenStruct)
|
||||||
|
if time.Until(tokenStruct.Expiry) < time.Minute*5 {
|
||||||
|
var appStruct struct {
|
||||||
|
Installed struct {
|
||||||
|
ClientID string `json:"client_id"`
|
||||||
|
ClientSecret string `json:"client_secret"`
|
||||||
|
GrantType string `json:"grant_type"`
|
||||||
|
RefreshToken string `json:"refresh_token"`
|
||||||
|
} `json:"installed"`
|
||||||
|
}
|
||||||
|
json.Unmarshal([]byte(configJSON), &appStruct)
|
||||||
|
appStruct.Installed.GrantType = "refresh_token"
|
||||||
|
appStruct.Installed.RefreshToken = tokenStruct.RefreshToken
|
||||||
|
b, _ := json.Marshal(appStruct.Installed)
|
||||||
|
resp, err := http.Post(`https://oauth2.googleapis.com/token`, `application/json`, bytes.NewReader(b))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
b, _ = io.ReadAll(resp.Body)
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("(%d) %s", resp.StatusCode, b)
|
||||||
|
}
|
||||||
|
tokenStruct.Expiry = time.Time{}
|
||||||
|
json.Unmarshal(b, &tokenStruct)
|
||||||
|
tokenStruct.Expiry = time.Now().Add(time.Duration(tokenStruct.ExpiresIn) * time.Second)
|
||||||
|
tokenStruct.RefreshToken = appStruct.Installed.RefreshToken
|
||||||
|
b, _ = json.Marshal(tokenStruct)
|
||||||
|
os.WriteFile(e.OAuth+".token", b, os.ModePerm)
|
||||||
|
}
|
||||||
|
log.Println("OAUTH", e.From, time.Until(tokenStruct.Expiry), tokenStruct.AccessToken)
|
||||||
|
return e.readIMAP(e.From, tokenStruct.AccessToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Emailer) configJSON() (string, error) {
|
||||||
|
b, err := os.ReadFile(e.OAuth)
|
||||||
|
return string(b), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Emailer) readIMAP(authU, authP string) (chan *mail.Message, error) {
|
||||||
c, err := client.DialTLS(e.IMAP, nil)
|
c, err := client.DialTLS(e.IMAP, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := c.Login(e.From, e.Password); err != nil {
|
if e.OAuth == "" {
|
||||||
return nil, err
|
if err := c.Login(authU, authP); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := c.Authenticate(sasl.NewOAuthBearerClient(&sasl.OAuthBearerOptions{
|
||||||
|
Username: authU,
|
||||||
|
Token: authP,
|
||||||
|
Host: "",
|
||||||
|
Port: 0,
|
||||||
|
})); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mbox, err := c.Select("INBOX", true) //readonly
|
mbox, err := c.Select(e.Inbox, true) //readonly
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -86,23 +205,35 @@ func (e *Emailer) ReadIMAP() (chan *mail.Message, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *Emailer) ReadPOP3() (chan *mail.Message, error) {
|
func (e *Emailer) ReadPOP3() (chan *mail.Message, error) {
|
||||||
emails := []*mail.Message{}
|
limit := e.Limit
|
||||||
|
if limit < 1 {
|
||||||
|
limit = 1000
|
||||||
|
}
|
||||||
|
emails := make(chan *mail.Message, limit)
|
||||||
|
defer close(emails)
|
||||||
|
|
||||||
|
log.Printf("pop3.DialTLS(%s)", e.POP3)
|
||||||
c, err := pop3.DialTLS(e.POP3)
|
c, err := pop3.DialTLS(e.POP3)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer c.Rset()
|
defer c.Rset()
|
||||||
|
|
||||||
|
log.Printf("c.Auth(%s, xyz)", e.From)
|
||||||
if err := c.Auth(e.From, e.Password); err != nil {
|
if err := c.Auth(e.From, e.Password); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("c.ListAll()")
|
||||||
ids, _, err := c.ListAll()
|
ids, _, err := c.ListAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for j, id := range ids {
|
log.Printf("/c.ListAll() = %v", ids)
|
||||||
if e.Limit > 0 && len(ids)-1-j >= e.Limit {
|
|
||||||
break
|
for i := len(ids) - 1; i >= 0; i-- {
|
||||||
}
|
id := ids[i]
|
||||||
|
log.Printf("c.Retr(%v)", id)
|
||||||
raw, err := c.Retr(id)
|
raw, err := c.Retr(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -111,10 +242,13 @@ func (e *Emailer) ReadPOP3() (chan *mail.Message, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
emails = append(emails, msg)
|
select {
|
||||||
|
case emails <- msg:
|
||||||
|
default:
|
||||||
|
return emails, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil, nil
|
return emails, nil
|
||||||
//return emails, nil //c.Quit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Emailer) Send(to, subj, msg string) error {
|
func (e *Emailer) Send(to, subj, msg string) error {
|
||||||
|
|||||||
7
go.mod
7
go.mod
@@ -1,13 +1,16 @@
|
|||||||
module gogs.inhome.blapointe.com/local-sandbox/contact
|
module gitea.inhome.blapointe.com/local-sandbox/contact
|
||||||
|
|
||||||
go 1.17
|
go 1.24.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/bytbox/go-pop3 v0.0.0-20120201222208-3046caf0763e
|
github.com/bytbox/go-pop3 v0.0.0-20120201222208-3046caf0763e
|
||||||
github.com/emersion/go-imap v1.2.0
|
github.com/emersion/go-imap v1.2.0
|
||||||
|
github.com/google/uuid v1.6.0
|
||||||
|
golang.org/x/oauth2 v0.35.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
cloud.google.com/go/compute/metadata v0.3.0 // indirect
|
||||||
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 // indirect
|
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 // indirect
|
||||||
golang.org/x/text v0.3.7 // indirect
|
golang.org/x/text v0.3.7 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
6
go.sum
6
go.sum
@@ -1,3 +1,5 @@
|
|||||||
|
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
|
||||||
|
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
||||||
github.com/bytbox/go-pop3 v0.0.0-20120201222208-3046caf0763e h1:mQTN05gz0rDZSABqKMzAPMb5ATWcvvdMljRzEh0LjBo=
|
github.com/bytbox/go-pop3 v0.0.0-20120201222208-3046caf0763e h1:mQTN05gz0rDZSABqKMzAPMb5ATWcvvdMljRzEh0LjBo=
|
||||||
github.com/bytbox/go-pop3 v0.0.0-20120201222208-3046caf0763e/go.mod h1:alXX+s7a4cKaIprgjeEboqi4Tm7XR/HXEwUTxUV/ywU=
|
github.com/bytbox/go-pop3 v0.0.0-20120201222208-3046caf0763e/go.mod h1:alXX+s7a4cKaIprgjeEboqi4Tm7XR/HXEwUTxUV/ywU=
|
||||||
github.com/emersion/go-imap v1.2.0 h1:lyUQ3+EVM21/qbWE/4Ya5UG9r5+usDxlg4yfp3TgHFA=
|
github.com/emersion/go-imap v1.2.0 h1:lyUQ3+EVM21/qbWE/4Ya5UG9r5+usDxlg4yfp3TgHFA=
|
||||||
@@ -6,6 +8,10 @@ github.com/emersion/go-message v0.15.0/go.mod h1:wQUEfE+38+7EW8p8aZ96ptg6bAb1iwd
|
|||||||
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 h1:OJyUGMJTzHTd1XQp98QTaHernxMYzRaOasRir9hUlFQ=
|
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-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
||||||
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
|
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ=
|
||||||
|
golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
|||||||
Reference in New Issue
Block a user