Ok pour la lecture des mails, reste à voir comment les servir via http
This commit is contained in:
9
blogentry.go
Normal file
9
blogentry.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type BlogEntry struct {
|
||||||
|
Id string
|
||||||
|
Title string
|
||||||
|
Author string
|
||||||
|
HTML string
|
||||||
|
Text string
|
||||||
|
}
|
49
cli.go
Normal file
49
cli.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func prompt(question string, options []string, defaultAnswer string, currentValue string, permitEmpty bool) (string, error) {
|
||||||
|
r := bufio.NewReader(os.Stdin)
|
||||||
|
if len(currentValue) == 0 {
|
||||||
|
currentValue = "None"
|
||||||
|
}
|
||||||
|
if len(defaultAnswer) == 0 {
|
||||||
|
defaultAnswer = "None"
|
||||||
|
}
|
||||||
|
if options == nil {
|
||||||
|
fmt.Print("->", question, "( defaults to:", defaultAnswer, " current value:", currentValue, " )", ":")
|
||||||
|
|
||||||
|
} else {
|
||||||
|
fmt.Print("->", question, "( defaults to:", defaultAnswer, " current value:", currentValue, " )[ ", strings.Join(options, " | "), " ]", ":")
|
||||||
|
}
|
||||||
|
response, _ := r.ReadString('\n')
|
||||||
|
response = strings.Trim(response, "\n")
|
||||||
|
if len(response) == 0 && currentValue != "None" {
|
||||||
|
response = currentValue
|
||||||
|
}
|
||||||
|
if len(response) == 0 && defaultAnswer != "None" {
|
||||||
|
response = defaultAnswer
|
||||||
|
}
|
||||||
|
if options != nil {
|
||||||
|
validOption := false
|
||||||
|
for _, opt := range options {
|
||||||
|
if strings.EqualFold(response, opt) {
|
||||||
|
validOption = true
|
||||||
|
response = opt
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !validOption {
|
||||||
|
return response, fmt.Errorf("invalid option")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !permitEmpty && len(response) == 0 {
|
||||||
|
return response, fmt.Errorf("empty response")
|
||||||
|
}
|
||||||
|
return response, nil
|
||||||
|
}
|
@@ -2,17 +2,15 @@ package main
|
|||||||
|
|
||||||
type MailBoxConfiguration struct {
|
type MailBoxConfiguration struct {
|
||||||
Server string
|
Server string
|
||||||
Port int
|
Port string
|
||||||
User string
|
User string
|
||||||
Password string
|
Password string
|
||||||
SSL string
|
SSL string
|
||||||
|
InBox string
|
||||||
}
|
}
|
||||||
type BlogConfiguration struct {
|
type BlogConfiguration struct {
|
||||||
Title string
|
Title string
|
||||||
MailBox string
|
ShortName string
|
||||||
|
MailBox MailBoxConfiguration
|
||||||
WWWRoot string
|
WWWRoot string
|
||||||
}
|
}
|
||||||
type Configuration struct {
|
|
||||||
MailBoxes map[string]MailBoxConfiguration
|
|
||||||
Blogs map[string]BlogConfiguration
|
|
||||||
}
|
|
||||||
|
107
imap_handler.go
107
imap_handler.go
@@ -6,6 +6,8 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"mime"
|
"mime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/emersion/go-imap/v2"
|
"github.com/emersion/go-imap/v2"
|
||||||
"github.com/emersion/go-imap/v2/imapclient"
|
"github.com/emersion/go-imap/v2/imapclient"
|
||||||
@@ -15,12 +17,28 @@ import (
|
|||||||
|
|
||||||
type MailBox struct {
|
type MailBox struct {
|
||||||
Server string
|
Server string
|
||||||
|
Port uint16
|
||||||
User string
|
User string
|
||||||
Password string
|
Password string
|
||||||
|
SSL string
|
||||||
InBox string
|
InBox string
|
||||||
Client *imapclient.Client
|
Client *imapclient.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mb *MailBox) Configure(conf *MailBoxConfiguration) error {
|
||||||
|
|
||||||
|
mb.Server = conf.Server
|
||||||
|
port, err := strconv.ParseUint(conf.Port, 10, 16)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
mb.Port = uint16(port)
|
||||||
|
mb.User = conf.User
|
||||||
|
mb.Password = conf.Password
|
||||||
|
mb.SSL = conf.SSL
|
||||||
|
mb.InBox = conf.InBox
|
||||||
|
return nil
|
||||||
|
}
|
||||||
func (mb *MailBox) ListMessages() (string, error) {
|
func (mb *MailBox) ListMessages() (string, error) {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
@@ -30,9 +48,19 @@ func (mb *MailBox) Connect() error {
|
|||||||
WordDecoder: &mime.WordDecoder{CharsetReader: charset.Reader},
|
WordDecoder: &mime.WordDecoder{CharsetReader: charset.Reader},
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
mb.Client, err = imapclient.DialTLS(mb.Server, options)
|
imapServer := fmt.Sprintf("%s:%d", mb.Server, mb.Port)
|
||||||
|
switch mb.SSL {
|
||||||
|
case "SSL/TLS":
|
||||||
|
mb.Client, err = imapclient.DialTLS(imapServer, options)
|
||||||
|
case "StartTLS":
|
||||||
|
mb.Client, err = imapclient.DialStartTLS(imapServer, options)
|
||||||
|
case "NoTLS":
|
||||||
|
mb.Client, err = imapclient.DialInsecure(imapServer, options)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("bad tls configuration")
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Error connnecting to", mb.Server, ":", err)
|
log.Println("Error connnecting to", imapServer, ":", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Println("Connected")
|
log.Println("Connected")
|
||||||
@@ -41,21 +69,33 @@ func (mb *MailBox) Connect() error {
|
|||||||
log.Fatal("failed to login:", err)
|
log.Fatal("failed to login:", err)
|
||||||
}
|
}
|
||||||
log.Println("Logged in")
|
log.Println("Logged in")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (mb *MailBox) ListFolders() ([]string, error) {
|
||||||
|
folders := make([]string, 0)
|
||||||
mailBoxes, err := mb.Client.List("", "%", nil).Collect()
|
mailBoxes, err := mb.Client.List("", "%", nil).Collect()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error listing mailboxes", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, mbox := range mailBoxes {
|
for _, mbox := range mailBoxes {
|
||||||
log.Println("mailbox:", mbox.Mailbox)
|
folders = append(folders, mbox.Mailbox)
|
||||||
}
|
}
|
||||||
|
return folders, nil
|
||||||
|
}
|
||||||
|
func (mb *MailBox) GetMessages() ([]BlogEntry, error) {
|
||||||
|
|
||||||
inbox, err := mb.Client.Select(mb.InBox, nil).Wait()
|
inbox, err := mb.Client.Select(mb.InBox, nil).Wait()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error selecting mailbox:", mb.InBox, err)
|
log.Fatal("Error selecting mailbox:", mb.InBox, err)
|
||||||
}
|
}
|
||||||
log.Println("Inbox has", inbox.NumMessages, "messages")
|
log.Println("Inbox has", inbox.NumMessages, "messages")
|
||||||
|
|
||||||
for i := uint32(0); i <= inbox.NumMessages; i++ {
|
if inbox.NumMessages <= 0 {
|
||||||
|
return nil, fmt.Errorf("nomessages")
|
||||||
|
}
|
||||||
|
entries := make([]BlogEntry, 0)
|
||||||
|
for i := uint32(1); i <= inbox.NumMessages; i++ {
|
||||||
|
var entry BlogEntry
|
||||||
seqSet := imap.SeqSetNum(i)
|
seqSet := imap.SeqSetNum(i)
|
||||||
bodySection := &imap.FetchItemBodySection{}
|
bodySection := &imap.FetchItemBodySection{}
|
||||||
fetchOptions := &imap.FetchOptions{
|
fetchOptions := &imap.FetchOptions{
|
||||||
@@ -66,44 +106,63 @@ func (mb *MailBox) Connect() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error fetching mails:", err)
|
log.Fatal("Error fetching mails:", err)
|
||||||
}
|
}
|
||||||
for _, msg := range messages {
|
entry.Title = strings.Trim(messages[0].Envelope.Subject, "\t ")
|
||||||
log.Println("**************MESSAGE**************")
|
if !strings.HasPrefix(strings.ToLower(entry.Title), "blog ") {
|
||||||
log.Println("From:", msg.Envelope.From)
|
continue
|
||||||
log.Println("To:", msg.Envelope.To)
|
}
|
||||||
log.Println("Date:", msg.Envelope.Date)
|
|
||||||
log.Println("Subject:", msg.Envelope.Subject)
|
|
||||||
|
|
||||||
section := msg.FindBodySection(bodySection)
|
entry.Id = messages[0].Envelope.MessageID
|
||||||
|
entry.Title = entry.Title[5:]
|
||||||
|
entry.Author = messages[0].Envelope.From[0].Name
|
||||||
|
|
||||||
|
section := messages[0].FindBodySection(bodySection)
|
||||||
ioReader := bytes.NewReader(section)
|
ioReader := bytes.NewReader(section)
|
||||||
mailReader, err := mail.CreateReader(ioReader)
|
mailReader, err := mail.CreateReader(ioReader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed to create mail reader: %v", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
part, err := mailReader.NextPart()
|
part, err := mailReader.NextPart()
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
break
|
break
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
log.Fatal("Error reading part", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
log.Println("------------PART-----------")
|
|
||||||
fmt.Println("Content-Type:", part.Header.Get("Content-Type"))
|
switch part.Header.(type) {
|
||||||
fmt.Println("Content-Transfer-Encoding:", part.Header.Get("Content-Transfer-Encoding"))
|
|
||||||
fmt.Println("header:", part.Header)
|
|
||||||
switch header := part.Header.(type) {
|
|
||||||
case *mail.AttachmentHeader:
|
case *mail.AttachmentHeader:
|
||||||
|
/*
|
||||||
filename, _ := header.Filename()
|
filename, _ := header.Filename()
|
||||||
|
|
||||||
log.Println("------------FILE-----------")
|
log.Println("------------FILE-----------")
|
||||||
log.Println("Attachment:", filename)
|
log.Println("Attachment:", filename)
|
||||||
|
*/
|
||||||
case *mail.InlineHeader:
|
case *mail.InlineHeader:
|
||||||
log.Println("------------INLINE-----------")
|
|
||||||
body, _ := io.ReadAll(part.Body)
|
body, _ := io.ReadAll(part.Body)
|
||||||
log.Println(string(body))
|
contentTypeFull := strings.TrimPrefix(
|
||||||
|
strings.ToLower(part.Header.Get("Content-Type")), "content-type")
|
||||||
|
mediaType, _, err := mime.ParseMediaType(strings.TrimLeft(contentTypeFull, ": "))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch mediaType {
|
||||||
|
case "text/plain":
|
||||||
|
entry.Text = string(body)
|
||||||
|
case "text/html":
|
||||||
|
entry.HTML = string(body)
|
||||||
|
default:
|
||||||
|
log.Println("Content-Type:", part.Header.Get("Content-Type"))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
log.Printf("Unkown part type %+v", part)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
entries = append(entries, entry)
|
||||||
}
|
}
|
||||||
}
|
return entries, nil
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mb *MailBox) Close() {
|
func (mb *MailBox) Close() {
|
||||||
|
119
mailblog.go
119
mailblog.go
@@ -1,11 +1,13 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
func slash(w http.ResponseWriter, r *http.Request) {
|
func slash(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -14,29 +16,122 @@ func slash(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
var mb MailBox
|
var err error
|
||||||
|
var configuration BlogConfiguration
|
||||||
|
args := os.Args[1:]
|
||||||
|
|
||||||
url, err := url.Parse(os.Getenv("MAILBOX"))
|
home := os.Getenv("HOME")
|
||||||
|
err = os.MkdirAll(fmt.Sprintf("%s/.config/", home), 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Bad parameter MAILBOX:", os.Getenv("MAILBOX"), "(", err, ")")
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
mb.Server = url.Host
|
file, err := os.Open(fmt.Sprintf("%s/.config/mailblog.json", home))
|
||||||
mb.User = os.Getenv("IMAP_USER")
|
if err == nil {
|
||||||
mb.Password = os.Getenv("IMAP_PASSWORD")
|
defer file.Close()
|
||||||
mb.InBox, _ = strings.CutPrefix(url.Path, "/")
|
decoder := json.NewDecoder(file)
|
||||||
|
err = decoder.Decode(&configuration)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) > 0 {
|
||||||
|
switch args[0] {
|
||||||
|
case "configure":
|
||||||
|
|
||||||
|
configuration.Title, err = prompt("Blog Title", nil, "", configuration.Title, false)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
configuration.ShortName, err = prompt("Blog Short Name (only letters and numbers, no special signes)", nil, "blog", configuration.ShortName, false)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if !regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString(configuration.ShortName) {
|
||||||
|
log.Fatal("Short Name invalid")
|
||||||
|
}
|
||||||
|
|
||||||
|
configuration.WWWRoot, err = prompt("Web Root directory", nil, fmt.Sprintf("/var/www/html/%s", configuration.ShortName), configuration.WWWRoot, false)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
configuration.MailBox.Server, err = prompt("IMAP Server Hostname or IP", nil, "", configuration.MailBox.Server, false)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
configuration.MailBox.Port, err = prompt("IMAP Server Port", nil, "993", configuration.MailBox.Port, false)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = strconv.ParseUint(configuration.MailBox.Port, 10, 16)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("IMAP Server Port invalid")
|
||||||
|
}
|
||||||
|
configuration.MailBox.User, err = prompt("IMAP User Login", nil, "", configuration.MailBox.User, false)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
configuration.MailBox.Password, err = prompt("IMAP User Password", nil, "", configuration.MailBox.Password, false)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
options := [...]string{"SSL/TLS", "StartTLS", "NoTLS"}
|
||||||
|
configuration.MailBox.SSL, err = prompt("IMAP SSL/TLS", options[:], "SSL/TLS", configuration.MailBox.SSL, false)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
configuration.MailBox.InBox, err = prompt("IMAP INBOX", nil, "INBOX", configuration.MailBox.InBox, false)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err = os.Create(fmt.Sprintf("%s/.config/mailblog.json", home))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
encoder := json.NewEncoder(file)
|
||||||
|
err = encoder.Encode(configuration)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Configuration save in %s/.config/mailblob.json\n", home)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var mb MailBox
|
||||||
|
mb.Configure(&configuration.MailBox)
|
||||||
err = mb.Connect()
|
err = mb.Connect()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
msg, err := mb.ListMessages()
|
folders, err := mb.ListFolders()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Println(folders)
|
||||||
|
msgs, err := mb.GetMessages()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println(msg)
|
for _, m := range msgs {
|
||||||
|
log.Println(m.Id)
|
||||||
|
log.Println(m.Title)
|
||||||
|
log.Println(m.Author)
|
||||||
|
log.Println("Text version")
|
||||||
|
log.Println(m.Text)
|
||||||
|
log.Println("HTML version")
|
||||||
|
log.Println(m.HTML)
|
||||||
|
log.Println(m.Id)
|
||||||
|
log.Println()
|
||||||
|
}
|
||||||
|
|
||||||
mb.Close()
|
mb.Close()
|
||||||
return
|
|
||||||
http.HandleFunc("/", slash)
|
http.HandleFunc("/", slash)
|
||||||
http.ListenAndServe("0.0.0.0:8080", nil)
|
http.ListenAndServe("0.0.0.0:8080", nil)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user