fix and recompile

This commit is contained in:
Othman Hendy Suseno
2025-09-09 00:08:24 +07:00
parent f0dc0f32fa
commit 8c4b661152
11 changed files with 393 additions and 168 deletions

332
README.md
View File

@@ -11,6 +11,9 @@ Email migration tools untuk memindahkan mailbox IMAP antar server dengan dukunga
-**Mailbox Detection**: Otomatis detect dan migrate semua mailbox/folder
-**Batch Processing**: Proses message dalam batch untuk efisiensi
-**Error Handling**: Robust error handling dengan retry logic
-**Authentication Support**: LOGIN dan AUTHENTICATE PLAIN methods
-**Special Characters**: Support password dengan karakter khusus
-**Debug Mode**: Detailed logging untuk troubleshooting
## Installation
@@ -33,207 +36,222 @@ Download binary dari releases page atau build sendiri.
```bash
# Basic migration
./mail-migrator --src "user1:pass1@imap.old.com:993" \
--dst "user1:pass1@imap.new.com:993"
./mail-migrator --src "user@domain.com:password@imap.source.com:993" \
--dst "user@domain.com:password@imap.dest.com:993" \
--insecure
# With insecure mode (trust self-signed certs)
./mail-migrator --src "user1:pass1@imap.old.com:993" \
--dst "user1:pass1@imap.new.com:993" \
--insecure
# With logging
./mail-migrator --src "user@domain.com:password@imap.source.com:993" \
--dst "user@domain.com:password@imap.dest.com:993" \
--insecure --log migration.log
# With resume support and logging
./mail-migrator --src "user1:pass1@imap.old.com:993" \
--dst "user1:pass1@imap.new.com:993" \
--insecure \
--resume \
--log migration.log
# With resume support
./mail-migrator --src "user@domain.com:password@imap.source.com:993" \
--dst "user@domain.com:password@imap.dest.com:993" \
--insecure --resume --log migration.log
```
### Batch Migration
Buat file CSV dengan format: `source,destination`
**accounts.csv:**
1. **Buat file CSV** (contoh: `accounts.csv`):
```csv
user1:pass1@old.com:993,user1:newpass1@new.com:993
user2:pass2@old.com:993,user2:newpass2@new.com:993
user3:pass3@old.com:993,user3:newpass3@new.com:993
user1@domain.com:password1@imap.source.com:993,user1@domain.com:password1@imap.dest.com:993
user2@domain.com:password2@imap.source.com:993,user2@domain.com:password2@imap.dest.com:993
info@trieltree.co.id:P@ssw0rd123@10.10.11.30:993,info@trieltree.co.id:P@ssw0rd123@10.10.11.24:993
```
2. **Jalankan batch migration**:
```bash
# Batch migration
./mail-migrator --batch accounts.csv --insecure --resume --log batch.log
./mail-migrator --batch accounts.csv --insecure --resume --log migration.log
```
## URL Format
Format URL untuk source dan destination:
```
username:password@hostname:port
```
### Contoh:
- `user@domain.com:mypassword@mail.server.com:993`
- `info@trieltree.co.id:P@ssw0rd123@10.10.11.30:993`
### Password dengan Karakter Khusus
Jika password mengandung karakter khusus (`@`, `:`, `/`, dll), gunakan URL encoding:
- `@``%40`
- `:``%3A`
- `/``%2F`
- `%``%25`
**Contoh:**
```bash
# Password: P@ssw0rd:123/test
# URL encoded: P%40ssw0rd%3A123%2Ftest
./mail-migrator --src "user:P%40ssw0rd%3A123%2Ftest@server:993" \
--dst "user:P%40ssw0rd%3A123%2Ftest@server:993" \
--insecure
```
## Command Line Options
| Flag | Description | Default |
|------|-------------|---------|
| `--src` | Source IMAP URL (user:pass@host:port) | - |
| `--dst` | Destination IMAP URL (user:pass@host:port) | - |
| `--batch` | CSV file with src,dst entries for batch migration | - |
| `--insecure` | Allow insecure TLS (trust self-signed certs) | false |
| `--resume` | Resume incomplete migrations | false |
| `--state` | Path to state DB (Bolt) | state.db |
| `--log` | Path to log file (optional) | - |
## URL Format
IMAP URL format: `username:password@hostname:port`
**Examples:**
- `john:secret123@mail.example.com:993` (IMAPS)
- `jane:mypass@imap.gmail.com:993` (Gmail)
- `user@domain.com:password@mail.server.com:143` (IMAP plain)
**Port defaults:**
- Port 993: IMAPS (TLS/SSL)
- Port 143: IMAP (plain/STARTTLS)
## Resume Functionality
Aplikasi menggunakan BoltDB untuk menyimpan state migrasi:
- **State Key**: `source|destination|mailbox`
- **Tracking**: Last migrated UID per mailbox
- **Resume**: Otomatis lanjut dari UID terakhir yang sukses
- **File**: Default `state.db` (bisa diubah dengan `--state`)
## Logging
- **Console**: Selalu aktif dengan timestamp
- **File**: Optional dengan flag `--log filename.log`
- **Format**: `[timestamp] [level] message`
- **Levels**: INFO, WARN, ERROR, DEBUG
## Error Handling
- **Connection errors**: Retry dengan backoff
- **Authentication**: Clear error message
- **Mailbox errors**: Skip dan lanjut ke mailbox berikutnya
- **Message errors**: Skip message yang corrupt, lanjut ke berikutnya
- **Batch errors**: Error di satu akun tidak stop seluruh batch
## Performance
- **Batch size**: 50 messages per batch (configurable dalam code)
- **Memory**: Efficient streaming untuk message besar
- **Concurrent**: Single-threaded untuk stability
- **Resume**: Minimal overhead dengan UID tracking
| `--src` | Source IMAP URL | Required |
| `--dst` | Destination IMAP URL | Required |
| `--batch` | CSV file untuk batch migration | - |
| `--insecure` | Skip SSL certificate verification | false |
| `--resume` | Resume interrupted migration | false |
| `--log` | Log file path | - |
## Troubleshooting
### Common Issues
### 1. Authentication Failed
**1. Certificate Errors**
```
x509: certificate signed by unknown authority
```
**Solution**: Gunakan flag `--insecure`
**Error**: `LOGIN Authentication failed`
**2. Authentication Failed**
```
login failed: Invalid credentials
```
**Solution**:
- Cek username/password
- Untuk Gmail: gunakan App Password, bukan password biasa
- Cek apakah IMAP enabled di server
**Solusi**:
1. **Pastikan credential benar**:
```bash
# Test dengan IMAP test tool
cd tools
go build -o imap-test.exe imap-test.go
./imap-test.exe <host> <port> <username> <password>
```
**3. Connection Timeout**
```
dial failed: i/o timeout
```
**Solution**:
- Cek hostname dan port
- Cek firewall/network connectivity
- Cek apakah server support IMAP
2. **Coba format username berbeda**:
- `username` (tanpa domain)
- `username@domain.com` (dengan domain)
- `username@mail.domain.com` (dengan subdomain)
**4. Mailbox Not Found**
```
select source INBOX: Mailbox doesn't exist
```
**Solution**:
- Cek apakah mailbox ada di source
- Beberapa server case-sensitive untuk nama mailbox
3. **Periksa server capabilities**:
- Tool akan otomatis mencoba LOGIN dan AUTHENTICATE PLAIN
- Check log untuk melihat supported authentication methods
### Debug Mode
### 2. SSL Certificate Error
Untuk debug lebih detail, edit code dan set:
```go
logrus.SetLevel(logrus.DebugLevel)
**Error**: `certificate verify failed`
**Solusi**:
```bash
# Gunakan flag --insecure untuk self-signed certificates
./mail-migrator --src "..." --dst "..." --insecure
```
### 3. Connection Timeout
**Error**: `connection timeout`
**Solusi**:
1. **Test koneksi manual**:
```bash
telnet <hostname> <port>
# atau
openssl s_client -connect <hostname>:<port>
```
2. **Periksa firewall dan network**
3. **Pastikan port benar** (biasanya 993 untuk IMAPS, 143 untuk IMAP)
### 4. Resume Migration
Jika migration terputus, gunakan `--resume` untuk melanjutkan:
```bash
./mail-migrator --src "..." --dst "..." --resume --log migration.log
```
State disimpan di file `state.db` menggunakan BoltDB.
### 5. Debug Mode
Untuk troubleshooting detail, check log file atau console output:
```bash
./mail-migrator --src "..." --dst "..." --log debug.log
```
Log akan menampilkan:
- Server capabilities
- Authentication attempts
- Message copy progress
- Error details
## Tools
### IMAP Test Tool
Tool untuk test koneksi dan authentication IMAP:
```bash
cd tools
go build -o imap-test.exe imap-test.go
./imap-test.exe <host> <port> <username> <password>
```
**Contoh**:
```bash
./imap-test.exe 10.10.11.30 993 info@trieltree.co.id P@ssw0rd123
```
Tool ini akan:
- Test koneksi SSL/TLS
- Tampilkan server capabilities
- Test berbagai format username
- Memberikan feedback detail untuk troubleshooting
## Examples
### Gmail to Gmail
### Contoh Sukses Migration
```bash
./mail-migrator --src "olduser@gmail.com:apppass1@imap.gmail.com:993" \
--dst "newuser@gmail.com:apppass2@imap.gmail.com:993" \
--insecure --resume --log gmail-migration.log
# Single account
./mail-migrator --src "info@trieltree.co.id:P@ssw0rd123@10.10.11.30:993" \
--dst "info@trieltree.co.id:P@ssw0rd123@10.10.11.24:993" \
--insecure --log migration.log
# Output:
# ✓ Connected to 10.10.11.30:993 as info@trieltree.co.id
# ✓ Connected to 10.10.11.24:993 as info@trieltree.co.id
# ✓ Found 6 mailboxes to migrate
# ✓ INBOX: 17 messages migrated
# ✓ Migration completed successfully
```
### Office365 Migration
### Batch Migration
```bash
./mail-migrator --src "user@old.com:password@outlook.office365.com:993" \
--dst "user@new.com:password@outlook.office365.com:993" \
--resume --log o365-migration.log
# File: accounts.csv
info@trieltree.co.id:P@ssw0rd123@10.10.11.30:993,info@trieltree.co.id:P@ssw0rd123@10.10.11.24:993
user2@domain.com:password2@10.10.11.30:993,user2@domain.com:password2@10.10.11.24:993
# Command:
./mail-migrator --batch accounts.csv --insecure --resume --log batch-migration.log
```
### Self-hosted with Self-signed Cert
```bash
./mail-migrator --src "user:pass@mail.old.local:993" \
--dst "user:pass@mail.new.local:993" \
--insecure --resume
```
## Technical Details
## Development
### Project Structure
```
├── main.go # CLI interface dan orchestration
├── imap.go # IMAP connection dan message copying
├── state.go # BoltDB state persistence
├── go.mod # Go module dependencies
└── README.md # Documentation
```
### Dependencies
- `github.com/emersion/go-imap` - IMAP client library
- `github.com/urfave/cli/v2` - CLI framework
- `go.etcd.io/bbolt` - Embedded key-value database
- `github.com/sirupsen/logrus` - Structured logging
### Building
```bash
# Development build
go run . --help
# Production build
go build -ldflags="-s -w" .
# Cross-compile for Linux
GOOS=linux GOARCH=amd64 go build .
```
- **Language**: Go
- **IMAP Library**: github.com/emersion/go-imap
- **State Storage**: BoltDB
- **Logging**: Logrus
- **Authentication**: LOGIN, AUTHENTICATE PLAIN
- **SSL/TLS**: Support dengan opsi insecure mode
## License
MIT License - see LICENSE file for details.
[Sesuai dengan license file]
## Contributing
1. Fork the repository
1. Fork repository
2. Create feature branch
3. Make changes
4. Add tests if applicable
5. Submit pull request
3. Commit changes
4. Push to branch
5. Create Pull Request
## Support
For issues and questions:
Jika mengalami masalah:
1. Check troubleshooting section
2. Search existing issues
3. Create new issue with:
- Command used
- Full error message
- Log output (with sensitive info removed)
2. Gunakan IMAP test tool untuk debug
3. Enable debug logging
4. Create issue dengan log detail

BIN
binary/imap-test.exe Normal file

Binary file not shown.

78
binary/migration.log Normal file
View File

@@ -0,0 +1,78 @@
time="2025-09-08T23:39:07+07:00" level=info msg="Starting migration agus.wahyudi:Pnd77net!@10.10.11.30:993 -> agus.wahyudi:P@ssw0rd123@10.10.11.24:993 (insecure=true, resume=true)"
time="2025-09-08T23:39:10+07:00" level=fatal msg="connect to source: login failed: LOGIN Authentication failed"
time="2025-09-08T23:39:49+07:00" level=info msg="Starting migration agus.wahyudi:'Pnd77net!'@10.10.11.30:993 -> agus.wahyudi:'P@ssw0rd123'@10.10.11.24:993 (insecure=true, resume=true)"
time="2025-09-08T23:39:52+07:00" level=fatal msg="connect to source: login failed: LOGIN Authentication failed"
time="2025-09-08T23:44:50+07:00" level=info msg="Starting migration agus.wahyudi:Pnd77net%21@10.10.11.30:993 -> agus.wahyudi:P%40ssw0rd123@10.10.11.24:993 (insecure=true, resume=false)"
time="2025-09-08T23:44:50+07:00" level=debug msg="Connecting to 10.10.11.30:993 with username: agus.wahyudi, password: *********"
time="2025-09-08T23:44:53+07:00" level=fatal msg="connect to source: login failed: LOGIN Authentication failed"
time="2025-09-08T23:44:59+07:00" level=info msg="Starting migration agus.wahyudi:Pnd77net!@10.10.11.30:993 -> agus.wahyudi:P@ssw0rd123@10.10.11.24:993 (insecure=true, resume=false)"
time="2025-09-08T23:44:59+07:00" level=debug msg="Connecting to 10.10.11.30:993 with username: agus.wahyudi, password: *********"
time="2025-09-08T23:45:02+07:00" level=fatal msg="connect to source: login failed: LOGIN Authentication failed"
time="2025-09-08T23:46:48+07:00" level=info msg="Starting migration agus.wahyudi:Pnd77net!@10.10.11.30:993 -> agus.wahyudi:P@ssw0rd123@10.10.11.24:993 (insecure=true, resume=false)"
time="2025-09-08T23:46:48+07:00" level=debug msg="Connecting to 10.10.11.30:993 with username: agus.wahyudi, password: *********"
time="2025-09-08T23:46:48+07:00" level=debug msg="Server capabilities: map[ACL:true ANNOTATE:true ANNOTATEMORE:true AUTH=CRAM-MD5:true AUTH=DIGEST-MD5:true AUTH=LOGIN:true AUTH=PLAIN:true ID:true IDLE:true IMAP4:true IMAP4rev1:true LISTEXT:true LITERAL+:true NAMESPACE:true QUOTA:true SORT:true SPECIAL-USE:true STATUS-COUNTERS:true UIDPLUS:true UNSELECT:true XAPPLEPUSHSERVICE:true XLIST:true]"
time="2025-09-08T23:46:51+07:00" level=fatal msg="connect to source: login failed: LOGIN Authentication failed"
time="2025-09-08T23:55:40+07:00" level=info msg="Starting migration agus.wahyudi:Pnd77net!@10.10.11.30:993 -> agus.wahyudi:P@ssw0rd123@10.10.11.24:993 (insecure=true, resume=false)"
time="2025-09-08T23:55:40+07:00" level=debug msg="Connecting to 10.10.11.30:993 with username: agus.wahyudi, password: *********"
time="2025-09-08T23:55:40+07:00" level=debug msg="Server capabilities: map[ACL:true ANNOTATE:true ANNOTATEMORE:true AUTH=CRAM-MD5:true AUTH=DIGEST-MD5:true AUTH=LOGIN:true AUTH=PLAIN:true ID:true IDLE:true IMAP4:true IMAP4rev1:true LISTEXT:true LITERAL+:true NAMESPACE:true QUOTA:true SORT:true SPECIAL-USE:true STATUS-COUNTERS:true UIDPLUS:true UNSELECT:true XAPPLEPUSHSERVICE:true XLIST:true]"
time="2025-09-08T23:55:43+07:00" level=debug msg="LOGIN failed: LOGIN Authentication failed"
time="2025-09-08T23:55:43+07:00" level=debug msg="Trying AUTHENTICATE PLAIN"
time="2025-09-08T23:55:49+07:00" level=fatal msg="connect to source: login failed: LOGIN Authentication failed (also tried AUTHENTICATE PLAIN: Authentication failed)"
time="2025-09-08T23:56:38+07:00" level=info msg="Starting migration agus.wahyudi@domain.com:Pnd77net!@10.10.11.30:993 -> agus.wahyudi@domain.com:P@ssw0rd123@10.10.11.24:993 (insecure=true, resume=false)"
time="2025-09-08T23:56:38+07:00" level=debug msg="Connecting to 10.10.11.30:993 with username: agus.wahyudi@domain.com, password: *********"
time="2025-09-08T23:56:38+07:00" level=debug msg="Server capabilities: map[ACL:true ANNOTATE:true ANNOTATEMORE:true AUTH=CRAM-MD5:true AUTH=DIGEST-MD5:true AUTH=LOGIN:true AUTH=PLAIN:true ID:true IDLE:true IMAP4:true IMAP4rev1:true LISTEXT:true LITERAL+:true NAMESPACE:true QUOTA:true SORT:true SPECIAL-USE:true STATUS-COUNTERS:true UIDPLUS:true UNSELECT:true XAPPLEPUSHSERVICE:true XLIST:true]"
time="2025-09-08T23:56:41+07:00" level=debug msg="LOGIN failed: LOGIN Authentication failed"
time="2025-09-08T23:56:41+07:00" level=debug msg="Trying AUTHENTICATE PLAIN"
time="2025-09-08T23:56:47+07:00" level=fatal msg="connect to source: login failed: LOGIN Authentication failed (also tried AUTHENTICATE PLAIN: Authentication failed)"
time="2025-09-08T23:57:04+07:00" level=info msg="Starting migration agus.wahyudi@adastra.id:Pnd77net!@10.10.11.30:993 -> agus.wahyudi@adastra.id:P@ssw0rd123@10.10.11.24:993 (insecure=true, resume=false)"
time="2025-09-08T23:57:04+07:00" level=debug msg="Connecting to 10.10.11.30:993 with username: agus.wahyudi@adastra.id, password: *********"
time="2025-09-08T23:57:04+07:00" level=debug msg="Server capabilities: map[ACL:true ANNOTATE:true ANNOTATEMORE:true AUTH=CRAM-MD5:true AUTH=DIGEST-MD5:true AUTH=LOGIN:true AUTH=PLAIN:true ID:true IDLE:true IMAP4:true IMAP4rev1:true LISTEXT:true LITERAL+:true NAMESPACE:true QUOTA:true SORT:true SPECIAL-USE:true STATUS-COUNTERS:true UIDPLUS:true UNSELECT:true XAPPLEPUSHSERVICE:true XLIST:true]"
time="2025-09-08T23:57:07+07:00" level=debug msg="LOGIN failed: LOGIN Authentication failed"
time="2025-09-08T23:57:07+07:00" level=debug msg="Trying AUTHENTICATE PLAIN"
time="2025-09-08T23:57:13+07:00" level=fatal msg="connect to source: login failed: LOGIN Authentication failed (also tried AUTHENTICATE PLAIN: Authentication failed)"
time="2025-09-09T00:01:05+07:00" level=info msg="Starting migration info@trieltree.co.id:P@ssw0rd123@10.10.11.30:993 -> info@trieltree.co.id:P@ssw0rd123@10.10.11.24:993 (insecure=true, resume=false)"
time="2025-09-09T00:01:05+07:00" level=debug msg="Connecting to 10.10.11.30:993 with username: info@trieltree.co.id, password: ***********"
time="2025-09-09T00:01:05+07:00" level=debug msg="Server capabilities: map[ACL:true ANNOTATE:true ANNOTATEMORE:true AUTH=CRAM-MD5:true AUTH=DIGEST-MD5:true AUTH=LOGIN:true AUTH=PLAIN:true ID:true IDLE:true IMAP4:true IMAP4rev1:true LISTEXT:true LITERAL+:true NAMESPACE:true QUOTA:true SORT:true SPECIAL-USE:true STATUS-COUNTERS:true UIDPLUS:true UNSELECT:true XAPPLEPUSHSERVICE:true XLIST:true]"
time="2025-09-09T00:01:05+07:00" level=debug msg="LOGIN succeeded"
time="2025-09-09T00:01:05+07:00" level=info msg="Connected to 10.10.11.30:993 as info@trieltree.co.id"
time="2025-09-09T00:01:05+07:00" level=debug msg="Connecting to 10.10.11.24:993 with username: info@trieltree.co.id, password: ***********"
time="2025-09-09T00:01:05+07:00" level=debug msg="Server capabilities: map[AUTH=LOGIN:true AUTH=PLAIN:true ENABLE:true ID:true IDLE:true IMAP4rev1:true LITERAL+:true LOGIN-REFERRALS:true SASL-IR:true]"
time="2025-09-09T00:01:05+07:00" level=debug msg="LOGIN succeeded"
time="2025-09-09T00:01:05+07:00" level=info msg="Connected to 10.10.11.24:993 as info@trieltree.co.id"
time="2025-09-09T00:01:05+07:00" level=info msg="Found 6 mailboxes to migrate"
time="2025-09-09T00:01:05+07:00" level=info msg="Migrating mailbox: INBOX"
time="2025-09-09T00:01:05+07:00" level=info msg="Migrating INBOX: 17 messages (resume from UID 0)"
time="2025-09-09T00:01:05+07:00" level=info msg="Found 17 messages to migrate"
time="2025-09-09T00:01:05+07:00" level=debug msg="Copied message UID 1"
time="2025-09-09T00:01:05+07:00" level=debug msg="Copied message UID 2"
time="2025-09-09T00:01:06+07:00" level=debug msg="Copied message UID 3"
time="2025-09-09T00:01:08+07:00" level=debug msg="Copied message UID 4"
time="2025-09-09T00:01:08+07:00" level=debug msg="Copied message UID 5"
time="2025-09-09T00:01:08+07:00" level=debug msg="Copied message UID 6"
time="2025-09-09T00:01:08+07:00" level=debug msg="Copied message UID 7"
time="2025-09-09T00:01:08+07:00" level=debug msg="Copied message UID 8"
time="2025-09-09T00:01:08+07:00" level=debug msg="Copied message UID 9"
time="2025-09-09T00:01:08+07:00" level=debug msg="Copied message UID 10"
time="2025-09-09T00:01:10+07:00" level=debug msg="Copied message UID 11"
time="2025-09-09T00:01:10+07:00" level=debug msg="Copied message UID 12"
time="2025-09-09T00:01:11+07:00" level=debug msg="Copied message UID 13"
time="2025-09-09T00:01:11+07:00" level=debug msg="Copied message UID 14"
time="2025-09-09T00:01:12+07:00" level=debug msg="Copied message UID 15"
time="2025-09-09T00:01:12+07:00" level=debug msg="Copied message UID 16"
time="2025-09-09T00:01:13+07:00" level=debug msg="Copied message UID 17"
time="2025-09-09T00:01:13+07:00" level=info msg="Migrated batch 1-17/17"
time="2025-09-09T00:01:13+07:00" level=info msg="Migrating mailbox: Deleted Items"
time="2025-09-09T00:01:13+07:00" level=info msg="Migrating Deleted Items: 0 messages (resume from UID 0)"
time="2025-09-09T00:01:13+07:00" level=info msg="No new messages to migrate in Deleted Items"
time="2025-09-09T00:01:13+07:00" level=info msg="Migrating mailbox: Drafts"
time="2025-09-09T00:01:13+07:00" level=info msg="Migrating Drafts: 0 messages (resume from UID 0)"
time="2025-09-09T00:01:13+07:00" level=info msg="No new messages to migrate in Drafts"
time="2025-09-09T00:01:13+07:00" level=info msg="Migrating mailbox: Junk E-mail"
time="2025-09-09T00:01:13+07:00" level=info msg="Migrating Junk E-mail: 0 messages (resume from UID 0)"
time="2025-09-09T00:01:13+07:00" level=info msg="No new messages to migrate in Junk E-mail"
time="2025-09-09T00:01:13+07:00" level=info msg="Migrating mailbox: Sent Items"
time="2025-09-09T00:01:13+07:00" level=info msg="Migrating Sent Items: 0 messages (resume from UID 0)"
time="2025-09-09T00:01:13+07:00" level=info msg="No new messages to migrate in Sent Items"
time="2025-09-09T00:01:13+07:00" level=info msg="Migrating mailbox: Public Folders"
time="2025-09-09T00:01:13+07:00" level=error msg="Failed to migrate mailbox Public Folders: select source Public Folders: SELECT Cannot access folder 'Public Folders'"
time="2025-09-09T00:01:13+07:00" level=info msg="Migration completed successfully"

BIN
binary/state.db Normal file

Binary file not shown.

View File

@@ -1,4 +1,3 @@
user1:password1@old-server.com:993,user1:newpassword1@new-server.com:993
user2:password2@old-server.com:993,user2:newpassword2@new-server.com:993
user3:password3@old-server.com:993,user3:newpassword3@new-server.com:993
admin@company.old:adminpass@mail.old.com:993,admin@company.new:adminpass@mail.new.com:993
info@trieltree.co.id:P@ssw0rd123@10.10.11.30:993,info@trieltree.co.id:P@ssw0rd123@10.10.11.24:993
user2@domain.com:password2@10.10.11.30:993,user2@domain.com:password2@10.10.11.24:993
test@example.com:testpass@mail.source.com:993,test@example.com:testpass@mail.dest.com:993
1 user1:password1@old-server.com:993 info@trieltree.co.id:P@ssw0rd123@10.10.11.30:993 user1:newpassword1@new-server.com:993 info@trieltree.co.id:P@ssw0rd123@10.10.11.24:993
2 user2:password2@old-server.com:993 user2@domain.com:password2@10.10.11.30:993 user2:newpassword2@new-server.com:993 user2@domain.com:password2@10.10.11.24:993
3 user3:password3@old-server.com:993 test@example.com:testpass@mail.source.com:993 user3:newpassword3@new-server.com:993 test@example.com:testpass@mail.dest.com:993
admin@company.old:adminpass@mail.old.com:993 admin@company.new:adminpass@mail.new.com:993

13
go.mod
View File

@@ -4,7 +4,16 @@ go 1.22
require (
github.com/emersion/go-imap v1.2.1
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21
github.com/sirupsen/logrus v1.9.3
github.com/urfave/cli/v2 v2.25.7
go.etcd.io/bbolt v1.3.8
github.com/sirupsen/logrus v1.9.3
)
)
require (
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
golang.org/x/sys v0.4.0 // indirect
golang.org/x/text v0.3.7 // indirect
)

51
imap.go
View File

@@ -10,6 +10,7 @@ import (
"github.com/emersion/go-imap"
"github.com/emersion/go-imap/client"
"github.com/emersion/go-sasl"
"github.com/sirupsen/logrus"
bolt "go.etcd.io/bbolt"
)
@@ -43,20 +44,37 @@ func parseIMAPURL(rawURL string) (*IMAPConfig, error) {
}
password, _ := u.User.Password()
// URL decode the password to handle special characters
if password != "" {
if decodedPassword, err := url.QueryUnescape(password); err == nil {
password = decodedPassword
}
}
username := u.User.Username()
// URL decode the username to handle special characters
if username != "" {
if decodedUsername, err := url.QueryUnescape(username); err == nil {
username = decodedUsername
}
}
return &IMAPConfig{
Host: u.Hostname(),
Port: port,
Username: u.User.Username(),
Username: username,
Password: password,
UseTLS: port == 993 || u.Scheme == "imaps",
}, nil
}
// connectIMAP creates IMAP client connection
func connectIMAP(cfg *IMAPConfig, insecure bool) (*client.Client, error) {
addr := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port)
// Debug logging (mask password for security)
maskedPassword := strings.Repeat("*", len(cfg.Password))
logrus.Debugf("Connecting to %s with username: %s, password: %s", addr, cfg.Username, maskedPassword)
var c *client.Client
var err error
@@ -74,9 +92,34 @@ func connectIMAP(cfg *IMAPConfig, insecure bool) (*client.Client, error) {
return nil, fmt.Errorf("dial failed: %w", err)
}
// Check server capabilities first
caps, errCaps := c.Capability()
if errCaps != nil {
logrus.Debugf("Failed to get capabilities: %v", errCaps)
caps = make(map[string]bool) // fallback to empty map
} else {
logrus.Debugf("Server capabilities: %v", caps)
}
// Try LOGIN first
if err := c.Login(cfg.Username, cfg.Password); err != nil {
c.Close()
return nil, fmt.Errorf("login failed: %w", err)
logrus.Debugf("LOGIN failed: %v", err)
// Try AUTHENTICATE PLAIN as fallback
if caps["AUTH=PLAIN"] || caps["AUTH=PLAIN-CLIENT-FIRST"] {
logrus.Debugf("Trying AUTHENTICATE PLAIN")
auth := sasl.NewPlainClient("", cfg.Username, cfg.Password)
if authErr := c.Authenticate(auth); authErr != nil {
c.Close()
return nil, fmt.Errorf("login failed: %w (also tried AUTHENTICATE PLAIN: %v)", err, authErr)
}
logrus.Debugf("AUTHENTICATE PLAIN succeeded")
} else {
c.Close()
return nil, fmt.Errorf("login failed: %w", err)
}
} else {
logrus.Debugf("LOGIN succeeded")
}
logrus.Infof("Connected to %s as %s", addr, cfg.Username)

View File

@@ -53,7 +53,7 @@ func main() {
func setupLogging(file string) {
logrus.SetFormatter(&logrus.TextFormatter{FullTimestamp: true})
logrus.SetLevel(logrus.InfoLevel)
logrus.SetLevel(logrus.DebugLevel) // Changed from InfoLevel to DebugLevel for troubleshooting
if file != "" {
f, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {

BIN
tools/imap-test.exe Normal file

Binary file not shown.

78
tools/imap-test.go Normal file
View File

@@ -0,0 +1,78 @@
package main
import (
"crypto/tls"
"fmt"
"log"
"os"
"github.com/emersion/go-imap/client"
)
func main() {
if len(os.Args) != 5 {
fmt.Println("Usage: imap-test <host> <port> <username> <password>")
fmt.Println("Example: imap-test 10.10.11.30 993 agus.wahyudi Pnd77net!")
os.Exit(1)
}
host := os.Args[1]
port := os.Args[2]
username := os.Args[3]
password := os.Args[4]
addr := fmt.Sprintf("%s:%s", host, port)
fmt.Printf("Testing IMAP connection to %s\n", addr)
fmt.Printf("Username: %s\n", username)
fmt.Printf("Password: %s\n", password)
// Connect with TLS
tlsConfig := &tls.Config{
ServerName: host,
InsecureSkipVerify: true,
}
c, err := client.DialTLS(addr, tlsConfig)
if err != nil {
log.Fatalf("Failed to connect: %v", err)
}
defer c.Close()
fmt.Println("✓ Connected successfully")
// Get capabilities
caps, err := c.Capability()
if err != nil {
fmt.Printf("Failed to get capabilities: %v\n", err)
} else {
fmt.Printf("Server capabilities: %v\n", caps)
}
// Try LOGIN
fmt.Println("Trying LOGIN...")
if err := c.Login(username, password); err != nil {
fmt.Printf("✗ LOGIN failed: %v\n", err)
// Try different username formats
testUsernames := []string{
username + "@adastra.id",
username + "@mail.adastra.id",
username,
}
for _, testUser := range testUsernames {
if testUser == username {
continue // already tried
}
fmt.Printf("Trying LOGIN with username: %s\n", testUser)
if err := c.Login(testUser, password); err != nil {
fmt.Printf("✗ LOGIN failed with %s: %v\n", testUser, err)
} else {
fmt.Printf("✓ LOGIN succeeded with username: %s\n", testUser)
return
}
}
} else {
fmt.Printf("✓ LOGIN succeeded with username: %s\n", username)
}
}