Initial Commit
This commit is contained in:
parent
11b5c2bc48
commit
87e057b2d7
15
go.mod
Normal file
15
go.mod
Normal file
@ -0,0 +1,15 @@
|
||||
module autoprint
|
||||
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/emersion/go-imap v1.2.1
|
||||
github.com/emersion/go-message v0.15.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 // indirect
|
||||
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
)
|
16
go.sum
Normal file
16
go.sum
Normal file
@ -0,0 +1,16 @@
|
||||
github.com/emersion/go-imap v1.2.1 h1:+s9ZjMEjOB8NzZMVTM3cCenz2JrQIGGo5j1df19WjTA=
|
||||
github.com/emersion/go-imap v1.2.1/go.mod h1:Qlx1FSx2FTxjnjWpIlVNEuX+ylerZQNFE5NsmKFSejY=
|
||||
github.com/emersion/go-message v0.15.0 h1:urgKGqt2JAc9NFJcgncQcohHdiYb803YTH9OQwHBHIY=
|
||||
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-textwrapper v0.0.0-20200911093747-65d896831594 h1:IbFBtwoTQyw0fIM5xv1HF+Y+3ZijDR839WMulgxCcUY=
|
||||
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
|
||||
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=
|
546
mailmonitor.go
Normal file
546
mailmonitor.go
Normal file
@ -0,0 +1,546 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
imap "github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-imap/client"
|
||||
"github.com/emersion/go-message/mail"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
const (
|
||||
layout string = "2006-01-02T15:04:05Z07:00"
|
||||
)
|
||||
|
||||
type Monitor struct {
|
||||
IMAPUri string `yaml:"IMAPUri"`
|
||||
Username string `yaml:"Username"`
|
||||
Password string `yaml:"Password"`
|
||||
SourceFolder string `yaml:"SourceFolder"`
|
||||
DestinationFolder string `yaml:"DestinationFolder"`
|
||||
TLS bool `yaml:"TLS"`
|
||||
DebugLevel int64 `yaml:"DebugLevel"`
|
||||
Client *client.Client
|
||||
FetchSet *imap.SeqSet
|
||||
ReadSet *imap.SeqSet
|
||||
MessageChan chan *imap.Message
|
||||
DoneChan chan error
|
||||
}
|
||||
|
||||
func NewMonitor(IMAPUri, Username, Password, SourceFolder, DestinationFolder string, TLS bool) (Monitor, error) {
|
||||
m := Monitor{
|
||||
IMAPUri: IMAPUri,
|
||||
Username: Username,
|
||||
Password: Password,
|
||||
TLS: TLS,
|
||||
FetchSet: &imap.SeqSet{},
|
||||
ReadSet: &imap.SeqSet{},
|
||||
SourceFolder: SourceFolder,
|
||||
DestinationFolder: DestinationFolder,
|
||||
MessageChan: make(chan *imap.Message, 10),
|
||||
DoneChan: make(chan error),
|
||||
}
|
||||
if TLS {
|
||||
cl, err := client.DialTLS(m.IMAPUri, nil)
|
||||
if err != nil {
|
||||
return m, err
|
||||
}
|
||||
m.Client = cl
|
||||
return m, nil
|
||||
} else {
|
||||
cl, err := client.Dial(m.IMAPUri)
|
||||
if err != nil {
|
||||
return m, err
|
||||
}
|
||||
err = cl.StartTLS(
|
||||
nil,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return m, err
|
||||
}
|
||||
m.Client = cl
|
||||
return m, nil
|
||||
}
|
||||
}
|
||||
|
||||
func NewMonitorFromFile(path string) (Monitor, error) {
|
||||
m := Monitor{
|
||||
FetchSet: &imap.SeqSet{},
|
||||
ReadSet: &imap.SeqSet{},
|
||||
MessageChan: make(chan *imap.Message, 10),
|
||||
DoneChan: make(chan error),
|
||||
}
|
||||
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
return m, errors.New("File does not exist\n")
|
||||
}
|
||||
|
||||
f, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return m, errors.New("File can't be opened\n")
|
||||
}
|
||||
|
||||
err = yaml.Unmarshal(f, &m)
|
||||
if err != nil {
|
||||
return m, errors.New("File can't be read\n")
|
||||
}
|
||||
log.Printf("Accessing E-Mail-Account %s on %s and moving unread Messages from %s to %s", m.Username, m.IMAPUri, m.SourceFolder, m.DestinationFolder)
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m *Monitor) Login() error {
|
||||
if m.DebugLevel > 0 {
|
||||
log.Printf("Logging in as %s on %s. TLS: %t\n", m.Username, m.IMAPUri, m.TLS)
|
||||
}
|
||||
if m.TLS {
|
||||
if m.DebugLevel > 0 {
|
||||
log.Printf("Dialing TLS...\n")
|
||||
}
|
||||
cl, err := client.DialTLS(m.IMAPUri, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.Client = cl
|
||||
} else {
|
||||
if m.DebugLevel > 0 {
|
||||
log.Printf("Dialing without TLS...\n")
|
||||
}
|
||||
cl, err := client.Dial(m.IMAPUri)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if m.DebugLevel > 0 {
|
||||
log.Printf("Starting TLS...\n")
|
||||
}
|
||||
err = cl.StartTLS(
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.Client = cl
|
||||
}
|
||||
if m.DebugLevel > 0 {
|
||||
log.Printf("Sending Login Request...\n")
|
||||
}
|
||||
err := m.Client.Login(m.Username, m.Password)
|
||||
if err != nil {
|
||||
log.Printf("%s - Login failed: %s", m.Username, err)
|
||||
}
|
||||
if m.DebugLevel > 1 {
|
||||
m.Client.SetDebug(os.Stderr)
|
||||
}
|
||||
log.Println("Logged in!")
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *Monitor) CheckConnection() error {
|
||||
if m.DebugLevel > 0 {
|
||||
log.Printf("Checking ConnectionState: %d\n", m.Client.State())
|
||||
}
|
||||
if m.Client.State() != imap.ConnectedState &&
|
||||
m.Client.State() != imap.AuthenticatedState &&
|
||||
m.Client.State() != imap.SelectedState {
|
||||
err := m.Login()
|
||||
if err != nil {
|
||||
log.Printf("%s - Login failed: %s", m.Username, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Monitor) Fetch() {
|
||||
if m.DebugLevel > 0 {
|
||||
log.Printf("Fetching unread Messages from Folder: %s\n", m.SourceFolder)
|
||||
}
|
||||
if err := m.CheckConnection(); err != nil {
|
||||
log.Printf("%s - Connection lost: %s", m.Username, err)
|
||||
return
|
||||
}
|
||||
|
||||
if m.DebugLevel > 0 {
|
||||
log.Printf("Preparing Search Request by Selecting Folder: %s\n", m.SourceFolder)
|
||||
}
|
||||
criteria := imap.NewSearchCriteria()
|
||||
criteria.WithoutFlags = []string{imap.SeenFlag}
|
||||
_, err := m.Client.Select(m.SourceFolder, false)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
if m.DebugLevel > 0 {
|
||||
log.Printf("Sending Search Request...\n")
|
||||
}
|
||||
ids, err := m.Client.Search(criteria)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
if m.DebugLevel > 0 {
|
||||
log.Printf("Found %d Messages in %s\n", len(ids), m.SourceFolder)
|
||||
}
|
||||
if len(ids) <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
m.FetchSet.AddNum(ids...)
|
||||
|
||||
if m.DebugLevel > 0 {
|
||||
log.Printf("Fetching %d Messages from %s\n", len(ids), m.SourceFolder)
|
||||
}
|
||||
go func() {
|
||||
m.DoneChan <- m.Client.Fetch(m.FetchSet, []imap.FetchItem{"BODY.PEEK[]"}, m.MessageChan) //imap.FetchRFC822}, m.MessageChan)
|
||||
}()
|
||||
|
||||
err = m.PrintAll()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
log.Printf("Fetching done\n")
|
||||
}
|
||||
|
||||
func (m *Monitor) MarkRead() {
|
||||
if m.DebugLevel > 0 {
|
||||
log.Printf("Marking Messages read in Folder: %s\n", m.DestinationFolder)
|
||||
}
|
||||
|
||||
if err := m.CheckConnection(); err != nil {
|
||||
log.Printf("%s - Connection lost: %s", m.Username, err)
|
||||
return
|
||||
}
|
||||
|
||||
if m.DebugLevel > 0 {
|
||||
log.Printf("Preparing Search Request by Selecting Folder: %s\n", m.DestinationFolder)
|
||||
}
|
||||
criteria := imap.NewSearchCriteria()
|
||||
criteria.WithoutFlags = []string{imap.SeenFlag}
|
||||
_, err := m.Client.Select(m.DestinationFolder, false)
|
||||
if err != nil {
|
||||
log.Println("Marking read failed: ", err)
|
||||
}
|
||||
|
||||
if m.DebugLevel > 0 {
|
||||
log.Printf("Sending Search Request...\n")
|
||||
}
|
||||
ids, err := m.Client.Search(criteria)
|
||||
if err != nil {
|
||||
log.Println("Getting unread messages: ", err)
|
||||
}
|
||||
|
||||
if m.DebugLevel > 0 {
|
||||
log.Printf("Found %d Messages in %s\n", len(ids), m.DestinationFolder)
|
||||
}
|
||||
if len(ids) <= 0 {
|
||||
log.Println("No unread Messages found.")
|
||||
return
|
||||
}
|
||||
|
||||
m.ReadSet.Clear()
|
||||
m.ReadSet.AddNum(ids...)
|
||||
|
||||
if m.DebugLevel > 0 {
|
||||
log.Printf("Marking %d Messages read in %s\n", len(ids), m.DestinationFolder)
|
||||
}
|
||||
item := imap.FormatFlagsOp(imap.AddFlags, true)
|
||||
flags := []interface{}{imap.SeenFlag}
|
||||
err = m.Client.Store(m.ReadSet, item, flags, nil)
|
||||
if err != nil {
|
||||
log.Fatal("Marking as read: ", err)
|
||||
}
|
||||
if m.DebugLevel > 0 {
|
||||
log.Printf("Marked %d Messages read in %s\n", len(ids), m.DestinationFolder)
|
||||
}
|
||||
log.Printf("Marking read done\n")
|
||||
}
|
||||
|
||||
func (m *Monitor) PrintAll() error {
|
||||
moveSeqSet := new(imap.SeqSet)
|
||||
if m.DebugLevel > 0 {
|
||||
log.Printf("Downloading & sending fetched Messages to Default Printer\n")
|
||||
}
|
||||
|
||||
for msg := range m.MessageChan {
|
||||
var file string
|
||||
var err error
|
||||
var subject string
|
||||
var from string
|
||||
var to string
|
||||
var cc string
|
||||
var date string
|
||||
var header []string
|
||||
var html int
|
||||
var text int
|
||||
|
||||
timestamp := time.Now().Format(layout)
|
||||
|
||||
file = filepath.Join(os.TempDir(), (base64.StdEncoding.EncodeToString([]byte(timestamp))))
|
||||
|
||||
filehtml := file + ".html"
|
||||
filetxt := file + ".txt"
|
||||
|
||||
fhtml, err := os.Create(filehtml)
|
||||
if err != nil {
|
||||
log.Println("File Creation: ", err)
|
||||
return err
|
||||
}
|
||||
defer fhtml.Close()
|
||||
|
||||
ftxt, err := os.Create(filetxt)
|
||||
if err != nil {
|
||||
log.Println("File Creation: ", err)
|
||||
return err
|
||||
}
|
||||
defer ftxt.Close()
|
||||
|
||||
for _, literal := range msg.Body {
|
||||
mr, err := mail.CreateReader(literal)
|
||||
if err != nil {
|
||||
log.Fatal("Creating Reader:", err)
|
||||
return err
|
||||
}
|
||||
|
||||
fr, _ := mr.Header.AddressList("From")
|
||||
for _, a := range fr {
|
||||
from += a.Address + ","
|
||||
}
|
||||
from = from[:len(from)-1]
|
||||
|
||||
t, _ := mr.Header.AddressList("To")
|
||||
for _, a := range t {
|
||||
to += a.Address + ","
|
||||
}
|
||||
to = to[:len(to)-1]
|
||||
|
||||
c, _ := mr.Header.AddressList("Cc")
|
||||
if len(c) > 0 {
|
||||
for _, a := range c {
|
||||
cc += a.Address + ","
|
||||
}
|
||||
cc = cc[:len(cc)-1]
|
||||
}
|
||||
date = mr.Header.Get("Date")
|
||||
subject = mr.Header.Get("Subject")
|
||||
|
||||
if m.DebugLevel > 0 {
|
||||
log.Println(date, from, to, subject, cc)
|
||||
}
|
||||
|
||||
if "" != date {
|
||||
header = append(
|
||||
header,
|
||||
("Date:\t\t" + date + "\n"),
|
||||
)
|
||||
}
|
||||
if "" != from {
|
||||
header = append(
|
||||
header,
|
||||
("From:\t\t" + from + "\n"),
|
||||
)
|
||||
}
|
||||
if "" != to {
|
||||
header = append(
|
||||
header,
|
||||
("To:\t\t" + to + "\n"),
|
||||
)
|
||||
}
|
||||
if "" != cc {
|
||||
header = append(
|
||||
header,
|
||||
("Cc:\t\t" + cc + "\n"),
|
||||
)
|
||||
}
|
||||
if "" != subject {
|
||||
header = append(
|
||||
header,
|
||||
("Subject:\t" + subject + "\n"),
|
||||
)
|
||||
}
|
||||
|
||||
header = append(
|
||||
header,
|
||||
("====================================\n\n\n\n"),
|
||||
)
|
||||
|
||||
for _, s := range header {
|
||||
fhtml.WriteString(s)
|
||||
if err != nil {
|
||||
log.Printf("Writing File (Header): %s\n", err)
|
||||
}
|
||||
ftxt.WriteString(s)
|
||||
if err != nil {
|
||||
log.Printf("Writing File (Header): %s\n", err)
|
||||
}
|
||||
}
|
||||
for {
|
||||
p, err := mr.NextPart()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
log.Printf("Reading Message: %s\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
switch h := p.Header.(type) {
|
||||
case *mail.InlineHeader:
|
||||
|
||||
t, _, err := h.ContentType()
|
||||
if err != nil {
|
||||
log.Printf("Reading ContentType: %s\n", err)
|
||||
return err
|
||||
}
|
||||
switch t {
|
||||
case "text/html":
|
||||
htmltmp := parseMessage(p.Body, fhtml)
|
||||
if htmltmp > 0 {
|
||||
html = htmltmp
|
||||
}
|
||||
case "text/plain":
|
||||
texttmp := parseMessage(p.Body, ftxt)
|
||||
if texttmp > 0 {
|
||||
text = texttmp
|
||||
}
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
moveSeqSet.AddNum(msg.Uid)
|
||||
err = m.Client.Move(moveSeqSet, m.DestinationFolder)
|
||||
if err != nil {
|
||||
log.Println("Moving Item: ", err)
|
||||
}
|
||||
}
|
||||
if text > 0 {
|
||||
file = filetxt
|
||||
} else if html > 0 {
|
||||
file = filehtml
|
||||
}
|
||||
|
||||
err = printLinux(file, true)
|
||||
if err != nil {
|
||||
log.Println("Printing: ", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
m.MessageChan = make(chan *imap.Message)
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseMessage(r io.Reader, f *os.File) int {
|
||||
var err error
|
||||
content, _ := io.ReadAll(r)
|
||||
i, err := f.Write(content)
|
||||
if err != nil {
|
||||
log.Printf("Writing File (%s): %s", f.Name(), err)
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func CheckPrerequisites() error {
|
||||
var err error
|
||||
_, err = exec.LookPath("wkhtmltopdf")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
_, err = exec.LookPath("lp")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func printLinux(path string, delete bool) error {
|
||||
lp, err := exec.LookPath("lp")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
if strings.Contains(path, "html") {
|
||||
npath := strings.ReplaceAll(path, ".html", ".pdf")
|
||||
|
||||
wkhtmltopdf, err := exec.LookPath("wkhtmltopdf")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := exec.Command(wkhtmltopdf, path, npath)
|
||||
_ = cmd.Run()
|
||||
if delete {
|
||||
err = os.Remove(path)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
path = npath
|
||||
}
|
||||
cmd := exec.Command(lp, path)
|
||||
_ = cmd.Run()
|
||||
|
||||
if delete {
|
||||
err = os.Remove(path)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func Run() {
|
||||
var m Monitor
|
||||
var err error
|
||||
|
||||
argLength := len(os.Args[1:])
|
||||
if err := CheckPrerequisites(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
switch {
|
||||
case argLength <= 0:
|
||||
if _, err := os.Stat("settings.yaml"); err != nil {
|
||||
log.Fatal("Required Arguments missing!")
|
||||
}
|
||||
m, err = NewMonitorFromFile("settings.yaml")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
case argLength >= 1:
|
||||
m, err = NewMonitorFromFile(os.Args[1])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if argLength > 1 {
|
||||
d, err := strconv.ParseInt(os.Args[2], 10, 64)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
m.DebugLevel = d
|
||||
}
|
||||
|
||||
default:
|
||||
log.Fatal("Invalid or missing Parameters. Make sure to pass the pass to the 'settings.yaml'")
|
||||
}
|
||||
err = m.Login()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
m.Fetch()
|
||||
m.MarkRead()
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user