├── .gitignore ├── LICENSE ├── README.md ├── addMailboxes ├── README.md ├── addMailboxes.js ├── package.json └── yarn.lock ├── addSyncJobs ├── README.md ├── addSyncJobs.js ├── package.json └── yarn.lock └── updateQuota ├── README.md ├── package.json ├── updateQuota.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Markus Schicker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Mailcow tools - simple tools that use the mailcow API 2 | 3 | Currently there a the following tools: 4 | 5 | - [addMailboxes](https://github.com/appcoders/mailcowtools/releases/tag/addmailboxes-1.0.1) 6 | - [updateQuota](https://github.com/appcoders/mailcowtools/releases/tag/updatequota-1.0.0) 7 | - [addSyncJobs](https://github.com/appcoders/mailcowtools/release/tag/addSyncJobs-1.0.0) 8 | 9 | I made them for my own use and maybe they are useful to someone else. 10 | 11 | I am not affiliated with mailcow in any way. 12 | 13 | More infos about [mailcow](https://mailcow.email/) and the [mailcow API](https://mx.mailcow.email/api/). 14 | 15 | Support mailcow it is great :-) ! 16 | -------------------------------------------------------------------------------- /addMailboxes/README.md: -------------------------------------------------------------------------------- 1 | ## Mailcow - Add Mailboxes 2 | 3 | This tool is made to bulk create mailboxes on mailcow. I made it for my own use and maybe it is useful to someone else. 4 | 5 | I am not affiliated with mailcow in any way. 6 | 7 | More infos about [mailcow](https://mailcow.email/) and the [mailcow API](https://mx.mailcow.email/api/). 8 | Support mailcow it is great :-) ! 9 | 10 | ### What you need 11 | Just create a CSV file (comma-separated) with four columns eg. 12 | 13 | ``` 14 | "joe@example.com","Joe Example","A-Very-Secret-Password",500 15 | "jane@example.com","Jane Example","A-Even-More-Secret-Password",900 16 | ``` 17 | 18 | You MUST not use a header line. 19 | 20 | You need an API key for your mailcow instance. 21 | 22 | ### Using addMailboxes 23 | ``` 24 | Usage: addMailboxes [options] 25 | 26 | Options: 27 | -V, --version output the version number 28 | -i, --importfile Path to import file CSV 29 | -s, --serverurl URL of mailcow server : https://mailcow.example.org 30 | -a, --apikey APIKEY for mailcow API 31 | -e, --exitonerror exit on first error 32 | -h, --help display help for command 33 | ``` 34 | 35 | For example: 36 | 37 | ``` 38 | node addMailboxes.js -i ./myimportlist -a XXXXX-ZZZZZZ-TTTTT-YYYYY-SSSSS -e -s https://mailcow.example.com 39 | ``` 40 | 41 | or if you use [precompiled binaries](https://github.com/appcoders/mailcowtools/releases): 42 | 43 | ``` 44 | addMailboxes-{version}-{platform} -i ./myimportlist -a XXXXX-ZZZZZZ-TTTTT-YYYYY-SSSSS -e -s https://mailcow.example.com 45 | ``` 46 | 47 | Use -e to stop on the first error that occurs. 48 | !! Make sure you lower the Mailcow Password Policy "System-Configuration-Options-Password Policy" cause for example if policy is set to Password Length 6 and any of the boxes are checked with special characters, accounts that are created with less 5 lowercase letters or other simple passwords are skipped and no errors will be shown. !! 49 | 50 | 51 | ## Using the source 52 | 53 | As usual: 54 | ``` 55 | yarn install 56 | ``` 57 | 58 | ## Pre-built binaries 59 | 60 | I build pre-built with [pkg](https://github.com/vercel/pkg#readme) you can also download [here](https://github.com/appcoders/mailcowtools/releases). 61 | -------------------------------------------------------------------------------- /addMailboxes/addMailboxes.js: -------------------------------------------------------------------------------- 1 | // Written by Markus Schicker, markus@appcoders.de 2 | // MIT License 3 | 4 | // Copyright (c) 2020 Markus Schicker 5 | 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | const { program } = require('commander'); 25 | const axios = require('axios'); 26 | const process = require('process'); 27 | const csvtojsonV2 = require("csvtojson/v2"); 28 | const version = require('./package').version; 29 | 30 | 31 | let axiosInstance = null; 32 | 33 | const configureAxios = () => { 34 | const instance = axios.create({ 35 | baseURL: program.serverurl, 36 | headers: { 'X-API-Key': program.apikey, 'Content-Type': 'application/json' } 37 | }); 38 | return instance; 39 | } 40 | 41 | const importFile = async (filename) => { 42 | let importJSON = null; 43 | 44 | try { 45 | importJSON = await csvtojsonV2({ 46 | noheader: true, 47 | headers: ['email', 'name', 'password', 'quota'] 48 | }).fromFile(filename); 49 | } catch (error) { 50 | console.error(`Error while import:\n${error}`); 51 | process.exit(-1); 52 | } 53 | return importJSON.map(element => { 54 | const emailParts = element.email.split('@'); 55 | delete element.email; 56 | return { ...element, local_part: emailParts[0], domain: emailParts[1], active: "1", password2: element.password } 57 | }); 58 | } 59 | 60 | const addMailbox = async (mailboxInfo) => { 61 | try { 62 | const result = await axiosInstance.post('/api/v1/add/mailbox', mailboxInfo); 63 | if (result.status !== 200) { 64 | console.error(`Error while creating mailbox ${mailboxInfo.local_part}@${mailboxInfo.domain}.`); 65 | if (program.exitonerror) { 66 | process.exit(3); 67 | } 68 | } 69 | console.log(`Created mailbox ${mailboxInfo.local_part}@${mailboxInfo.domain} with quota ${mailboxInfo.quota} MB`); 70 | } catch (error) { 71 | console.error(`Error while adding Mailbox ${mailboxInfo.local_part}@${mailboxInfo.domain}:\n${error}`); 72 | process.exit(2); 73 | } 74 | } 75 | 76 | const addMailboxes = async (mailboxInfos) => { 77 | console.log(`Beginning import of ${mailboxInfos.length} mailboxes`); 78 | mailboxInfos.map(async (mailboxInfo) => { 79 | await addMailbox(mailboxInfo); 80 | }) 81 | } 82 | 83 | const main = async () => { 84 | program.version(version); 85 | 86 | program 87 | .requiredOption('-i, --importfile ', 'Path to import file CSV') 88 | .requiredOption('-s, --serverurl ', 'URL of mailcow server : https://mailcow.example.org') 89 | .requiredOption('-a, --apikey ', 'APIKEY for mailcow API') 90 | .option('-e, --exitonerror', 'exit on first error'); 91 | 92 | program.parse(process.argv); 93 | axiosInstance = configureAxios(); 94 | 95 | const mailboxInfos = await importFile(program.importfile); 96 | await addMailboxes(mailboxInfos); 97 | } 98 | 99 | main(); 100 | 101 | 102 | -------------------------------------------------------------------------------- /addMailboxes/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mailcowaddmailboxes", 3 | "version": "1.0.1", 4 | "main": "addMailboxes.js", 5 | "license": "MIT", 6 | "dependencies": { 7 | "axios": "^0.19.2", 8 | "commander": "^6.0.0", 9 | "csvtojson": "^2.0.10" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /addMailboxes/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | axios@^0.19.2: 6 | version "0.19.2" 7 | resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27" 8 | integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA== 9 | dependencies: 10 | follow-redirects "1.5.10" 11 | 12 | bluebird@^3.5.1: 13 | version "3.7.2" 14 | resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" 15 | integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== 16 | 17 | commander@^6.0.0: 18 | version "6.0.0" 19 | resolved "https://registry.yarnpkg.com/commander/-/commander-6.0.0.tgz#2b270da94f8fb9014455312f829a1129dbf8887e" 20 | integrity sha512-s7EA+hDtTYNhuXkTlhqew4txMZVdszBmKWSPEMxGr8ru8JXR7bLUFIAtPhcSuFdJQ0ILMxnJi8GkQL0yvDy/YA== 21 | 22 | csvtojson@^2.0.10: 23 | version "2.0.10" 24 | resolved "https://registry.yarnpkg.com/csvtojson/-/csvtojson-2.0.10.tgz#11e7242cc630da54efce7958a45f443210357574" 25 | integrity sha512-lUWFxGKyhraKCW8Qghz6Z0f2l/PqB1W3AO0HKJzGIQ5JRSlR651ekJDiGJbBT4sRNNv5ddnSGVEnsxP9XRCVpQ== 26 | dependencies: 27 | bluebird "^3.5.1" 28 | lodash "^4.17.3" 29 | strip-bom "^2.0.0" 30 | 31 | debug@=3.1.0: 32 | version "3.1.0" 33 | resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" 34 | integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== 35 | dependencies: 36 | ms "2.0.0" 37 | 38 | follow-redirects@1.5.10: 39 | version "1.5.10" 40 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" 41 | integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== 42 | dependencies: 43 | debug "=3.1.0" 44 | 45 | is-utf8@^0.2.0: 46 | version "0.2.1" 47 | resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" 48 | integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= 49 | 50 | lodash@^4.17.3: 51 | version "4.17.19" 52 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" 53 | integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== 54 | 55 | ms@2.0.0: 56 | version "2.0.0" 57 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 58 | integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= 59 | 60 | strip-bom@^2.0.0: 61 | version "2.0.0" 62 | resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" 63 | integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= 64 | dependencies: 65 | is-utf8 "^0.2.0" 66 | -------------------------------------------------------------------------------- /addSyncJobs/README.md: -------------------------------------------------------------------------------- 1 | ## Mailcow - Add Sync Jobs 2 | 3 | This tool is made to bulk create mailboxes on mailcow. I made it for my own use and maybe it is useful to someone else. 4 | 5 | I am not affiliated with mailcow in any way. 6 | 7 | More infos about [mailcow](https://mailcow.email/) and the [mailcow API](https://mx.mailcow.email/api/). 8 | Support mailcow it is great :-) ! 9 | 10 | ### What you need 11 | Just create a CSV file (comma-separated) with three columns eg. 12 | 13 | Source IMAP Server mailbox username, Source IMAP Server mailbox password, Mailcow Mailbox name 14 | 15 | ``` 16 | "joe@oldexample.com","A-Very-Old-Secret-Password","joe@example.com" 17 | "jane@oldexample.com""A-Even-More-Old-Secret-Password","jane@example.com" 18 | ``` 19 | 20 | You MUST not use a header line. 21 | 22 | You need an API key for your mailcow instance. 23 | 24 | ### Using addSyncJobs 25 | ``` 26 | Usage: addSyncJobs [options] 27 | 28 | Options: 29 | -V, --version output the version number 30 | -i, --syncjobfile Path to sync job file CSV 31 | -s, --serverurl URL of mailcow server : https://mailcow.example.org 32 | -a, --apikey APIKEY for mailcow API 33 | --host1 Hostname of IMAP source server 34 | --port1 Port of IMAP source server (default: "993") 35 | --enc1 Encryption mode (PLAIN, SSL, TLS) of IMAP source server (default: "SSL") 36 | --timeout1 Timeout setting in seconds for IMAP source server (default: 600) 37 | --inactive Sync job is added as inactive 38 | --minsinterval Sync job interval in minutes (default: 20) 39 | --subfolder2 Sync job target server folder name (default: "") 40 | --maxage Syncing only last maxage days (0=syncall) (default: 0) 41 | --maxbytespersecond Max sync speed in bytes/s (0=no limit) (default: 0) 42 | --delete2duplicates Delete duplicates on target server (1 | 0) (default: 1) 43 | --delete1 Delete messages from source server (1 | 0) 44 | --delete2 Delete messages from target server which are missing on source server (1 | 0) 45 | --automap Automap target and source folder (1 | 0) (default: 1) 46 | --skipcrossduplicates Skip duplicates over folders first come, first serve 47 | --subscribeall Subscribe to all folders (default: 1) 48 | --exclude Exclude regex elements (default: "(?i)spam|(?i)junk") 49 | --customparams Your own imapsync params (default: "") 50 | -e, --exitonerror exit on first error 51 | -h, --help display help for command 52 | ``` 53 | 54 | For example: 55 | 56 | ``` 57 | node addSyncJobs.js -i ./syncjobslist -a XXXXX-ZZZZZZ-TTTTT-YYYYY-SSSSS -e -s https://mailcow.example.com --host1 imap.example.com 58 | ``` 59 | 60 | or if you use [precompiled binaries](https://github.com/appcoders/mailcowtools/releases): 61 | 62 | ``` 63 | addSyncJobs-{version}-{platform} -i ./syncjobslist -a XXXXX-ZZZZZZ-TTTTT-YYYYY-SSSSS -e -s https://mailcow.example.com --host1 imap.example.com 64 | ``` 65 | 66 | Use -e to stop on the first error that occurs. 67 | 68 | ## Using the source 69 | 70 | As usual: 71 | ``` 72 | yarn install 73 | ``` 74 | 75 | ## Pre-built binaries 76 | 77 | I build pre-built with [pkg](https://github.com/vercel/pkg#readme) you can also download [here](https://github.com/appcoders/mailcowtools/releases). 78 | -------------------------------------------------------------------------------- /addSyncJobs/addSyncJobs.js: -------------------------------------------------------------------------------- 1 | // Written by Markus Schicker, markus@appcoders.de 2 | // MIT License 3 | 4 | // Copyright (c) 2020 Markus Schicker 5 | 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | const { program } = require('commander'); 25 | const axios = require('axios'); 26 | const process = require('process'); 27 | const csvtojsonV2 = require("csvtojson/v2"); 28 | const { exit } = require('process'); 29 | const version = require('./package').version; 30 | 31 | 32 | let axiosInstance = null; 33 | 34 | const configureAxios = () => { 35 | const instance = axios.create({ 36 | baseURL: program.serverurl, 37 | headers: { 'X-API-Key': program.apikey, 'Content-Type': 'application/json' } 38 | }); 39 | return instance; 40 | } 41 | 42 | const importSyncJobs = async (filename) => { 43 | let importJSON = null; 44 | 45 | const settings = { 46 | "delete2duplicates": program.delete2duplicates ? 1 : 0, 47 | "custom_params": program.customparams, 48 | "mins_interval": program.minsinterval, 49 | "authmech1": "PLAIN", 50 | "port1": program.port1, 51 | "timeout1": program.timeout1, 52 | "skipcrossduplicates": program.skipcrossduplicates ? 1 : 0, 53 | "active": program.inactive ? 0 : 1, 54 | "timeout2": 600, 55 | "authmd51": 0, 56 | "regextrans2": "", 57 | "domain2": "", 58 | "subfolder2": program.subfolder2, 59 | "enc1": program.enc1, 60 | "automap": program.automap, 61 | "subscribeall": program.subscribeall, 62 | "maxage": program.maxage, 63 | "host1": program.host1, 64 | "delete1": program.delete1 ? 1 : 0, 65 | "exclude": program.exclude, 66 | "maxbytespersecond": `${program.maxbytespersecond}`, 67 | "delete2": program.delete2 ? 1 : 0 68 | } 69 | 70 | try { 71 | importJSON = await csvtojsonV2({ 72 | noheader: true, 73 | headers: ['sourceemail', 'sourcepassword', 'targetmailbox'] 74 | }).fromFile(filename); 75 | } catch (error) { 76 | console.error(`Error while import:\n${error}`); 77 | process.exit(-1); 78 | } 79 | return importJSON.map(element => { 80 | return { ...settings, user1: element.sourceemail, username: element.targetmailbox, password1: element.sourcepassword } 81 | }); 82 | } 83 | 84 | const addSyncJob = async (syncJobInfo) => { 85 | try { 86 | const result = await axiosInstance.post('/api/v1/add/syncjob', syncJobInfo); 87 | if (result.status !== 200 || !result.data || !result.data.type === 'success') { 88 | console.error(`Error while adding syncjob ${syncJobInfo.user1} -> ${syncJobInfo.username}`); 89 | if (program.exitonerror) { 90 | process.exit(3); 91 | } 92 | } 93 | console.log(`Added syncjob ${syncJobInfo.user1} -> ${syncJobInfo.username}`); 94 | } catch (error) { 95 | console.error(`Error while adding syncjob ${syncJobInfo.user1} -> ${syncJobInfo.username}:\n${error}`); 96 | process.exit(2); 97 | } 98 | } 99 | 100 | const addSyncJobs = async (syncJobsInfos) => { 101 | console.log(`Beginning adding of ${syncJobsInfos.length} syncjobs`); 102 | syncJobsInfos.map(async (syncJobInfo) => { 103 | await addSyncJob(syncJobInfo); 104 | }) 105 | } 106 | 107 | const main = async () => { 108 | program.version(version); 109 | 110 | program 111 | .requiredOption('-i, --syncjobfile ', 'Path to sync job file CSV') 112 | .requiredOption('-s, --serverurl ', 'URL of mailcow server : https://mailcow.example.org') 113 | .requiredOption('-a, --apikey ', 'APIKEY for mailcow API') 114 | .requiredOption('--host1 ', 'Hostname of IMAP source server') 115 | .option('--port1 ', 'Port of IMAP source server', '993') 116 | .option('--enc1 ', 'Encryption mode (PLAIN, SSL, TLS) of IMAP source server', 'SSL') 117 | .option('--timeout1 ', 'Timeout setting in seconds for IMAP source server', 600) 118 | .option('--inactive', 'Sync job is added as inactive') 119 | .option('--minsinterval ', 'Sync job interval in minutes', 20) 120 | .option('--subfolder2 ', 'Sync job target server folder name', '') 121 | .option('--maxage ', 'Syncing only last maxage days (0=syncall)', 0) 122 | .option('--maxbytespersecond ', 'Max sync speed in bytes/s (0=no limit)', 0) 123 | .option('--delete2duplicates ', 'Delete duplicates on target server (1 | 0)', 1) 124 | .option('--delete1', 'Delete messages from source server (1 | 0)') 125 | .option('--delete2', 'Delete messages from target server which are missing on source server (1 | 0)') 126 | .option('--automap ', 'Automap target and source folder (1 | 0)', 1) 127 | .option('--skipcrossduplicates', 'Skip duplicates over folders first come, first serve') 128 | .option('--subscribeall ', 'Subscribe to all folders', 1) 129 | .option('--exclude ', 'Exclude regex elements', '(?i)spam|(?i)junk') 130 | .option('--customparams ', 'Your own imapsync params', '') 131 | .option('-e, --exitonerror', 'exit on first error'); 132 | 133 | program.parse(process.argv); 134 | 135 | axiosInstance = configureAxios(); 136 | 137 | const syncJobInfos = await importSyncJobs(program.syncjobfile); 138 | await addSyncJobs(syncJobInfos); 139 | } 140 | 141 | main(); 142 | 143 | 144 | -------------------------------------------------------------------------------- /addSyncJobs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mailcowaddsyncjobs", 3 | "version": "1.0.0", 4 | "main": "addSyncJobs.js", 5 | "license": "MIT", 6 | "dependencies": { 7 | "axios": "^0.19.2", 8 | "commander": "^6.0.0", 9 | "csvtojson": "^2.0.10" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /addSyncJobs/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | axios@^0.19.2: 6 | version "0.19.2" 7 | resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27" 8 | integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA== 9 | dependencies: 10 | follow-redirects "1.5.10" 11 | 12 | bluebird@^3.5.1: 13 | version "3.7.2" 14 | resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" 15 | integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== 16 | 17 | commander@^6.0.0: 18 | version "6.0.0" 19 | resolved "https://registry.yarnpkg.com/commander/-/commander-6.0.0.tgz#2b270da94f8fb9014455312f829a1129dbf8887e" 20 | integrity sha512-s7EA+hDtTYNhuXkTlhqew4txMZVdszBmKWSPEMxGr8ru8JXR7bLUFIAtPhcSuFdJQ0ILMxnJi8GkQL0yvDy/YA== 21 | 22 | csvtojson@^2.0.10: 23 | version "2.0.10" 24 | resolved "https://registry.yarnpkg.com/csvtojson/-/csvtojson-2.0.10.tgz#11e7242cc630da54efce7958a45f443210357574" 25 | integrity sha512-lUWFxGKyhraKCW8Qghz6Z0f2l/PqB1W3AO0HKJzGIQ5JRSlR651ekJDiGJbBT4sRNNv5ddnSGVEnsxP9XRCVpQ== 26 | dependencies: 27 | bluebird "^3.5.1" 28 | lodash "^4.17.3" 29 | strip-bom "^2.0.0" 30 | 31 | debug@=3.1.0: 32 | version "3.1.0" 33 | resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" 34 | integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== 35 | dependencies: 36 | ms "2.0.0" 37 | 38 | follow-redirects@1.5.10: 39 | version "1.5.10" 40 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" 41 | integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== 42 | dependencies: 43 | debug "=3.1.0" 44 | 45 | is-utf8@^0.2.0: 46 | version "0.2.1" 47 | resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" 48 | integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= 49 | 50 | lodash@^4.17.3: 51 | version "4.17.20" 52 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" 53 | integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== 54 | 55 | ms@2.0.0: 56 | version "2.0.0" 57 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 58 | integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= 59 | 60 | strip-bom@^2.0.0: 61 | version "2.0.0" 62 | resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" 63 | integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= 64 | dependencies: 65 | is-utf8 "^0.2.0" 66 | -------------------------------------------------------------------------------- /updateQuota/README.md: -------------------------------------------------------------------------------- 1 | ## Mailcow - Update quota 2 | 3 | This tool is made to bulk modifying the quota of mailboxes on mailcow. I made it for my own use and maybe it is useful to someone else. 4 | 5 | I am not affiliated with mailcow in any way. 6 | 7 | More infos about [mailcow](https://mailcow.email/) and the [mailcow API](https://mx.mailcow.email/api/). 8 | Support mailcow it is great :-) ! 9 | 10 | ### What you need 11 | You need an API key for your mailcow instance. 12 | 13 | ### Using updateQuota 14 | ``` 15 | Usage: updateQuota [options] 16 | 17 | Options: 18 | -V, --version output the version number 19 | -s, --serverurl URL of mailcow server : https://mailcow.example.org 20 | -a, --apikey APIKEY for mailcow API 21 | -q, --quota New quota in MB - only applied when old quota is lower 22 | -d, --domain Apply quota update to only to this domain 23 | -f, --force Force quota update on ALL mailboxes - handle with care 24 | -e, --exitonerror exit on first error 25 | -h, --help display help for command 26 | ``` 27 | 28 | For example: 29 | 30 | ``` 31 | node updateQuota.js -a XXXXX-ZZZZZZ-TTTTT-YYYYY-SSSSS -s https://mailcow.example.com 32 | ``` 33 | 34 | or if you use [precompiled binaries](https://github.com/appcoders/mailcowtools/releases): 35 | 36 | ``` 37 | updateQuota-{version}-{platform} -a XXXXX-ZZZZZZ-TTTTT-YYYYY-SSSSS -s https://mailcow.example.com -q 500 -d example.com 38 | ``` 39 | 40 | Use -e to stop on the first error that occurs. 41 | 42 | ## Using the source 43 | 44 | As usual: 45 | ``` 46 | yarn install 47 | ``` 48 | 49 | ## Pre-built binaries 50 | 51 | I build pre-built with [pkg](https://github.com/vercel/pkg#readme) you can also download [here](https://github.com/appcoders/mailcowtools/releases). 52 | -------------------------------------------------------------------------------- /updateQuota/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mailcowupdatequota", 3 | "version": "1.0.0", 4 | "main": "updateQuota.js", 5 | "license": "MIT", 6 | "dependencies": { 7 | "axios": "^0.19.2", 8 | "commander": "^6.0.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /updateQuota/updateQuota.js: -------------------------------------------------------------------------------- 1 | // Written by Markus Schicker, markus@appcoders.de 2 | // MIT License 3 | 4 | // Copyright (c) 2020 Markus Schicker 5 | 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | const { program } = require('commander'); 25 | const axios = require('axios'); 26 | const process = require('process'); 27 | const version = require('./package').version; 28 | 29 | 30 | let axiosInstance = null; 31 | 32 | const configureAxios = () => { 33 | const instance = axios.create({ 34 | baseURL: program.serverurl, 35 | headers: { 'X-API-Key': program.apikey, 'Content-Type': 'application/json' } 36 | }); 37 | return instance; 38 | } 39 | 40 | const getAllMailboxes = async () => { 41 | try { 42 | const result = await axiosInstance.get('/api/v1/get/mailbox/all'); 43 | if (result.status !== 200 || !Array.isArray(result.data)) { 44 | console.error(`Error while getting all mailboxes`); 45 | if (program.exitonerror) { 46 | process.exit(3); 47 | } 48 | } 49 | console.log(`Found ${result.data.length} mailboxes.`); 50 | return result.data; 51 | } catch (error) { 52 | console.error(`Error while adding Mailbox ${mailboxInfo.local_part}@${mailboxInfo.domain}:\n${error}`); 53 | process.exit(2); 54 | } 55 | return []; 56 | } 57 | 58 | const updateMailboxQuota = async (mailbox) => { 59 | try { 60 | const updateData = { 61 | "attr": { 62 | "quota": mailbox.quota 63 | }, 64 | "items": [mailbox.username] 65 | } 66 | 67 | const result = await axiosInstance.post('/api/v1/edit/mailbox', updateData); 68 | if (result.status !== 200) { 69 | console.error(`Error while updating mailbox ${mailbox.username}.`); 70 | if (program.exitonerror) { 71 | process.exit(3); 72 | } 73 | } 74 | console.log(`Updated mailbox ${mailbox.username} with quota ${mailbox.quota} MB`); 75 | } catch (error) { 76 | console.error(`Error while updating mailbox ${mailbox.username}:\n${error}`); 77 | process.exit(2); 78 | } 79 | } 80 | 81 | const updateMailboxes = async (mailboxes) => { 82 | console.log(`Beginning update of ${mailboxes.length} mailboxes`); 83 | mailboxes.map(async (mailbox) => { 84 | await updateMailboxQuota(mailbox); 85 | }) 86 | } 87 | 88 | const main = async () => { 89 | program.version(version); 90 | program 91 | .requiredOption('-s, --serverurl ', 'URL of mailcow server : https://mailcow.example.org') 92 | .requiredOption('-a, --apikey ', 'APIKEY for mailcow API') 93 | .requiredOption('-q, --quota ', 'New quota in MB - only applied when old quota is lower') 94 | .option('-d, --domain ', 'Apply quota update to only to this domain') 95 | .option('-f, --force', 'Force quota update on ALL mailboxes - handle with care') 96 | .option('-e, --exitonerror', 'exit on first error'); 97 | 98 | program.parse(process.argv); 99 | 100 | if (!program.force && !program.domain) { 101 | console.error(`You need at least a domain set with -d or --force for update all mailboxes regardless of domain.`); 102 | process.exit(4); 103 | } 104 | axiosInstance = configureAxios(); 105 | 106 | const mailboxes = await getAllMailboxes(); 107 | const filteredMailboxesByDomain = program.domain ? mailboxes.filter(mailbox => mailbox.domain === program.domain) : mailboxes; 108 | const filteredMailboxesByDomainAndQuota = filteredMailboxesByDomain.filter(mailbox => (mailbox.quota / 1024 / 1024) < program.quota); 109 | if (filteredMailboxesByDomainAndQuota.length===0) { 110 | console.log(`No mailboxes found with current settings to update.`); 111 | process.exit(5); 112 | } 113 | const updatedQuotaMailboxList = filteredMailboxesByDomainAndQuota.map(mailbox => { return { username: mailbox.username, quota: program.quota }}) 114 | await updateMailboxes(updatedQuotaMailboxList); 115 | } 116 | 117 | main(); 118 | 119 | 120 | -------------------------------------------------------------------------------- /updateQuota/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | axios@^0.19.2: 6 | version "0.19.2" 7 | resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27" 8 | integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA== 9 | dependencies: 10 | follow-redirects "1.5.10" 11 | 12 | commander@^6.0.0: 13 | version "6.0.0" 14 | resolved "https://registry.yarnpkg.com/commander/-/commander-6.0.0.tgz#2b270da94f8fb9014455312f829a1129dbf8887e" 15 | integrity sha512-s7EA+hDtTYNhuXkTlhqew4txMZVdszBmKWSPEMxGr8ru8JXR7bLUFIAtPhcSuFdJQ0ILMxnJi8GkQL0yvDy/YA== 16 | 17 | debug@=3.1.0: 18 | version "3.1.0" 19 | resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" 20 | integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== 21 | dependencies: 22 | ms "2.0.0" 23 | 24 | follow-redirects@1.5.10: 25 | version "1.5.10" 26 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" 27 | integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== 28 | dependencies: 29 | debug "=3.1.0" 30 | 31 | ms@2.0.0: 32 | version "2.0.0" 33 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 34 | integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= 35 | --------------------------------------------------------------------------------