Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
4c74ebc1c7 | |||
7a43b90f67 | |||
5cab29c344 | |||
e129c63b37 |
58
README.md
58
README.md
@ -2,7 +2,7 @@
|
||||
|
||||
[![Go Reference](https://pkg.go.dev/badge/git.b0zal.io/H0llyW00dzZ/imap.svg)](https://pkg.go.dev/git.b0zal.io/H0llyW00dzZ/imap) [![Go Report Card](https://goreportcard.com/badge/git.b0zal.io/H0llyW00dzZ/imap)](https://goreportcard.com/report/git.b0zal.io/H0llyW00dzZ/imap)
|
||||
|
||||
This package provides a simple interface to manage IMAP connections for single or multiple users. It allows you to connect to an IMAP server, list messages, export messages, and manage multiple user accounts.
|
||||
This package provides a simple interface to manage IMAP connections for single or multiple users. It allows you to connect to an IMAP server, list messages, export messages, manage multiple user accounts, and list available mailboxes.
|
||||
|
||||
## Features
|
||||
|
||||
@ -10,6 +10,7 @@ This package provides a simple interface to manage IMAP connections for single o
|
||||
- List messages in a specified mailbox
|
||||
- Export messages to various formats
|
||||
- Manage multiple users with separate IMAP clients
|
||||
- **New**: List all available mailboxes
|
||||
|
||||
## Installation
|
||||
|
||||
@ -51,6 +52,13 @@ func main() {
|
||||
}
|
||||
defer imapClient.Disconnect()
|
||||
|
||||
// New: List all available mailboxes
|
||||
mailboxes, err := imapClient.ListMailboxes()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to list mailboxes: %v", err)
|
||||
}
|
||||
fmt.Println("Mailboxes:", mailboxes)
|
||||
|
||||
messageConfig := client.MessageConfig{
|
||||
GrabID: true,
|
||||
GrabFrom: true,
|
||||
@ -93,6 +101,17 @@ func main() {
|
||||
}
|
||||
defer multiUserIMAP.DisconnectUser("user1@example.com")
|
||||
|
||||
// New: List all available mailboxes for user1
|
||||
client, err := multiUserIMAP.GetClient("user1@example.com")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to get client for user1: %v", err)
|
||||
}
|
||||
mailboxes, err := client.ListMailboxes()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to list mailboxes for user1: %v", err)
|
||||
}
|
||||
fmt.Println("User1 Mailboxes:", mailboxes)
|
||||
|
||||
messageConfig := client.MessageConfig{
|
||||
GrabID: true,
|
||||
GrabFrom: true,
|
||||
@ -161,6 +180,43 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### List All Available Mailboxes
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"git.b0zal.io/H0llyW00dzZ/imap/client"
|
||||
)
|
||||
|
||||
func main() {
|
||||
config := &client.Config{
|
||||
Username: "user@example.com",
|
||||
Password: "password",
|
||||
Server: "imap.example.com:993",
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
|
||||
imapClient := client.NewIMAP(config)
|
||||
|
||||
err := imapClient.Connect()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to connect: %v", err)
|
||||
}
|
||||
defer imapClient.Disconnect()
|
||||
|
||||
// List all available mailboxes
|
||||
mailboxes, err := imapClient.ListMailboxes()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to list mailboxes: %v", err)
|
||||
}
|
||||
fmt.Println("Mailboxes:", mailboxes)
|
||||
}
|
||||
```
|
||||
|
||||
## TODO
|
||||
|
||||
- Implement functionality to send emails.
|
||||
|
@ -5,7 +5,11 @@
|
||||
|
||||
package client
|
||||
|
||||
import "github.com/emersion/go-imap/client"
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/emersion/go-imap/client"
|
||||
)
|
||||
|
||||
// Config holds the configuration for the IMAP client
|
||||
type Config struct {
|
||||
@ -17,10 +21,17 @@ type Config struct {
|
||||
|
||||
// MessageConfig defines what parts of the message to retrieve
|
||||
type MessageConfig struct {
|
||||
GrabID bool
|
||||
GrabFrom bool
|
||||
GrabSubject bool
|
||||
GrabBody bool
|
||||
GrabID bool
|
||||
GrabFrom bool
|
||||
GrabSubject bool
|
||||
GrabBody bool
|
||||
GrabSender bool
|
||||
GrabReplyTo bool
|
||||
GrabTo bool
|
||||
GrabCc bool
|
||||
GrabBcc bool
|
||||
GrabInReplyTo bool
|
||||
GrabDate bool
|
||||
}
|
||||
|
||||
// IMAPClient represents an IMAP client
|
||||
@ -28,3 +39,11 @@ type IMAPClient struct {
|
||||
config *Config
|
||||
client *client.Client
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrorsClientIsNotConnected is returned when an operation is attempted on a client that is not connected.
|
||||
ErrorsClientIsNotConnected = errors.New("client is not connected")
|
||||
|
||||
// ErrorsFailedToReadBody is returned when there is a failure in reading the body of a message.
|
||||
ErrorsFailedToReadBody = errors.New("failed to read body")
|
||||
)
|
||||
|
@ -5,13 +5,14 @@
|
||||
|
||||
// Package client provides a simple interface to manage IMAP connections
|
||||
// for single or multiple users. It allows you to connect to an IMAP server,
|
||||
// list messages, export messages, and manage multiple user accounts.
|
||||
// list messages, export messages, manage multiple user accounts, and list available mailboxes.
|
||||
//
|
||||
// # Features
|
||||
// - Connect and disconnect from an IMAP server
|
||||
// - List messages in a specified mailbox
|
||||
// - Export messages to various formats
|
||||
// - Manage multiple users with separate IMAP clients
|
||||
// - List all available mailboxes
|
||||
//
|
||||
// # Usage
|
||||
//
|
||||
@ -59,6 +60,40 @@
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// List All Available Mailboxes Example:
|
||||
//
|
||||
// package main
|
||||
//
|
||||
// import (
|
||||
// "fmt"
|
||||
// "log"
|
||||
//
|
||||
// "git.b0zal.io/H0llyW00dzZ/imap/client"
|
||||
// )
|
||||
//
|
||||
// func main() {
|
||||
// config := &client.Config{
|
||||
// Username: "user@example.com",
|
||||
// Password: "password",
|
||||
// Server: "imap.example.com:993",
|
||||
// InsecureSkipVerify: true,
|
||||
// }
|
||||
//
|
||||
// imapClient := client.NewIMAP(config)
|
||||
//
|
||||
// err := imapClient.Connect()
|
||||
// if err != nil {
|
||||
// log.Fatalf("Failed to connect: %v", err)
|
||||
// }
|
||||
// defer imapClient.Disconnect()
|
||||
//
|
||||
// mailboxes, err := imapClient.ListMailboxes()
|
||||
// if err != nil {
|
||||
// log.Fatalf("Failed to list mailboxes: %v", err)
|
||||
// }
|
||||
// fmt.Println("Mailboxes:", mailboxes)
|
||||
// }
|
||||
//
|
||||
// Multiple Users Example:
|
||||
//
|
||||
// package main
|
||||
|
@ -15,7 +15,7 @@ import (
|
||||
// ExportMessagesTo uses the provided exporter to write messages to the writer
|
||||
func (c *IMAPClient) ExportMessagesTo(writer io.Writer, exporter export.Exporter, mailbox string, numMessages uint32, config MessageConfig) error {
|
||||
if c.client == nil {
|
||||
return fmt.Errorf("client is not connected")
|
||||
return ErrorsClientIsNotConnected
|
||||
}
|
||||
|
||||
// Fetch messages based on the provided MessageConfig and number of messages
|
||||
|
@ -123,3 +123,32 @@ func TestListMessages(t *testing.T) {
|
||||
|
||||
assert.Equal(t, expectedBody, messages[0][client.KeyBody], "Unexpected body content")
|
||||
}
|
||||
|
||||
func TestListMailboxes(t *testing.T) {
|
||||
listener, srv := setupTestServer()
|
||||
defer listener.Close()
|
||||
defer srv.Close() // Ensure the server is closed properly
|
||||
|
||||
// Use the existing user in the memory backend
|
||||
config := &client.Config{
|
||||
Username: "username",
|
||||
Password: "password",
|
||||
Server: listener.Addr().String(),
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
|
||||
imapClient := client.NewIMAP(config)
|
||||
|
||||
// Connect to the server
|
||||
err := imapClient.Connect()
|
||||
assert.NoError(t, err, "Failed to connect")
|
||||
defer imapClient.Disconnect()
|
||||
|
||||
// List mailboxes
|
||||
mailboxes, err := imapClient.ListMailboxes()
|
||||
assert.NoError(t, err, "Failed to list mailboxes")
|
||||
|
||||
// Validate the mailboxes
|
||||
expectedMailboxes := []string{"INBOX"}
|
||||
assert.ElementsMatch(t, expectedMailboxes, mailboxes, "Unexpected mailboxes")
|
||||
}
|
||||
|
38
client/list_mailboxes.go
Normal file
38
client/list_mailboxes.go
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2025 H0llyW00dzZ All rights reserved.
|
||||
//
|
||||
// By accessing or using this software, you agree to be bound by the terms
|
||||
// of the License Agreement, which you can find at LICENSE files.
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
// ListMailboxes retrieves all available mailboxes for the connected user
|
||||
func (c *IMAPClient) ListMailboxes() ([]string, error) {
|
||||
if c.client == nil {
|
||||
return nil, ErrorsClientIsNotConnected
|
||||
}
|
||||
|
||||
mailboxes := make(chan *imap.MailboxInfo, 10)
|
||||
done := make(chan error, 1)
|
||||
|
||||
// Start listing mailboxes
|
||||
go func() {
|
||||
done <- c.client.List("", "*", mailboxes)
|
||||
}()
|
||||
|
||||
var mailboxNames []string
|
||||
for m := range mailboxes {
|
||||
mailboxNames = append(mailboxNames, m.Name)
|
||||
}
|
||||
|
||||
if err := <-done; err != nil {
|
||||
return nil, fmt.Errorf("failed to list mailboxes: %v", err)
|
||||
}
|
||||
|
||||
return mailboxNames, nil
|
||||
}
|
@ -7,6 +7,7 @@ package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/valyala/bytebufferpool"
|
||||
@ -21,12 +22,26 @@ const (
|
||||
KeySubject = "subject"
|
||||
// KeyBody is the key for the message body
|
||||
KeyBody = "body"
|
||||
// KeySender is the key for the sender's address in the envelope
|
||||
KeySender = "sender"
|
||||
// KeyReplyTo is the key for the reply-to addresses
|
||||
KeyReplyTo = "replyTo"
|
||||
// KeyTo is the key for the 'To' addresses
|
||||
KeyTo = "to"
|
||||
// KeyCc is the key for the 'Cc' addresses
|
||||
KeyCc = "cc"
|
||||
// KeyBcc is the key for the 'Bcc' addresses
|
||||
KeyBcc = "bcc"
|
||||
// KeyInReplyTo is the key for the in-reply-to identifier
|
||||
KeyInReplyTo = "inReplyTo"
|
||||
// KeyDate is the key for the message date
|
||||
KeyDate = "date"
|
||||
)
|
||||
|
||||
// ListMessages lists the messages in the specified mailbox based on the MessageConfig
|
||||
func (c *IMAPClient) ListMessages(mailbox string, numMessages uint32, config MessageConfig) ([]map[string]any, error) {
|
||||
if c.client == nil {
|
||||
return nil, fmt.Errorf("client is not connected")
|
||||
return nil, ErrorsClientIsNotConnected
|
||||
}
|
||||
|
||||
mbox, err := c.selectMailbox(mailbox)
|
||||
@ -90,29 +105,75 @@ func (c *IMAPClient) getFetchItems(config MessageConfig) []imap.FetchItem {
|
||||
func (c *IMAPClient) processMessages(messages chan *imap.Message, config MessageConfig) ([]map[string]any, error) {
|
||||
var results []map[string]any
|
||||
for msg := range messages {
|
||||
details := make(map[string]any)
|
||||
|
||||
if config.GrabID && msg.Envelope.MessageId != "" {
|
||||
details[KeyID] = msg.Envelope.MessageId
|
||||
details := c.extractDetails(msg, config)
|
||||
if details != nil {
|
||||
results = append(results, details)
|
||||
}
|
||||
if config.GrabFrom {
|
||||
details[KeyFrom] = c.extractAddresses(msg.Envelope.From)
|
||||
}
|
||||
if config.GrabSubject && msg.Envelope.Subject != "" {
|
||||
details[KeySubject] = msg.Envelope.Subject
|
||||
}
|
||||
if config.GrabBody {
|
||||
body, err := c.extractBody(msg.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
details[KeyBody] = body
|
||||
}
|
||||
results = append(results, details)
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// extractDetails extracts message details based on the MessageConfig.
|
||||
func (c *IMAPClient) extractDetails(msg *imap.Message, config MessageConfig) map[string]any {
|
||||
details := make(map[string]any)
|
||||
|
||||
// Add message ID if configured
|
||||
c.addIfNotEmpty(details, KeyID, config.GrabID, msg.Envelope.MessageId)
|
||||
// Add 'From' addresses if configured
|
||||
c.addAddresses(details, KeyFrom, config.GrabFrom, msg.Envelope.From)
|
||||
// Add subject if configured
|
||||
c.addIfNotEmpty(details, KeySubject, config.GrabSubject, msg.Envelope.Subject)
|
||||
// Add body if configured
|
||||
c.addBody(details, KeyBody, config.GrabBody, msg.Body)
|
||||
// Add sender if configured
|
||||
c.addAddresses(details, KeySender, config.GrabSender, msg.Envelope.Sender)
|
||||
// Add reply-to addresses if configured
|
||||
c.addAddresses(details, KeyReplyTo, config.GrabReplyTo, msg.Envelope.ReplyTo)
|
||||
// Add 'To' addresses if configured
|
||||
c.addAddresses(details, KeyTo, config.GrabTo, msg.Envelope.To)
|
||||
// Add 'Cc' addresses if configured
|
||||
c.addAddresses(details, KeyCc, config.GrabCc, msg.Envelope.Cc)
|
||||
// Add 'Bcc' addresses if configured
|
||||
c.addAddresses(details, KeyBcc, config.GrabBcc, msg.Envelope.Bcc)
|
||||
// Add in-reply-to ID if configured
|
||||
c.addIfNotEmpty(details, KeyInReplyTo, config.GrabInReplyTo, msg.Envelope.InReplyTo)
|
||||
// Add date if configured
|
||||
c.addIfNotZero(details, KeyDate, config.GrabDate, msg.Envelope.Date)
|
||||
|
||||
return details
|
||||
}
|
||||
|
||||
// addIfNotEmpty adds a value to details if it's not empty or nil and grabbing is enabled.
|
||||
func (c *IMAPClient) addIfNotEmpty(details map[string]any, key string, grab bool, value string) {
|
||||
if grab && value != "" && value != "<nil>" {
|
||||
details[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
// addAddresses adds email addresses to details if grabbing is enabled.
|
||||
func (c *IMAPClient) addAddresses(details map[string]any, key string, grab bool, addresses []*imap.Address) {
|
||||
if grab && len(addresses) > 0 {
|
||||
details[key] = c.extractAddresses(addresses)
|
||||
}
|
||||
}
|
||||
|
||||
// addBody adds the message body to details if grabbing is enabled.
|
||||
func (c *IMAPClient) addBody(details map[string]any, key string, grab bool, body map[*imap.BodySectionName]imap.Literal) {
|
||||
if grab {
|
||||
content, err := c.extractBody(body)
|
||||
if err == nil && content != "" {
|
||||
details[key] = content
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// addIfNotZero adds a date to details if it's not zero and grabbing is enabled.
|
||||
func (c *IMAPClient) addIfNotZero(details map[string]any, key string, grab bool, date time.Time) {
|
||||
if grab && !date.IsZero() {
|
||||
details[key] = date
|
||||
}
|
||||
}
|
||||
|
||||
// extractAddresses extracts email addresses from a list of IMAP addresses.
|
||||
func (c *IMAPClient) extractAddresses(addresses []*imap.Address) []string {
|
||||
var from []string
|
||||
@ -133,7 +194,7 @@ func (c *IMAPClient) extractBody(body map[*imap.BodySectionName]imap.Literal) (s
|
||||
return result, nil
|
||||
}
|
||||
bytebufferpool.Put(buf)
|
||||
return "", fmt.Errorf("failed to read body")
|
||||
return "", ErrorsFailedToReadBody
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user