fix and recompile
This commit is contained in:
332
README.md
332
README.md
@@ -11,6 +11,9 @@ Email migration tools untuk memindahkan mailbox IMAP antar server dengan dukunga
|
|||||||
- ✅ **Mailbox Detection**: Otomatis detect dan migrate semua mailbox/folder
|
- ✅ **Mailbox Detection**: Otomatis detect dan migrate semua mailbox/folder
|
||||||
- ✅ **Batch Processing**: Proses message dalam batch untuk efisiensi
|
- ✅ **Batch Processing**: Proses message dalam batch untuk efisiensi
|
||||||
- ✅ **Error Handling**: Robust error handling dengan retry logic
|
- ✅ **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
|
## Installation
|
||||||
|
|
||||||
@@ -33,207 +36,222 @@ Download binary dari releases page atau build sendiri.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Basic migration
|
# Basic migration
|
||||||
./mail-migrator --src "user1:pass1@imap.old.com:993" \
|
./mail-migrator --src "user@domain.com:password@imap.source.com:993" \
|
||||||
--dst "user1:pass1@imap.new.com:993"
|
--dst "user@domain.com:password@imap.dest.com:993" \
|
||||||
|
--insecure
|
||||||
|
|
||||||
# With insecure mode (trust self-signed certs)
|
# With logging
|
||||||
./mail-migrator --src "user1:pass1@imap.old.com:993" \
|
./mail-migrator --src "user@domain.com:password@imap.source.com:993" \
|
||||||
--dst "user1:pass1@imap.new.com:993" \
|
--dst "user@domain.com:password@imap.dest.com:993" \
|
||||||
--insecure
|
--insecure --log migration.log
|
||||||
|
|
||||||
# With resume support and logging
|
# With resume support
|
||||||
./mail-migrator --src "user1:pass1@imap.old.com:993" \
|
./mail-migrator --src "user@domain.com:password@imap.source.com:993" \
|
||||||
--dst "user1:pass1@imap.new.com:993" \
|
--dst "user@domain.com:password@imap.dest.com:993" \
|
||||||
--insecure \
|
--insecure --resume --log migration.log
|
||||||
--resume \
|
|
||||||
--log migration.log
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Batch Migration
|
### Batch Migration
|
||||||
|
|
||||||
Buat file CSV dengan format: `source,destination`
|
1. **Buat file CSV** (contoh: `accounts.csv`):
|
||||||
|
|
||||||
**accounts.csv:**
|
|
||||||
```csv
|
```csv
|
||||||
user1:pass1@old.com:993,user1:newpass1@new.com:993
|
user1@domain.com:password1@imap.source.com:993,user1@domain.com:password1@imap.dest.com:993
|
||||||
user2:pass2@old.com:993,user2:newpass2@new.com:993
|
user2@domain.com:password2@imap.source.com:993,user2@domain.com:password2@imap.dest.com:993
|
||||||
user3:pass3@old.com:993,user3:newpass3@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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
2. **Jalankan batch migration**:
|
||||||
```bash
|
```bash
|
||||||
# Batch migration
|
./mail-migrator --batch accounts.csv --insecure --resume --log migration.log
|
||||||
./mail-migrator --batch accounts.csv --insecure --resume --log batch.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
|
## Command Line Options
|
||||||
|
|
||||||
| Flag | Description | Default |
|
| Flag | Description | Default |
|
||||||
|------|-------------|---------|
|
|------|-------------|---------|
|
||||||
| `--src` | Source IMAP URL (user:pass@host:port) | - |
|
| `--src` | Source IMAP URL | Required |
|
||||||
| `--dst` | Destination IMAP URL (user:pass@host:port) | - |
|
| `--dst` | Destination IMAP URL | Required |
|
||||||
| `--batch` | CSV file with src,dst entries for batch migration | - |
|
| `--batch` | CSV file untuk batch migration | - |
|
||||||
| `--insecure` | Allow insecure TLS (trust self-signed certs) | false |
|
| `--insecure` | Skip SSL certificate verification | false |
|
||||||
| `--resume` | Resume incomplete migrations | false |
|
| `--resume` | Resume interrupted migration | false |
|
||||||
| `--state` | Path to state DB (Bolt) | state.db |
|
| `--log` | Log file path | - |
|
||||||
| `--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
|
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### Common Issues
|
### 1. Authentication Failed
|
||||||
|
|
||||||
**1. Certificate Errors**
|
**Error**: `LOGIN Authentication failed`
|
||||||
```
|
|
||||||
x509: certificate signed by unknown authority
|
|
||||||
```
|
|
||||||
**Solution**: Gunakan flag `--insecure`
|
|
||||||
|
|
||||||
**2. Authentication Failed**
|
**Solusi**:
|
||||||
```
|
1. **Pastikan credential benar**:
|
||||||
login failed: Invalid credentials
|
```bash
|
||||||
```
|
# Test dengan IMAP test tool
|
||||||
**Solution**:
|
cd tools
|
||||||
- Cek username/password
|
go build -o imap-test.exe imap-test.go
|
||||||
- Untuk Gmail: gunakan App Password, bukan password biasa
|
./imap-test.exe <host> <port> <username> <password>
|
||||||
- Cek apakah IMAP enabled di server
|
```
|
||||||
|
|
||||||
**3. Connection Timeout**
|
2. **Coba format username berbeda**:
|
||||||
```
|
- `username` (tanpa domain)
|
||||||
dial failed: i/o timeout
|
- `username@domain.com` (dengan domain)
|
||||||
```
|
- `username@mail.domain.com` (dengan subdomain)
|
||||||
**Solution**:
|
|
||||||
- Cek hostname dan port
|
|
||||||
- Cek firewall/network connectivity
|
|
||||||
- Cek apakah server support IMAP
|
|
||||||
|
|
||||||
**4. Mailbox Not Found**
|
3. **Periksa server capabilities**:
|
||||||
```
|
- Tool akan otomatis mencoba LOGIN dan AUTHENTICATE PLAIN
|
||||||
select source INBOX: Mailbox doesn't exist
|
- Check log untuk melihat supported authentication methods
|
||||||
```
|
|
||||||
**Solution**:
|
|
||||||
- Cek apakah mailbox ada di source
|
|
||||||
- Beberapa server case-sensitive untuk nama mailbox
|
|
||||||
|
|
||||||
### Debug Mode
|
### 2. SSL Certificate Error
|
||||||
|
|
||||||
Untuk debug lebih detail, edit code dan set:
|
**Error**: `certificate verify failed`
|
||||||
```go
|
|
||||||
logrus.SetLevel(logrus.DebugLevel)
|
**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
|
## Examples
|
||||||
|
|
||||||
### Gmail to Gmail
|
### Contoh Sukses Migration
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./mail-migrator --src "olduser@gmail.com:apppass1@imap.gmail.com:993" \
|
# Single account
|
||||||
--dst "newuser@gmail.com:apppass2@imap.gmail.com:993" \
|
./mail-migrator --src "info@trieltree.co.id:P@ssw0rd123@10.10.11.30:993" \
|
||||||
--insecure --resume --log gmail-migration.log
|
--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
|
```bash
|
||||||
./mail-migrator --src "user@old.com:password@outlook.office365.com:993" \
|
# File: accounts.csv
|
||||||
--dst "user@new.com:password@outlook.office365.com:993" \
|
info@trieltree.co.id:P@ssw0rd123@10.10.11.30:993,info@trieltree.co.id:P@ssw0rd123@10.10.11.24:993
|
||||||
--resume --log o365-migration.log
|
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
|
## Technical Details
|
||||||
```bash
|
|
||||||
./mail-migrator --src "user:pass@mail.old.local:993" \
|
|
||||||
--dst "user:pass@mail.new.local:993" \
|
|
||||||
--insecure --resume
|
|
||||||
```
|
|
||||||
|
|
||||||
## Development
|
- **Language**: Go
|
||||||
|
- **IMAP Library**: github.com/emersion/go-imap
|
||||||
### Project Structure
|
- **State Storage**: BoltDB
|
||||||
```
|
- **Logging**: Logrus
|
||||||
├── main.go # CLI interface dan orchestration
|
- **Authentication**: LOGIN, AUTHENTICATE PLAIN
|
||||||
├── imap.go # IMAP connection dan message copying
|
- **SSL/TLS**: Support dengan opsi insecure mode
|
||||||
├── 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 .
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
MIT License - see LICENSE file for details.
|
[Sesuai dengan license file]
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
1. Fork the repository
|
1. Fork repository
|
||||||
2. Create feature branch
|
2. Create feature branch
|
||||||
3. Make changes
|
3. Commit changes
|
||||||
4. Add tests if applicable
|
4. Push to branch
|
||||||
5. Submit pull request
|
5. Create Pull Request
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
For issues and questions:
|
Jika mengalami masalah:
|
||||||
1. Check troubleshooting section
|
1. Check troubleshooting section
|
||||||
2. Search existing issues
|
2. Gunakan IMAP test tool untuk debug
|
||||||
3. Create new issue with:
|
3. Enable debug logging
|
||||||
- Command used
|
4. Create issue dengan log detail
|
||||||
- Full error message
|
|
||||||
- Log output (with sensitive info removed)
|
|
||||||
BIN
binary/imap-test.exe
Normal file
BIN
binary/imap-test.exe
Normal file
Binary file not shown.
Binary file not shown.
78
binary/migration.log
Normal file
78
binary/migration.log
Normal 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
BIN
binary/state.db
Normal file
Binary file not shown.
@@ -1,4 +1,3 @@
|
|||||||
user1:password1@old-server.com:993,user1:newpassword1@new-server.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:password2@old-server.com:993,user2:newpassword2@new-server.com:993
|
user2@domain.com:password2@10.10.11.30:993,user2@domain.com:password2@10.10.11.24:993
|
||||||
user3:password3@old-server.com:993,user3:newpassword3@new-server.com:993
|
test@example.com:testpass@mail.source.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
|
|
||||||
|
11
go.mod
11
go.mod
@@ -4,7 +4,16 @@ go 1.22
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/emersion/go-imap v1.2.1
|
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
|
github.com/urfave/cli/v2 v2.25.7
|
||||||
go.etcd.io/bbolt v1.3.8
|
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
51
imap.go
@@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
"github.com/emersion/go-imap"
|
"github.com/emersion/go-imap"
|
||||||
"github.com/emersion/go-imap/client"
|
"github.com/emersion/go-imap/client"
|
||||||
|
"github.com/emersion/go-sasl"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
bolt "go.etcd.io/bbolt"
|
bolt "go.etcd.io/bbolt"
|
||||||
)
|
)
|
||||||
@@ -43,20 +44,37 @@ func parseIMAPURL(rawURL string) (*IMAPConfig, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
password, _ := u.User.Password()
|
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{
|
return &IMAPConfig{
|
||||||
Host: u.Hostname(),
|
Host: u.Hostname(),
|
||||||
Port: port,
|
Port: port,
|
||||||
Username: u.User.Username(),
|
Username: username,
|
||||||
Password: password,
|
Password: password,
|
||||||
UseTLS: port == 993 || u.Scheme == "imaps",
|
UseTLS: port == 993 || u.Scheme == "imaps",
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// connectIMAP creates IMAP client connection
|
|
||||||
func connectIMAP(cfg *IMAPConfig, insecure bool) (*client.Client, error) {
|
func connectIMAP(cfg *IMAPConfig, insecure bool) (*client.Client, error) {
|
||||||
addr := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port)
|
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 c *client.Client
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
@@ -74,9 +92,34 @@ func connectIMAP(cfg *IMAPConfig, insecure bool) (*client.Client, error) {
|
|||||||
return nil, fmt.Errorf("dial failed: %w", err)
|
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 {
|
if err := c.Login(cfg.Username, cfg.Password); err != nil {
|
||||||
c.Close()
|
logrus.Debugf("LOGIN failed: %v", err)
|
||||||
return nil, fmt.Errorf("login failed: %w", 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)
|
logrus.Infof("Connected to %s as %s", addr, cfg.Username)
|
||||||
|
|||||||
2
main.go
2
main.go
@@ -53,7 +53,7 @@ func main() {
|
|||||||
|
|
||||||
func setupLogging(file string) {
|
func setupLogging(file string) {
|
||||||
logrus.SetFormatter(&logrus.TextFormatter{FullTimestamp: true})
|
logrus.SetFormatter(&logrus.TextFormatter{FullTimestamp: true})
|
||||||
logrus.SetLevel(logrus.InfoLevel)
|
logrus.SetLevel(logrus.DebugLevel) // Changed from InfoLevel to DebugLevel for troubleshooting
|
||||||
if file != "" {
|
if file != "" {
|
||||||
f, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
f, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
BIN
tools/imap-test.exe
Normal file
BIN
tools/imap-test.exe
Normal file
Binary file not shown.
78
tools/imap-test.go
Normal file
78
tools/imap-test.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user