cymdeveloppement / roundcube-gdrive-attachments
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
Requires
- php: >=7.3
- ext-json: *
- ext-openssl: *
- roundcube/plugin-installer: >=0.3.0
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
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 ongdrive_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-Attachmentheader 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
- 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.
- 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. - 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. - 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.
- 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_filesizeandpost_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_sizeinconfig/config.inc.php): the actual limit of your mail server, e.g.'25M'. Attachments abovemax_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.