How to Automate PST Exports and Downloads in Barracuda Cloud Archiving Service

Picture of Bullish Coder

Bullish Coder

If you are a global administrator tasked with migrating data out of the Barracuda Cloud Archiving Service (BCAS), you already know the pain of the native export process. Exporting years of data usually requires splitting searches into smaller timeframes (like front-half and back-half of the year), initiating the export, and then manually clicking through dozens of 4.7GB chunk files to download them.

When you are migrating terabytes of data, doing this manually is not just tedious—it’s prone to human error.

To speed up this migration project, you can use Vanilla JavaScript directly in your browser’s Developer Console to automate the UI interactions. Below are two scripts: one to automate queuing up the export searches, and a second to automate downloading the resulting PST files.

Disclaimer: These scripts interact with the DOM elements of the Barracuda web interface. If Barracuda updates their UI, these scripts may need adjustments. Always test with a small batch first, and use these at your own risk.


Part 1: Automating the Export Queuing

The first step in the migration is triggering the export tasks. Instead of manually entering date ranges, clicking search, opening the tools menu, and configuring the export window for every single block of time, this script does it for you.

How it works:

It loops through a predefined array of names and date ranges. For each item, it inputs the start and end dates, clicks “Search”, waits for the grid to load, opens the “Export Messages” tool, names the export, selects the “4.7GB – DVD” chunk size, and submits the job.

Instructions:

  1. Log into your Barracuda Cloud Archiving Service and navigate to the Search tab.
  2. Ensure your search criteria rows are set exactly to:
    • Row 1: Email | Date | on or after
    • Row 2: Email | Date | on or before
  3. Press F12 (or Ctrl+Shift+J on Windows / Cmd+Option+J on Mac) to open the browser’s Developer Tools, and navigate to the Console tab.
  4. Modify the exportTasks array in the script below to fit your desired date ranges, paste it into the console, and press Enter.
(async function automateExports() {
    // List of all your export tasks. Modify this array as needed for your migration.
    const exportTasks = [
        { name: "2018 - Back Half", start: "2018-06-01 00:00:00 -0500", end: "2018-12-31 00:00:00 -0500" },
        { name: "2019 - Front Half", start: "2019-01-01 00:00:00 -0500", end: "2019-05-31 00:00:00 -0500" },
        { name: "2019 - Back Half", start: "2019-06-01 00:00:00 -0500", end: "2019-12-31 00:00:00 -0500" },
        { name: "2020 - Front Half", start: "2020-01-01 00:00:00 -0500", end: "2020-05-31 00:00:00 -0500" },
        { name: "2020 - Back Half", start: "2020-06-01 00:00:00 -0500", end: "2020-12-31 00:00:00 -0500" }
        // Add additional years as needed...
    ];

    // Helper to pause execution to allow the UI to load
    const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

    for (let i = 0; i < exportTasks.length; i++) {
        let task = exportTasks[i];
        console.log(`[%c${i+1}/${exportTasks.length}] Starting task: ${task.name}...`, 'color: #0654ba; font-weight: bold;');

        // 1. Enter start date and end date
        let dateInputs = document.querySelectorAll('.filter_row_value');
        if (dateInputs.length >= 2) {
            dateInputs[0].value = task.start;
            dateInputs[1].value = task.end;
            dateInputs[0].dispatchEvent(new Event('change', { bubbles: true }));
            dateInputs[1].dispatchEvent(new Event('change', { bubbles: true }));
        } else {
            console.error("Could not find the date input fields. Make sure the search rows are set up correctly.");
            return;
        }

        // 2. Click Search
        let searchBtn = document.getElementById('filter_search_button');
        if (searchBtn) searchBtn.click();
        else return console.error("Could not find the 'Search' button.");

        // 3. Wait 5 seconds for the search grid to populate
        console.log("Waiting 5 seconds for search results...");
        await sleep(5000);

        // 4. Click 'Tools'
        let toolsBtn = Array.from(document.querySelectorAll('button')).find(el => el.textContent.trim() === 'Tools');
        if (toolsBtn) toolsBtn.click();
        else return console.error("Could not find the 'Tools' button.");
        
        await sleep(1000);

        // 5. Click 'Export Messages'
        let exportBtn = Array.from(document.querySelectorAll('.x-menu-item-text, a.x-menu-item')).find(el => el.textContent.includes('Export Messages'));
        if (exportBtn) exportBtn.click();
        else return console.error("Could not find the 'Export Messages' option.");
        
        await sleep(2000); 

        // 6. Handle Modal interactions
        let exportWindows = Array.from(document.querySelectorAll('.x-window')).filter(w => w.style.visibility !== 'hidden' && w.textContent.includes('Export Messages'));
        
        if (exportWindows.length > 0) {
            let activeWin = exportWindows[exportWindows.length - 1]; 

            // Set Export Name
            let nameInput = activeWin.querySelector('input[type="text"]');
            if (nameInput) {
                nameInput.value = task.name;
                nameInput.dispatchEvent(new Event('change', { bubbles: true }));
            }

            // 7. Select "4.7GB - DVD" Chunk Size
            let chunkInputs = Array.from(activeWin.querySelectorAll('input[type="text"]'));
            let comboInput = chunkInputs.find(input => input.value.includes('MB') || input.value.includes('GB'));
            
            if (comboInput) {
                let dropdownTrigger = comboInput.parentElement.querySelector('.x-form-trigger');
                if (dropdownTrigger) {
                    dropdownTrigger.click(); 
                    await sleep(500); 

                    let visibleComboLists = Array.from(document.querySelectorAll('.x-combo-list')).filter(list => list.style.visibility !== 'hidden' && list.style.display !== 'none');
                    for (let list of visibleComboLists) {
                        let option = Array.from(list.querySelectorAll('.x-combo-list-item')).find(o => o.textContent.includes('4.7GB'));
                        if (option) {
                            option.click();
                            break;
                        }
                    }
                }
            }
            await sleep(1000);

            // 8. Click 'OK'
            let okBtn = Array.from(activeWin.querySelectorAll('button')).find(b => b.textContent.trim() === 'OK');
            if (okBtn) okBtn.click();

            console.log(`%cSuccessfully queued export: ${task.name}`, 'color: green;');
            console.log("Waiting 5 seconds before starting the next job...");
            await sleep(5000); 

        } else {
            console.error("The 'Export Messages' pop-up window could not be found.");
            return;
        }
    }
    console.log("%cAll export tasks have been queued successfully!", 'color: green; font-weight: bold; font-size: 14px;');
})();

Note: Do not click around the page while this script is running, as it relies on interacting with the dynamic menus.


Part 2: Automating the Downloads

Once your tasks finish retrieving and exporting the messages on the backend, you are left with a massive list of tasks on the Tasks tab. If an export contains 50GB of data, clicking through 12 different 4.7GB chunk files manually is exhausting.

This script iterates through the tasks you manually select. It opens the download prompt, clicks every single PST link with a 60-second delay between clicks to prevent browser throttling, marks the link visually as “In Progress”, closes the window, and moves to the next selected task.

Instructions:

  1. Navigate to the Tasks tab in Barracuda.
  2. Check the boxes next to the fully exported tasks you are ready to download.
  3. Open the Developer Console (F12).
  4. Paste the script below and hit Enter.
(async function automateDownloads() {
    const STORAGE_KEY = 'barracuda_downloads';
    const DELAY_BETWEEN_CLICKS = 5000; 
    
    let isPaused = false;
    let isRunning = false;
    let skipWaitFlag = false; 
    let currentSidebarWidthPct = 33; 
    
    let updateUITable = () => {}; 
    let updateTaskStatus = (index, status, color) => {}; 
    let updateTimerText = (text, color = '#333') => {};
    let toggleSkipBtn = (enabled) => {};
    let syncPauseBtnUI = () => {};

    const getBatchSize = () => Math.max(1, parseInt(document.getElementById('ui-batch-size').value) || 3);
    const getWaitMinutes = () => Math.max(0, parseInt(document.getElementById('ui-wait-minutes').value) || 1);

    // --- KEEP-ALIVE HEARTBEAT ---
    setInterval(() => {
        fetch(window.location.href)
            .then(() => console.log("%c[Keep-Alive] Sent silent ping to server to maintain session.", "color: #00bcd4; font-style: italic; font-size: 11px;"))
            .catch(err => console.warn("[Keep-Alive] Ping failed. Session might be at risk.", err));
    }, 4 * 60 * 1000);

    const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
    
    const checkPause = async () => {
        while (isPaused && !skipWaitFlag) {
            await sleep(500);
        }
        if (isPaused && skipWaitFlag) {
            isPaused = false;
            syncPauseBtnUI();
        }
    };

    const smartWait = async (ms) => {
        let elapsed = 0;
        while (elapsed < ms) {
            if (skipWaitFlag) break;
            await checkPause(); 
            await sleep(1000);
            elapsed += 1000;
        }
    };

    const countdownWait = async (ms) => {
        let elapsed = 0;
        skipWaitFlag = false; 
        toggleSkipBtn(true);
        
        while (elapsed < ms) {
            if (skipWaitFlag) {
                console.log("%cWait skipped by user!", "color: purple; font-weight: bold;");
                break;
            }
            await checkPause(); 
            
            let remaining = ms - elapsed;
            let minutes = Math.floor(remaining / 60000);
            let seconds = Math.floor((remaining % 60000) / 1000);
            updateTimerText(`Status: Waiting ${minutes}:${seconds.toString().padStart(2, '0')} until next batch`, '#d32f2f');

            await sleep(1000);
            elapsed += 1000;
        }
        
        toggleSkipBtn(false);
        skipWaitFlag = false;
        updateTimerText('Status: Working...', '#4CAF50');
    };

    const getDownloaded = () => JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
    const markDownloaded = (fileName) => {
        let arr = getDownloaded();
        if (!arr.includes(fileName)) {
            arr.push(fileName);
            localStorage.setItem(STORAGE_KEY, JSON.stringify(arr));
            updateUITable(); 
        }
    };

    const allRows = Array.from(document.querySelectorAll('.x-grid3-row'));
    const selectedRows = allRows.filter(row => {
        const isSelectedClass = row.classList.contains('x-grid3-row-selected');
        const checkbox = row.querySelector('.x-grid3-row-checker, input[type="checkbox"]');
        return isSelectedClass || (checkbox && checkbox.checked);
    });

    if (selectedRows.length === 0) {
        alert("No rows selected! Please select at least one task using the checkboxes before running the script.");
        return;
    }

    // --- UI BUILDER ---
    const buildUI = () => {
        if (document.getElementById('download-manager-ui')) {
            document.getElementById('download-manager-ui').remove();
        }

        // ExtJS monkey patch
        try {
            if (typeof window.Ext !== 'undefined' && !window._bbsExtPatched) {
                window._bbsExtPatched = true;
                if (window.Ext.lib && window.Ext.lib.Dom && window.Ext.lib.Dom.getViewWidth) {
                    const origGetViewWidth = window.Ext.lib.Dom.getViewWidth;
                    window.Ext.lib.Dom.getViewWidth = function(full) {
                        let w = origGetViewWidth.call(this, full);
                        let ui = document.getElementById('download-manager-ui');
                        return ui ? w - ui.offsetWidth : w;
                    };
                }
            }
        } catch (e) {
            console.warn("Could not patch ExtJS layout manager.", e);
        }

        // Aggressive CSS Squeezing
        document.body.style.setProperty('width', `calc(100vw - ${currentSidebarWidthPct}vw)`, 'important');
        document.body.style.setProperty('max-width', `calc(100vw - ${currentSidebarWidthPct}vw)`, 'important');
        document.body.style.setProperty('overflow-x', 'hidden', 'important');
        document.body.style.setProperty('position', 'absolute', 'important');
        document.body.style.setProperty('left', '0', 'important');
        document.body.style.setProperty('top', '0', 'important');

        // Main UI panel
        const ui = document.createElement('div');
        ui.id = 'download-manager-ui';
        ui.style.cssText = `position:fixed; top:0; right:0; width:${currentSidebarWidthPct}vw; height:100vh; background:#f5f5f5; z-index:999999; padding:15px 15px 15px 21px; box-shadow: -3px 0 10px rgba(0,0,0,0.15); font-family: sans-serif; color: #333; box-sizing: border-box; display: flex; flex-direction: column;`;
        
        // Resizer Drag Handle
        const resizer = document.createElement('div');
        resizer.style.cssText = 'position: absolute; top: 0; left: 0; width: 6px; height: 100%; cursor: col-resize; background: #dcdcdc; z-index: 1000000; border-right: 1px solid #bbb; display: flex; align-items: center; justify-content: center; transition: background 0.2s;';
        resizer.innerHTML = '<div style="display:flex;flex-direction:column;gap:4px;"><div style="width:2px;height:2px;background:#888;border-radius:50%"></div><div style="width:2px;height:2px;background:#888;border-radius:50%"></div><div style="width:2px;height:2px;background:#888;border-radius:50%"></div></div>';
        resizer.onmouseenter = () => resizer.style.background = '#b0b0b0';
        resizer.onmouseleave = () => resizer.style.background = '#dcdcdc';
        ui.appendChild(resizer);

        let isDragging = false;
        const dragStyle = document.createElement('style'); 

        resizer.addEventListener('mousedown', (e) => {
            isDragging = true;
            dragStyle.innerHTML = '* { user-select: none !important; cursor: col-resize !important; }';
            document.head.appendChild(dragStyle);
            e.preventDefault();
        });

        document.addEventListener('mousemove', (e) => {
            if (!isDragging) return;
            let newWidthPx = window.innerWidth - e.clientX;
            currentSidebarWidthPct = (newWidthPx / window.innerWidth) * 100;
            if (currentSidebarWidthPct < 15) currentSidebarWidthPct = 15;
            if (currentSidebarWidthPct > 60) currentSidebarWidthPct = 60;
            ui.style.width = `${currentSidebarWidthPct}vw`;
            document.body.style.setProperty('width', `calc(100vw - ${currentSidebarWidthPct}vw)`, 'important');
            document.body.style.setProperty('max-width', `calc(100vw - ${currentSidebarWidthPct}vw)`, 'important');
            window.dispatchEvent(new Event('resize')); 
        });

        document.addEventListener('mouseup', () => {
            if (isDragging) {
                isDragging = false;
                if (dragStyle.parentNode) document.head.removeChild(dragStyle);
            }
        });

        const header = document.createElement('h2');
        header.textContent = 'Script Manager';
        header.style.cssText = 'font-size: 18px; margin-top: 0; margin-bottom: 10px; flex-shrink: 0;';
        ui.appendChild(header);

        // --- TABS SYSTEM ---
        const tabsContainer = document.createElement('div');
        tabsContainer.style.cssText = 'display: flex; border-bottom: 2px solid #ddd; margin-bottom: 15px; flex-shrink: 0;';

        const overviewTabBtn = document.createElement('button');
        overviewTabBtn.textContent = 'Overview';
        overviewTabBtn.style.cssText = 'flex: 1; padding: 10px 0; cursor: pointer; background: transparent; border: none; border-bottom: 3px solid #2196F3; font-weight: bold; color: #2196F3; font-size: 14px; outline: none; transition: all 0.2s; margin-bottom: -2px;';

        const logTabBtn = document.createElement('button');
        logTabBtn.textContent = 'Log';
        logTabBtn.style.cssText = 'flex: 1; padding: 10px 0; cursor: pointer; background: transparent; border: none; border-bottom: 3px solid transparent; font-weight: bold; color: #777; font-size: 14px; outline: none; transition: all 0.2s; margin-bottom: -2px;';

        tabsContainer.appendChild(overviewTabBtn);
        tabsContainer.appendChild(logTabBtn);
        ui.appendChild(tabsContainer);

        // Content Containers
        const overviewContent = document.createElement('div');
        overviewContent.style.cssText = 'display: flex; flex-direction: column; flex-grow: 1; overflow-y: auto; padding-right: 5px; min-height: 0;';

        const logContent = document.createElement('div');
        logContent.style.cssText = 'display: none; flex-direction: column; flex-grow: 1; min-height: 0;';

        ui.appendChild(overviewContent);
        ui.appendChild(logContent);

        // Tab Switching Logic
        overviewTabBtn.onclick = () => {
            overviewTabBtn.style.borderBottomColor = '#2196F3';
            overviewTabBtn.style.color = '#2196F3';
            logTabBtn.style.borderBottomColor = 'transparent';
            logTabBtn.style.color = '#777';
            overviewContent.style.display = 'flex';
            logContent.style.display = 'none';
        };

        logTabBtn.onclick = () => {
            logTabBtn.style.borderBottomColor = '#2196F3';
            logTabBtn.style.color = '#2196F3';
            overviewTabBtn.style.borderBottomColor = 'transparent';
            overviewTabBtn.style.color = '#777';
            logContent.style.display = 'flex';
            overviewContent.style.display = 'none';
        };

        // --- OVERVIEW TAB CONTENT ---
        const configContainer = document.createElement('div');
        configContainer.style.cssText = 'background: white; border: 1px solid #ccc; padding: 10px; margin-bottom: 15px; border-radius: 4px; font-size: 13px; flex-shrink: 0;';

        const configHeader = document.createElement('div');
        configHeader.textContent = 'Settings (Updates Live)';
        configHeader.style.cssText = 'font-weight: bold; margin-bottom: 8px; border-bottom: 1px solid #eee; padding-bottom: 4px;';
        configContainer.appendChild(configHeader);

        const batchRow = document.createElement('div');
        batchRow.style.cssText = 'display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;';
        batchRow.innerHTML = `<label style="font-weight: normal;">Downloads per batch:</label><input type="number" id="ui-batch-size" min="1" value="3" style="width: 50px; padding: 4px; border: 1px solid #ccc; border-radius: 3px; text-align: center;">`;
        configContainer.appendChild(batchRow);

        const waitRow = document.createElement('div');
        waitRow.style.cssText = 'display: flex; justify-content: space-between; align-items: center;';
        waitRow.innerHTML = `<label style="font-weight: normal;">Minutes to wait:</label><input type="number" id="ui-wait-minutes" min="1" value="1" style="width: 50px; padding: 4px; border: 1px solid #ccc; border-radius: 3px; text-align: center;">`;
        configContainer.appendChild(waitRow);

        overviewContent.appendChild(configContainer);
        
        const controls = document.createElement('div');
        controls.style.cssText = 'display: flex; flex-direction: column; gap: 8px; margin-bottom: 15px; flex-shrink: 0;';
        
        const btnCSS = 'display: flex; justify-content: center; align-items: center; height: 38px; cursor: pointer; color: white; border: none; border-radius: 4px; font-weight: bold; font-size: 14px; box-sizing: border-box; margin: 0; padding: 0 10px; line-height: 1; text-decoration: none;';
        
        const startBtn = document.createElement('button');
        startBtn.textContent = '▶ Start Downloads';
        startBtn.style.cssText = btnCSS + ' background: #4CAF50;';
        
        const pauseBtn = document.createElement('button');
        pauseBtn.textContent = '⏸ Pause Script';
        pauseBtn.style.cssText = btnCSS + ' background: #ff9800; opacity: 0.5;';
        pauseBtn.disabled = true; 

        const skipBtn = document.createElement('button');
        skipBtn.textContent = '⏭ Skip Wait (Force Next)';
        skipBtn.style.cssText = btnCSS + ' background: #9c27b0; opacity: 0.5;';
        skipBtn.disabled = true; 

        controls.appendChild(startBtn);
        controls.appendChild(pauseBtn);
        controls.appendChild(skipBtn);
        overviewContent.appendChild(controls);
        
        const statusDisplay = document.createElement('div');
        statusDisplay.textContent = 'Status: Idle';
        statusDisplay.style.cssText = 'display: flex; justify-content: center; align-items: center; height: 38px; font-weight: bold; margin-bottom: 15px; font-size: 14px; padding: 0 10px; background: #e0e0e0; border-radius: 4px; flex-shrink: 0; box-sizing: border-box;';
        overviewContent.appendChild(statusDisplay);

        const tasksHeader = document.createElement('h3');
        tasksHeader.textContent = `Targeted Tasks (${selectedRows.length})`;
        tasksHeader.style.cssText = 'font-size: 14px; margin-bottom: 8px; border-bottom: 1px solid #ddd; padding-bottom: 4px; flex-shrink: 0;';
        overviewContent.appendChild(tasksHeader);

        const tasksContainer = document.createElement('div');
        tasksContainer.style.cssText = 'background: white; border: 1px solid #ccc; overflow-y: auto; font-size: 12px; padding: 5px; flex-shrink: 0; border-radius: 4px;';
        selectedRows.forEach((row, idx) => {
            const taskEl = document.createElement('div');
            taskEl.id = `ui-task-${idx}`;
            taskEl.style.cssText = 'padding: 6px 4px; border-bottom: 1px solid #eee; display: flex; justify-content: space-between; align-items: center;';
            taskEl.innerHTML = `<span>Task ${idx + 1}</span> <span style="color: #888; font-weight: bold;">Pending</span>`;
            tasksContainer.appendChild(taskEl);
        });
        overviewContent.appendChild(tasksContainer);

        // --- LOG TAB CONTENT ---
        const logControls = document.createElement('div');
        logControls.style.cssText = 'margin-bottom: 10px; flex-shrink: 0;';
        const requeueBtn = document.createElement('button');
        requeueBtn.textContent = '↺ Re-queue Selected Files';
        requeueBtn.style.cssText = btnCSS + ' background: #2196F3; width: 100%;';
        logControls.appendChild(requeueBtn);
        logContent.appendChild(logControls);

        const tableContainer = document.createElement('div');
        tableContainer.style.cssText = 'flex-grow: 1; overflow-y: auto; border: 1px solid #ccc; background: white; border-radius: 4px; position: relative;';
        logContent.appendChild(tableContainer);
        
        updateTimerText = (text, color = '#333') => {
            statusDisplay.textContent = text;
            statusDisplay.style.color = color;
        };

        toggleSkipBtn = (enabled) => {
            skipBtn.disabled = !enabled;
            skipBtn.style.opacity = enabled ? '1' : '0.5';
        };

        syncPauseBtnUI = () => {
            pauseBtn.textContent = isPaused ? '▶ Resume Script' : '⏸ Pause Script';
            pauseBtn.style.background = isPaused ? '#4CAF50' : '#ff9800';
            if (isPaused) updateTimerText('Status: PAUSED', '#ff9800');
        };

        startBtn.onclick = () => {
            if (isRunning) return;
            isRunning = true;
            startBtn.textContent = '⚙ Running...';
            startBtn.style.background = '#888';
            startBtn.style.cursor = 'default';
            pauseBtn.disabled = false;
            pauseBtn.style.opacity = '1';
            updateTimerText('Status: Working...', '#4CAF50');
            executeDownloads(); 
        };

        pauseBtn.onclick = () => {
            if (!isRunning) return;
            isPaused = !isPaused;
            syncPauseBtnUI();
        };

        skipBtn.onclick = () => {
            if (!skipBtn.disabled) {
                skipWaitFlag = true;
                updateTimerText('Status: Skipping wait...', 'purple');
            }
        };

        updateTaskStatus = (index, status, color) => {
            const el = document.getElementById(`ui-task-${index}`);
            if (el) {
                el.innerHTML = `<span>Task ${index + 1}</span> <span style="color: ${color}; font-weight: bold;">${status}</span>`;
            }
        };
        
        const refreshTable = () => {
            tableContainer.innerHTML = '';
            const items = getDownloaded();
            
            if (items.length === 0) {
                tableContainer.innerHTML = '<p style="color:#666; font-size:14px; text-align:center; padding: 15px; margin: 0;">No files tracked yet.</p>';
                return;
            }
            
            const table = document.createElement('table');
            table.style.cssText = 'width: 100%; border-collapse: collapse; font-size: 12px;';
            
            table.innerHTML = `
                <thead style="position: sticky; top: 0; background: #e0e0e0; z-index: 10;">
                    <tr style="text-align: left;">
                        <th style="padding: 10px 8px; border-bottom: 2px solid #ccc; width: 30px;"><input type="checkbox" id="selectAll-manager"></th>
                        <th style="padding: 10px 8px; border-bottom: 2px solid #ccc;">Filename</th>
                    </tr>
                </thead>
                <tbody></tbody>
            `;
            
            const tbody = table.querySelector('tbody');
            items.forEach((item) => {
                const tr = document.createElement('tr');
                tr.style.borderBottom = '1px solid #ddd';
                tr.innerHTML = `
                    <td style="padding: 8px;"><input type="checkbox" class="manager-cb" value="${item}"></td>
                    <td style="padding: 8px; word-break: break-word;">${item}</td>
                `;
                tbody.appendChild(tr);
            });
            
            tableContainer.appendChild(table);
            
            document.getElementById('selectAll-manager').onchange = (e) => {
                document.querySelectorAll('.manager-cb').forEach(cb => cb.checked = e.target.checked);
            };
        };

        requeueBtn.onclick = () => {
            const checked = Array.from(document.querySelectorAll('.manager-cb:checked')).map(cb => cb.value);
            if (checked.length === 0) {
                alert('Please select at least one file to re-queue.');
                return;
            }
            
            const current = getDownloaded();
            const updated = current.filter(val => !checked.includes(val));
            localStorage.setItem(STORAGE_KEY, JSON.stringify(updated));
            
            document.querySelectorAll('.x-window-body a').forEach(link => {
                if (checked.includes(link.textContent.trim())) {
                    Array.from(link.parentNode.childNodes).forEach(node => {
                        if (node.nodeType === Node.TEXT_NODE && node.textContent.includes("✓ [In Progress]")) node.remove();
                        if (node.tagName === 'SPAN' && node.textContent.includes("✓ [In Progress]")) node.remove();
                    });
                }
            });
            
            refreshTable();
        };
        
        document.body.appendChild(ui);
        
        // Final resize trigger to snap everything into place on load
        window.dispatchEvent(new Event('resize')); 
        
        return refreshTable;
    };

    updateUITable = buildUI();
    updateUITable();
    // ----------------------

    const executeDownloads = async () => {
        for (let i = 0; i < selectedRows.length; i++) {
            await checkPause(); 
            updateTaskStatus(i, "In Progress", "#ff9800");

            let row = selectedRows[i];
            let downloadBtn = Array.from(row.querySelectorAll('a')).find(el => el.textContent.trim() === 'Download');
            
            if (!downloadBtn) {
                updateTaskStatus(i, "Skipped", "red");
                continue;
            }

            let lastPopupOpenTime = 0;

            const openPopup = async () => {
                updateTimerText('Status: Opening popup...', '#4CAF50');
                downloadBtn.click();
                await smartWait(3000); 
                lastPopupOpenTime = Date.now();
                
                let activeWindows = Array.from(document.querySelectorAll('.x-window')).filter(w => w.style.visibility !== 'hidden' && w.style.display !== 'none');
                return activeWindows.find(w => {
                    let header = w.querySelector('.x-window-header-text');
                    return header && header.textContent.includes('Downloads');
                });
            };

            const closePopup = (win) => {
                let closeBtn = win.querySelector('.x-tool-close');
                if (closeBtn) closeBtn.click();
            };

            let downloadWin = await openPopup();
            if (!downloadWin) {
                updateTaskStatus(i, "Error", "red");
                continue;
            }

            let taskComplete = false;

            while (!taskComplete) {
                await checkPause(); 

                if (Date.now() - lastPopupOpenTime >= 29 * 60 * 1000) {
                    updateTimerText('Status: Refreshing popup...', '#ff9800');
                    closePopup(downloadWin);
                    await smartWait(3000);
                    downloadWin = await openPopup();
                    if (!downloadWin) break; 
                }

                updateTimerText('Status: Loading links...', '#4CAF50');
                let downloadLinks = [];
                let retries = 0;
                while (retries < 15) { 
                    downloadLinks = Array.from(downloadWin.querySelectorAll('.x-window-body a'));
                    if (downloadLinks.length > 0) break;
                    await sleep(2000);
                    retries++;
                }

                if (downloadLinks.length === 0) {
                    taskComplete = true;
                    break;
                }

                let downloadedSet = new Set(getDownloaded());

                for (let link of downloadLinks) {
                    let fileName = link.textContent.trim();
                    if (downloadedSet.has(fileName) && !link.parentNode.innerHTML.includes("✓ [In Progress]")) {
                        let statusSpan = document.createElement('span');
                        statusSpan.textContent = " ✓ [In Progress]";
                        statusSpan.style.color = "#00a651";
                        statusSpan.style.fontWeight = "bold";
                        statusSpan.style.marginLeft = "8px";
                        link.parentNode.insertBefore(statusSpan, link.nextSibling || null);
                    }
                }

                let unclickedLinks = downloadLinks.filter(link => !downloadedSet.has(link.textContent.trim()));

                if (unclickedLinks.length === 0) {
                    taskComplete = true;
                    break; 
                }

                let currentBatchSize = getBatchSize();
                let batch = unclickedLinks.slice(0, currentBatchSize);
                
                updateTimerText(`Status: Clicking batch of ${batch.length}...`, '#4CAF50');

                for (let link of batch) {
                    await checkPause(); 
                    let fileNameToDownload = link.textContent.trim();
                    
                    if (!document.body.contains(link)) continue; 

                    link.scrollIntoView({ behavior: 'smooth', block: 'center' });
                    await sleep(1000); 
                    link.setAttribute('target', '_blank');
                    
                    try {
                        link.click();
                        markDownloaded(fileNameToDownload);
                        downloadedSet.add(fileNameToDownload);
                        
                        if (!link.parentNode.innerHTML.includes("✓ [In Progress]")) {
                            let statusSpan = document.createElement('span');
                            statusSpan.textContent = " ✓ [In Progress]";
                            statusSpan.style.color = "#00a651";
                            statusSpan.style.fontWeight = "bold";
                            statusSpan.style.marginLeft = "8px";
                            link.parentNode.insertBefore(statusSpan, link.nextSibling || null);
                        }
                    } catch (err) {
                        console.error(`Failed to click ${fileNameToDownload}:`, err);
                    }

                    await smartWait(DELAY_BETWEEN_CLICKS); 
                }

                let remainingLinks = unclickedLinks.length - batch.length;
                
                if (remainingLinks > 0) {
                    let currentWaitMinutes = getWaitMinutes();
                    await countdownWait(currentWaitMinutes * 60 * 1000); 
                } else {
                    updateTimerText('Status: Finalizing task...', '#4CAF50');
                    await smartWait(5000); 
                }
            }

            closePopup(downloadWin);
            updateTaskStatus(i, "Done", "#4CAF50");
            await smartWait(2000);
        }

        updateTimerText('Status: COMPLETE ✅', 'green');
        const startBtn = document.querySelector('#download-manager-ui button');
        startBtn.textContent = '✅ All Tasks Finished';
    };

})();

Important Web Browser Note

The first time this script triggers multiple downloads, your browser (Chrome, Firefox, Edge) will likely block it to protect you from spam. Look at your URL bar for a small prompt asking: “Allow mas.barracudanetworks.com to download multiple files?” You must click Allow, otherwise, the browser will silently block the rest of your files. The script specifically waits 60 seconds between clicks to maintain a stable connection, but you still need to allow the initial multi-download permission.


Migrating email archives is rarely a fun process, but a little JavaScript can save you hours of manual clicking!

More to explorer 🧭