diff --git a/README.md b/README.md index bd2c508..5294ec1 100644 --- a/README.md +++ b/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 - ✅ **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 + ``` -**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 + # atau + openssl s_client -connect : + ``` + +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 +``` + +**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) \ No newline at end of file +2. Gunakan IMAP test tool untuk debug +3. Enable debug logging +4. Create issue dengan log detail \ No newline at end of file diff --git a/binary/imap-test.exe b/binary/imap-test.exe new file mode 100644 index 0000000..40c8fcb Binary files /dev/null and b/binary/imap-test.exe differ diff --git a/mail-migrator.exe b/binary/mail-migrator.exe similarity index 56% rename from mail-migrator.exe rename to binary/mail-migrator.exe index 81bf847..8667cf9 100644 Binary files a/mail-migrator.exe and b/binary/mail-migrator.exe differ diff --git a/binary/migration.log b/binary/migration.log new file mode 100644 index 0000000..e427dd8 --- /dev/null +++ b/binary/migration.log @@ -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" diff --git a/binary/state.db b/binary/state.db new file mode 100644 index 0000000..7ef2036 Binary files /dev/null and b/binary/state.db differ diff --git a/example-accounts.csv b/example-accounts.csv index 645a8a3..3ba97e6 100644 --- a/example-accounts.csv +++ b/example-accounts.csv @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/go.mod b/go.mod index 342e571..1a02f21 100644 --- a/go.mod +++ b/go.mod @@ -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 -) \ No newline at end of file +) + +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 +) diff --git a/imap.go b/imap.go index 9166db2..03b5f97 100644 --- a/imap.go +++ b/imap.go @@ -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) diff --git a/main.go b/main.go index 7c34fd1..eb4c7ae 100644 --- a/main.go +++ b/main.go @@ -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 { diff --git a/tools/imap-test.exe b/tools/imap-test.exe new file mode 100644 index 0000000..72492b7 Binary files /dev/null and b/tools/imap-test.exe differ diff --git a/tools/imap-test.go b/tools/imap-test.go new file mode 100644 index 0000000..a62d442 --- /dev/null +++ b/tools/imap-test.go @@ -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 ") + 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) + } +}