Skip to content

Latest commit

 

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 

Repository files navigation

Cyrus Mailbox Cleaner

License: MIT Shell Script Status

A standalone cleanup tool for Cyrus IMAP when native expiration mechanisms (cyr_expire) are unavailable. Offers direct filesystem-level cleaning of old messages from specific folders with full logging, safety validation, and automatic index reconstruction.

Features

Multi-Mode Operation

  • Single user cleanup (--user)
  • All users cleanup (--all)
  • Dry-run simulation (--dry-run)
  • Automated mode for cron (--auto)
  • Show all users including those with 0 files to clean (--show-all)

Easy Installation

  • One-command setup (--install)
  • Automatic cron scheduling with interactive frequency selection
  • Auto-detection of script path
  • Safe uninstall with log archiving (--uninstall)

Production-Ready Security

  • Multi-layer path validation
  • Symlink protection
  • Username sanitization (alphanumeric + ., -, _ only)
  • Empty variable guards
  • Recursive safe deletion with regex filtering

Comprehensive Logging

  • Session-based tracking with unique IDs
  • Timestamped entries with severity levels (INFO/WARNING/ERROR)
  • Execution time tracking
  • Automatic log rotation (12 weeks retention)

Battle-Tested

  • Used in production mail environments
  • Handles edge cases (invalid usernames, missing folders, symlinks)
  • Automatic Cyrus index reconstruction after cleanup

Quick Start

Note: All commands must be run as root or with sudo privileges.

Installation

# Download the script
git clone https://github.com/RyzuOPs/cyrus-mailbox-cleaner.git
cd cyrus-mailbox-cleaner

# Set executable permissions
chmod +x cyrus-mailbox-cleaner

# Initialize log, logrotate, and optionally setup cron
./cyrus-mailbox-cleaner --install

# During installation you'll be asked:
# - Create log file: /var/log/cyrus_clean.log
# - Setup logrotate (12 weeks rotation)
# - Add to cron? (optional - choose frequency: daily/weekly/monthly/custom)

Basic Usage

# Test with dry-run (safe, nothing deleted)
./cyrus-mailbox-cleaner --user johndoe --dry-run

# Clean specific user
./cyrus-mailbox-cleaner --user johndoe

# Clean all users (with confirmation prompt)
./cyrus-mailbox-cleaner --all

# Automated cleanup (no prompts, for cron)
./cyrus-mailbox-cleaner --all --auto

Requirements

  • OS: Debian 7+ (compatible with most Linux distributions)
  • Cyrus IMAP: 2.x or newer
  • Permissions: root/sudo access
  • Dependencies:
    • find (GNU findutils)
    • awk
    • bash 4.0+
    • cyrus-imapd (reconstruct binary)

Configuration

Edit the following variables in the script (lines 15-19):

SPOOL_DIR="/cyrus/imap/user"                    # Cyrus user directory
RECONSTRUCT_BIN="/usr/cyrus/bin/reconstruct"   # Path to reconstruct
LOG_FILE="/var/log/cyrus_clean.log"            # Log file location
DAYS_OLD=30                                     # Age threshold (days)
TARGET_FOLDERS=("Trash" "Spam" "Junk" "Quarantine")  # Folders to clean

Usage Examples

Single User Cleanup with Preview

# See what would be deleted (dry-run)
./cyrus-mailbox-cleaner --user alice --dry-run

# Output:
# ==========================================
# [DRY-RUN MODE] Simulation for: alice
# Files to delete: 150
# Space to reclaim: 45.2 MB
# ==========================================
# --- [DRY-RUN] Simulating cleanup: alice (older than 30 days) ---
#    [DRY-RUN] Trash: Would delete 120 files (40.5 MB)
#    [DRY-RUN] Spam: Would delete 30 files (4.7 MB)
# ...

# Review the preview, then execute
./cyrus-mailbox-cleaner --user alice

All Users Cleanup

# Dry-run for all users (shows only users with files to delete)
./cyrus-mailbox-cleaner --all --dry-run

# Show all users including those with 0 files or validation errors
./cyrus-mailbox-cleaner --all --dry-run --show-all

# Execute cleanup
./cyrus-mailbox-cleaner --all --auto

Automated Daily Cleanup (Cron)

Option 1: During Installation

The easiest way is to let --install set it up for you:

./cyrus-mailbox-cleaner --install

# You'll see:
# ==========================================
# CRON SETUP (OPTIONAL)
# ==========================================
# Add automatic cleanup to cron? (y/n): y
#
# Select cleanup frequency:
#   1) Daily (every day at 2:00 AM)
#   2) Weekly (Sunday at 2:00 AM)
#   3) Monthly (1st day of month at 2:00 AM)
#   4) Custom cron expression
#
# Choice [1-4]: 1
#
# [OK] Cron job added successfully!
# Schedule: Daily at 2:00 AM
# Command: /path/to/cyrus-mailbox-cleaner --all --auto >> /var/log/cyrus_clean.log 2>&1

Option 2: Manual Setup

Add to /etc/cron.d/cyrus-cleanup or root's crontab:

# Daily at 2:00 AM
0 2 * * * root /usr/local/bin/cyrus-mailbox-cleaner --all --auto >> /var/log/cyrus_clean.log 2>&1

# Weekly on Sunday at 2:00 AM
0 2 * * 0 root /usr/local/bin/cyrus-mailbox-cleaner --all --auto >> /var/log/cyrus_clean.log 2>&1

# Monthly on 1st at 2:00 AM
0 2 1 * * root /usr/local/bin/cyrus-mailbox-cleaner --all --auto >> /var/log/cyrus_clean.log 2>&1

Note: The >> /var/log/cyrus_clean.log 2>&1 ensures all output (including cron errors) goes to the main log file.

Managing Cron Jobs

# View installed cron jobs
crontab -l

# Edit cron jobs manually
crontab -e

# Remove cron job during uninstall
./cyrus-mailbox-cleaner --uninstall

Log Analysis

Log Format

Each log entry follows this structure:

TIMESTAMP [SESSION_ID] SEVERITY: message

Example session log:

2026-03-11 15:01:34 [9350-1773237694] INFO: SESSION_START | Mode: --user alice --dry-run
2026-03-11 15:01:34 [9350-1773237694] INFO: START: Cleaning alice | DRY-RUN
2026-03-11 15:01:34 [9350-1773237694] INFO: USER: alice | FOLDER: Trash | DELETED: 120 files | SIZE: 40.5 MB | DRY-RUN
2026-03-11 15:01:34 [9350-1773237694] INFO: USER_FINISH: alice | TOTAL: 150 files | SIZE: 45.2 MB | DRY-RUN
2026-03-11 15:01:34 [9350-1773237694] INFO: SESSION_END | User: alice | Files: 150 | Size: 45.2 MB | Time: 0s | DRY-RUN

Useful Log Commands

# View last session
tail -50 /var/log/cyrus_clean.log

# Find specific session by ID
grep "\[9350-1773237694\]" /var/log/cyrus_clean.log

# Show only errors
grep "ERROR" /var/log/cyrus_clean.log

# Daily statistics
grep "SESSION_END" /var/log/cyrus_clean.log | grep "$(date +%Y-%m-%d)"

# Total files deleted today
grep "$(date +%Y-%m-%d)" /var/log/cyrus_clean.log | grep "SESSION_END" | \
  awk -F'Files: ' '{print $2}' | awk '{sum+=$1} END {print sum " files"}'

Command-Line Options

Option Description
--user <name> Clean folders for specific user
--all Clean folders for all users
--auto Automatic mode (no confirmation prompts, for cron)
--dry-run Simulation mode - shows what would be deleted without actual deletion
--show-all Show all users in --all mode, including those with 0 files or errors
--install Create log file, logrotate config, and optionally setup cron automation
--uninstall Archive log and remove configuration and cron job
--help Display help message

Security Features

Username Validation

Only alphanumeric characters, dots (.), hyphens (-), and underscores (_) are allowed. Invalid usernames are logged and skipped.

Rejected examples:

  • "user with spaces" (contains spaces)
  • "user'name" (contains apostrophe)
  • "user@domain" (contains @ symbol)

Symlink Protection

Directories that are symbolic links are automatically detected and skipped to prevent symlink escape attacks.

Path Validation

All paths must match the pattern: /cyrus/imap/user/<user>/<folder>

Any attempt to escape this structure is blocked.

Empty Variable Protection

Empty values for $user or $folder trigger immediate error and return, preventing accidental deletion of parent directories.

Cyrus Index Reconstruction

After file deletion, the script automatically runs:

/usr/cyrus/bin/reconstruct -rf user.<user>.<folder>

(executed as cyrus user)

This ensures Cyrus IMAP database consistency.

Troubleshooting

"ERROR: Directory /cyrus/imap/user does not exist"

Cause: Incorrect SPOOL_DIR path
Solution: Edit script line 16 to match your Cyrus installation path

"ERROR: Binary /usr/cyrus/bin/reconstruct does not exist"

Cause: reconstruct binary in different location
Solution:

# Find reconstruct location
which reconstruct
find /usr -name reconstruct 2>/dev/null

# Edit RECONSTRUCT_BIN in script (line 17)

Reconstruction Warnings

Symptom: [WARNING] Trash: Index reconstruction failed!

Cyrus reconstruct may report warnings even when successful (e.g., folder was already consistent). Usually safe to ignore, but check Cyrus logs if persistent.

Invalid Username Errors in Log

Example: ERROR: Invalid username: _invalid=user=name

This is normal behavior. Cyrus may contain directories with invalid names from migrations or errors. The script safely skips these.

Uninstallation

The --uninstall command handles everything automatically:

# Archive log and remove all configuration (log, logrotate, cron)
./cyrus-mailbox-cleaner --uninstall

# Output:
# ==========================================
# UNINSTALLATION: Cyrus Mailbox Cleaner
# ==========================================
# Archiving log to ./log/cyrus_clean_20260318_150234.log... [OK]
# Removing /var/log/cyrus_clean.log... [OK]
# Removing logrotate configuration... [OK]
# Removing cron job... [OK]
#
# ==========================================
# UNINSTALLATION COMPLETED
# ==========================================

Manual removal (if needed):

# Remove cron job manually
crontab -e  # Remove line with cyrus-mailbox-cleaner

# Remove script
rm /usr/local/bin/cyrus-mailbox-cleaner

How It Works

  1. Discovery: Scans /cyrus/imap/user/ for user directories
  2. Validation: Validates usernames, paths, and checks for symlinks
  3. Analysis: Uses GNU find with regex to locate Cyrus mail files ([0-9]+\.$) older than threshold
  4. Deletion: Removes matching files (if not dry-run)
  5. Reconstruction: Rebuilds Cyrus IMAP indexes for cleaned folders
  6. Logging: Records session with unique ID, timestamps, and statistics

Targeted File Pattern

The script uses POSIX Extended regex to match Cyrus mail files:

.*/[0-9]+\.$

This matches paths ending with one or more digits followed by a period (e.g., 123., 4567.).

Recursive Cleaning

The script cleans recursively through all subfolders. For example:

  • Trash/123.
  • Trash/Important/456.
  • Trash/2024/Archive/789.

Contributing

Contributions are welcome! Please:

  1. Test changes with --dry-run extensively
  2. Maintain existing security validations
  3. Update documentation for new features
  4. Follow bash best practices (shellcheck compliant)

License

MIT License - see script header for details

Authors

  • Łukasz Ryszkiewicz - Initial development and maintenance
  • GitHub: @RyzuOPs

Acknowledgments

Battle-tested in production mail environments serving thousands of users. Special thanks to the Cyrus IMAP community for the robust mail server foundation.


⚠️ Important: Always test with --dry-run before production deployment. While the script has multiple safety layers, email deletion is permanent and irreversible.

About

Safe and automated cleanup tool for Cyrus IMAP mail folders (Trash, Spam, Junk, Quarantine)

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages