Search by

cymdeveloppement / roundcube-gdrive-attachments

cymdev

Uploads attachments larger than the message size limit to a Google shared drive with a service account, and inserts a download link in the message

Package info

github.com/CymDeveloppement/roundcube-gdrive-attachments

Type:roundcube-plugin

pkg:composer/cymdeveloppement/roundcube-gdrive-attachments

Statistics

Installs: 2

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-10-12 07:29 UTC

This package is auto-updated.

Last update: 2026-10-12 07:39:10 UTC


README

Sends the attachments that exceed the message size limit of Roundcube webmail to a Google Workspace shared drive, with a service account of the company, and replaces them in the message with a download link, like Gmail does.

Users do not need a Google account and do not sign in to anything. The files belong to the company, in a shared drive managed by its administrators.

On the receiving side, Roundcube lists the Drive files with the other attachments of the message, and users can save a copy to their own Drive.

Features

  • Large files intercepted in the compose screen: when a file exceeds the attachment limit, the user is asked whether to share it via Google Drive (or it is uploaded right away, see gdrive_attachments_behavior).
  • Soft limit: files that could be attached but are large also go to Google Drive (gdrive_attachments_softlimit), after asking the user or right away, depending on gdrive_attachments_behavior.
  • "Anyone with the link" sharing: recipients download the file without a Google account. The file is not listed in searches.
  • Link inserted in the message, before the signature, in HTML and plain text messages. Nothing is attached: each file is described in an X-Gdrive-Attachment header of the message (name, size, expiration).
  • Drive files listed with the attachments of received messages (in Roundcube), with their real name and size. Drafts and forwarded messages keep their Drive files.
  • Save to my Drive (optional): a button copies the file to the user's own Drive, with domain-wide delegation of the service account.
  • One folder per user (configurable, e.g. {username}/{year}-{month}).
  • Resumable upload in 8 MB chunks: large files never have to fit in memory, and an interrupted chunk is resumed.
  • Retention: files are moved to the trash after a number of days by a cron script; the date is shown next to the link.
  • Removal: removing the attachment in the compose screen offers to also remove the file from Google Drive.

Requirements

  • Roundcube 1.6 or later (tested with 1.6.19 and 1.7.4)
  • PHP 7.3 or later with the openssl and json extensions
  • Google Workspace with shared drives

Google Workspace setup

  1. Create a service account in the Google Cloud console: create (or pick) a project, enable the Google Drive API (APIs & Services > Library), then IAM & Admin > Service accounts > Create. No role is needed on the project.
  2. Create a JSON key for it (Keys > Add key > JSON) and copy the file to the Roundcube server, out of the web root, readable by the web server only:
    chown root:www-data /etc/roundcube/gdrive-key.json
    chmod 640 /etc/roundcube/gdrive-key.json
    If your organization policy blocks key creation (iam.disableServiceAccountKeyCreation), an administrator must allow it for this project.
  3. Create a shared drive (e.g. "Mail attachments") and add the service account's address (name@project.iam.gserviceaccount.com) as a member with the Content manager role.
  4. Allow link sharing outside the organization in the Admin console (Apps > Google Workspace > Drive and Docs > Sharing settings), for the organizational unit and for the shared drive (Shared drive settings: "Allow people outside the organization to access files"). Otherwise creating the link fails and the upload is cancelled.
  5. Copy the shared drive id: the last part of its URL, https://drive.google.com/drive/folders/<id>.

For Save to my Drive, the service account must also act for the users (domain-wide delegation): in the Admin console, Security > Access and data control > API controls > Domain-wide delegation > Add new, enter the client ID of the service account (in its JSON key, client_id) and the scope https://www.googleapis.com/auth/drive.

Service accounts cannot store files in their own drive (Google gives them no storage quota): a shared drive is required. Without shared drives, the service account can impersonate a user with domain-wide delegation (gdrive_attachments_subject), and the files then use that user's storage.

Installation

cd /path/to/roundcube/plugins
git clone https://github.com/CymDeveloppement/roundcube-gdrive-attachments roundcube_gdrive_attachments
cd roundcube_gdrive_attachments
cp config.inc.php.dist config.inc.php

Then enable the plugin in config/config.inc.php:

$config['plugins'] = array(..., 'roundcube_gdrive_attachments');

Size limits

Files going to Google Drive still go through the Roundcube server, so two limits must be set apart:

  • PHP (upload_max_filesize and post_max_size): the largest file a user may share, e.g. 2G. With the official Docker image: ROUNDCUBEMAIL_UPLOAD_MAX_FILESIZE=2G.
  • Roundcube (max_message_size in config/config.inc.php): the actual limit of your mail server, e.g. '25M'. Attachments above max_message_size / 1.33 (base64 overhead) go to Google Drive.

Large uploads can also hit the limits of the web server (e.g. LimitRequestBody for Apache, client_max_body_size for nginx) and its timeouts. The plugin removes the PHP execution time limit during the upload.

Configuration

In config.inc.php (see config.inc.php.dist for all options):

$config['gdrive_attachments_key_file'] = '/etc/roundcube/gdrive-key.json';
$config['gdrive_attachments_drive_id'] = '0AbCdEfGhIjKlUk9PVA';
$config['gdrive_attachments_folder'] = '{username}/{year}-{month}';
$config['gdrive_attachments_behavior'] = 'prompt';
$config['gdrive_attachments_retention_days'] = 30;
Option Default Description
gdrive_attachments_key_file '' JSON key of the service account. The plugin is disabled while empty.
gdrive_attachments_drive_id '' Id of the shared drive
gdrive_attachments_folder_id null Folder of the shared drive to use instead of its root
gdrive_attachments_subject null Account to impersonate (domain-wide delegation), without shared drive
gdrive_attachments_folder '{username}/{year}-{month}' Folder of the files: {username}, {local}, {domain}, {year}, {month}, {day}
gdrive_attachments_behavior 'prompt' 'prompt': ask the user; 'upload': upload right away (above the limit and above the soft limit)
gdrive_attachments_softlimit null Also use Drive for files larger than this (e.g. '10M'); with 'prompt' the user may still attach them
gdrive_attachments_retention_days 30 Days before the cleanup script trashes the files (0 = kept)
gdrive_attachments_timeout 300 Timeout of each request to Google, in seconds
gdrive_attachments_excluded_users [] Logins that cannot upload to Drive
gdrive_attachments_save_to_drive false Show the "Save to my Drive" button (domain-wide delegation required)
gdrive_attachments_save_account '{email}' Google account of the user: {email} (default identity) or {username} (login)
gdrive_attachments_save_domains [] Show the button only to accounts of these domains (empty: everyone)
gdrive_attachments_save_folder 'Mail attachments' Folder of the user's Drive receiving the copies ('': root)

Cleanup of expired files

Each file records its expiration date. Run the cleanup script daily, as the user running Roundcube:

0 3 * * * php /path/to/roundcube/plugins/roundcube_gdrive_attachments/bin/cleanup.php

--dry-run lists the expired files without trashing them. Files are moved to the trash of the shared drive, which Google empties after 30 days. Changing gdrive_attachments_retention_days only affects files uploaded afterwards.

How the files are described

Each Drive file adds a header to the message:

X-Gdrive-Attachment: id=1AbC...; size=12000000; expires=1794381147;
 name*=UTF-8''rapport%20annuel.pdf

Roundcube reads it to list the file with the attachments. The link is always rebuilt from the file id (https://drive.google.com/file/d/<id>/view), so a forged header can only point to Google Drive. "Save to my Drive" only accepts files listed in the headers of the message. Other mail clients ignore the header: their users get the link in the message body.

Notes

  • Anyone who has the link can download the file: this is the purpose, but the link must be treated like the file itself.
  • The copy saved with "Save to my Drive" belongs to the user and uses their storage; it is not removed at the end of the retention period.
  • A message whose only attachments are Drive files has no paperclip icon in the message list.
  • When a message is not sent, its files stay on Drive until they expire.
  • Errors are written to the Roundcube error log, prefixed with roundcube_gdrive_attachments:.

License

MIT. The interception of uploads in the compose screen is adapted from nextcloud_attachments by Bennet Becker (MIT). See LICENSE.