First Commit

- [+] feat: add initial implementation of IMAP client package with support for single and multiple user management
- [+] chore: add .gitignore file to exclude binaries, IDE files, and environment configurations
- [+] docs: add README file with project description, features, installation, usage examples, and license information
- [+] docs: add BSD 3-Clause License file
- [+] feat(client): implement configuration and connection handling for IMAP clients
- [+] feat(client): implement message listing functionality for single and multiple users
- [+] feat(client): add support for secure TLS connections in IMAP client
- [+] feat(client): create helper functions for initializing single and multi-user IMAP clients
- [+] chore: initialize Go module and add dependencies for IMAP and byte buffer pooling
This commit is contained in:
H0llyW00dzZ 2025-01-24 11:26:48 +07:00
commit 464fdc1bbd
Signed by: H0llyW00dzZ
GPG Key ID: A0F9424A7002343A
10 changed files with 463 additions and 0 deletions

34
.gitignore vendored Normal file
View File

@ -0,0 +1,34 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with "go test -c"
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
# Go workspace file
go.work
tmp/
# IDE specific files
.vscode
.idea
# https://idx.google.com it's free
.idx
# .env file
.env
# Project build
bin
emails.csv
run.go

30
LICENSE Normal file
View File

@ -0,0 +1,30 @@
BSD 3-Clause License
-----------
Copyright (c) 2024, H0llyW00dzZ / H0llyW00dz
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

120
README.md Normal file
View File

@ -0,0 +1,120 @@
# IMAP Client Package
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, and manage multiple user accounts.
## Features
- Connect and disconnect from an IMAP server
- List messages in a specified mailbox
- Manage multiple users with separate IMAP clients
## Installation
To install the package, use `go get`:
```bash
go get git.b0zal.io/H0llyW00dzZ/imap
```
## Usage
### Single User
```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()
messageConfig := client.MessageConfig{
GrabID: true,
GrabFrom: true,
GrabSubject: true,
GrabBody: true,
}
messages, err := imapClient.ListMessages("INBOX", 10, messageConfig)
if err != nil {
log.Fatalf("Failed to list messages: %v", err)
}
for _, msg := range messages {
fmt.Printf("ID: %s, From: %v, Subject: %s, Body: %s\n", msg.ID, msg.From, msg.Subject, msg.Body)
}
}
```
### Multiple Users
```go
package main
import (
"fmt"
"log"
"git.b0zal.io/H0llyW00dzZ/imap/client"
)
func main() {
multiUserIMAP := client.NewMultiUserIMAP()
multiUserIMAP.AddUser("user1@example.com", "password1", "imap.example.com:993", true)
multiUserIMAP.AddUser("user2@example.com", "password2", "imap.example.com:993", true)
err := multiUserIMAP.ConnectUser("user1@example.com")
if err != nil {
log.Fatalf("Failed to connect user1: %v", err)
}
defer multiUserIMAP.DisconnectUser("user1@example.com")
messageConfig := client.MessageConfig{
GrabID: true,
GrabFrom: true,
GrabSubject: true,
GrabBody: true,
}
messages, err := multiUserIMAP.ListUserMessages("user1@example.com", "INBOX", 10, messageConfig)
if err != nil {
log.Fatalf("Failed to list messages for user1: %v", err)
}
for _, msg := range messages {
fmt.Printf("ID: %s, From: %v, Subject: %s, Body: %s\n", msg.ID, msg.From, msg.Subject, msg.Body)
}
}
```
## TODO
- Implement functionality to send emails.
- Add support for folder management (create, delete, rename).
- Enhance error handling and logging.
- Support for message search and filtering.
- Implement message deletion and flagging.
- Add more tests for edge cases and concurrent access.
## License
This project is licensed under the BSD 3-Clause License. See the [LICENSE](LICENSE) file for details.

30
client/config.go Normal file
View File

@ -0,0 +1,30 @@
// 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 "github.com/emersion/go-imap/client"
// Config holds the configuration for the IMAP client
type Config struct {
Username string
Password string
Server string
InsecureSkipVerify bool
}
// MessageConfig defines what parts of the message to retrieve
type MessageConfig struct {
GrabID bool
GrabFrom bool
GrabSubject bool
GrabBody bool
}
// IMAPClient represents an IMAP client
type IMAPClient struct {
config *Config
client *client.Client
}

84
client/connection.go Normal file
View File

@ -0,0 +1,84 @@
// 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 (
"crypto/tls"
"fmt"
"github.com/emersion/go-imap/client"
)
// Connect establishes a connection to the IMAP server
func (c *IMAPClient) Connect() error {
tlsConfig := &tls.Config{
InsecureSkipVerify: c.config.InsecureSkipVerify,
}
cl, err := client.DialTLS(c.config.Server, tlsConfig)
if err != nil {
return fmt.Errorf("failed to connect to server: %v", err)
}
if err := cl.Login(c.config.Username, c.config.Password); err != nil {
return fmt.Errorf("failed to login: %v", err)
}
c.client = cl
return nil
}
// Disconnect logs out and closes the connection
func (c *IMAPClient) Disconnect() error {
if c.client != nil {
if err := c.client.Logout(); err != nil {
return fmt.Errorf("failed to logout: %v", err)
}
}
return nil
}
// AddUser adds a new user to the MultiUserIMAP
func (m *MultiUserIMAP) AddUser(username, password, server string, insecureSkipVerify bool) {
m.mu.Lock()
defer m.mu.Unlock()
config := &Config{
Username: username,
Password: password,
Server: server,
InsecureSkipVerify: insecureSkipVerify,
}
client := NewIMAP(config)
m.clients[username] = client
}
// ConnectUser connects a specific user
func (m *MultiUserIMAP) ConnectUser(username string) error {
m.mu.Lock()
defer m.mu.Unlock()
client, exists := m.clients[username]
if !exists {
return fmt.Errorf("user not found: %s", username)
}
return client.Connect()
}
// DisconnectUser disconnects a specific user
func (m *MultiUserIMAP) DisconnectUser(username string) error {
m.mu.Lock()
defer m.mu.Unlock()
client, exists := m.clients[username]
if !exists {
return fmt.Errorf("user not found: %s", username)
}
return client.Disconnect()
}

102
client/list_messages.go Normal file
View File

@ -0,0 +1,102 @@
// 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"
"github.com/valyala/bytebufferpool"
)
// MessageDetails holds the details of an email message.
// This struct allows the caller to access and format the message's
// ID, sender information, subject, and body as needed.
type MessageDetails struct {
ID string
From []string
Subject string
Body string
}
// ListMessages lists the messages in the specified mailbox based on the MessageConfig
func (c *IMAPClient) ListMessages(mailbox string, numMessages uint32, config MessageConfig) ([]MessageDetails, error) {
if c.client == nil {
return nil, fmt.Errorf("client is not connected")
}
mbox, err := c.client.Select(mailbox, false)
if err != nil {
return nil, fmt.Errorf("failed to select %s: %v", mailbox, err)
}
from := uint32(1)
to := mbox.Messages
if mbox.Messages > numMessages {
from = mbox.Messages - numMessages + 1
}
seqset := new(imap.SeqSet)
seqset.AddRange(from, to)
items := []imap.FetchItem{imap.FetchEnvelope}
if config.GrabBody {
items = append(items, imap.FetchItem("BODY.PEEK[]"))
}
messages := make(chan *imap.Message, numMessages)
done := make(chan error, 1)
go func() {
done <- c.client.Fetch(seqset, items, messages)
}()
var results []MessageDetails
for msg := range messages {
details := MessageDetails{}
if config.GrabID {
details.ID = msg.Envelope.MessageId
}
if config.GrabFrom {
for _, addr := range msg.Envelope.From {
details.From = append(details.From, addr.Address())
}
}
if config.GrabSubject {
details.Subject = msg.Envelope.Subject
}
if config.GrabBody {
for _, literal := range msg.Body {
buf := bytebufferpool.Get()
defer bytebufferpool.Put(buf)
_, err := buf.ReadFrom(literal)
if err == nil {
details.Body = buf.String()
}
}
}
results = append(results, details)
}
if err := <-done; err != nil {
return nil, fmt.Errorf("failed to fetch messages: %v", err)
}
return results, nil
}
// ListUserMessages lists messages for a specific user based on the MessageConfig
func (m *MultiUserIMAP) ListUserMessages(username, mailbox string, numMessages uint32, config MessageConfig) ([]MessageDetails, error) {
m.mu.Lock()
defer m.mu.Unlock()
client, exists := m.clients[username]
if !exists {
return nil, fmt.Errorf("user not found: %s", username)
}
return client.ListMessages(mailbox, numMessages, config)
}

17
client/new.go Normal file
View File

@ -0,0 +1,17 @@
// 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
// NewIMAP creates a new IMAP client with the given configuration
func NewIMAP(config *Config) *IMAPClient {
if config == nil {
config = &Config{}
}
return &IMAPClient{
config: config,
}
}

21
client/new_multi_user.go Normal file
View File

@ -0,0 +1,21 @@
// 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 "sync"
// MultiUserIMAP manages multiple IMAP clients for different users
type MultiUserIMAP struct {
clients map[string]*IMAPClient
mu sync.Mutex
}
// NewMultiUserIMAP initializes a new MultiUserIMAP
func NewMultiUserIMAP() *MultiUserIMAP {
return &MultiUserIMAP{
clients: make(map[string]*IMAPClient),
}
}

13
go.mod Normal file
View File

@ -0,0 +1,13 @@
module git.b0zal.io/H0llyW00dzZ/imap
go 1.23.5
require (
github.com/emersion/go-imap v1.2.1
github.com/valyala/bytebufferpool v1.0.0
)
require (
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 // indirect
golang.org/x/text v0.3.7 // indirect
)

12
go.sum Normal file
View File

@ -0,0 +1,12 @@
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/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/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
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=