├── .babelrc ├── .electron-vue ├── build.js ├── dev-client.js ├── dev-runner.js ├── revision.js ├── webpack.main.config.js ├── webpack.renderer.config.js └── webpack.web.config.js ├── .eslintignore ├── .eslintrc.js ├── .gitattributes ├── .gitignore ├── .imapsync └── upgrade.js ├── .travis.yml ├── LICENSE ├── README.md ├── _config.yml ├── appveyor.yml ├── build └── icons │ ├── 256x256.png │ ├── icon.icns │ └── icon.ico ├── dist ├── electron │ └── .gitkeep └── web │ └── .gitkeep ├── docs ├── example │ ├── example.csv │ └── example.txt └── media │ └── preview.gif ├── imapsync_binaries ├── bin │ └── imapsync ├── darwin │ └── imapsync ├── ubuntu │ └── after_installation.sh ├── version.json └── windows │ ├── Cook │ ├── build_exe.bat │ ├── imapsync │ ├── install_modules.bat │ ├── test_cook_exe.bat │ └── test_cook_src.bat │ ├── README.txt │ ├── README_Windows.txt │ ├── file.txt │ ├── imapsync.exe │ ├── imapsync_example.bat │ └── sync_loop_windows.bat ├── package.json ├── revision.json ├── src ├── index.ejs ├── main │ ├── imapsync.js │ ├── index.dev.js │ ├── index.js │ ├── menu.js │ └── updater.js └── renderer │ ├── App.vue │ ├── assets │ ├── .gitkeep │ ├── app │ │ └── icons │ │ │ ├── 128x128.png │ │ │ ├── 16x16.png │ │ │ ├── 24x24.png │ │ │ ├── 256x256.png │ │ │ ├── 32x32.png │ │ │ ├── 48x48.png │ │ │ ├── 512x512.png │ │ │ ├── 64x64.png │ │ │ └── 96x96.png │ ├── logo.png │ ├── logo_heart.svg │ └── logo_sync.svg │ ├── components │ ├── About.vue │ ├── Add │ │ └── MailboxForm.vue │ ├── ImportCsvButton.vue │ ├── MainComponent.vue │ ├── QueueTable │ │ ├── QueueTable.vue │ │ └── QueueTablePassword.vue │ └── ShellAlikeBox.vue │ ├── main.js │ ├── router │ └── index.js │ └── store │ ├── index.js │ └── modules │ ├── Counter.js │ ├── EventBus.js │ ├── Mailboxes.js │ ├── ShellHistory.js │ ├── Version.js │ └── index.js ├── static └── .gitkeep ├── test.csv └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "comments": false, 3 | "env": { 4 | "main": { 5 | "presets": [ 6 | ["env", { 7 | "targets": { "node": 7 } 8 | }], 9 | "stage-0" 10 | ] 11 | }, 12 | "renderer": { 13 | "presets": [ 14 | ["env", { 15 | "modules": false 16 | }], 17 | "stage-0" 18 | ] 19 | }, 20 | "web": { 21 | "presets": [ 22 | ["env", { 23 | "modules": false 24 | }], 25 | "stage-0" 26 | ] 27 | } 28 | }, 29 | "plugins": ["transform-runtime"] 30 | } 31 | -------------------------------------------------------------------------------- /.electron-vue/build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | process.env.NODE_ENV = 'production' 4 | 5 | const { say } = require('cfonts') 6 | const chalk = require('chalk') 7 | const del = require('del') 8 | const { spawn } = require('child_process') 9 | const webpack = require('webpack') 10 | const Multispinner = require('multispinner') 11 | 12 | 13 | const mainConfig = require('./webpack.main.config') 14 | const rendererConfig = require('./webpack.renderer.config') 15 | const webConfig = require('./webpack.web.config') 16 | 17 | const doneLog = chalk.bgGreen.white(' DONE ') + ' ' 18 | const errorLog = chalk.bgRed.white(' ERROR ') + ' ' 19 | const okayLog = chalk.bgBlue.white(' OKAY ') + ' ' 20 | const isCI = process.env.CI || false 21 | 22 | if (process.env.BUILD_TARGET === 'clean') clean() 23 | else if (process.env.BUILD_TARGET === 'web') web() 24 | else build() 25 | 26 | function clean () { 27 | del.sync(['build/*', '!build/icons', '!build/icons/icon.*']) 28 | console.log(`\n${doneLog}\n`) 29 | process.exit() 30 | } 31 | 32 | function build () { 33 | greeting() 34 | 35 | del.sync(['dist/electron/*', '!.gitkeep']) 36 | 37 | const tasks = ['main', 'renderer'] 38 | const m = new Multispinner(tasks, { 39 | preText: 'building', 40 | postText: 'process' 41 | }) 42 | 43 | let results = '' 44 | 45 | m.on('success', () => { 46 | process.stdout.write('\x1B[2J\x1B[0f') 47 | console.log(`\n\n${results}`) 48 | console.log(`${okayLog}take it away ${chalk.yellow('`electron-builder`')}\n`) 49 | process.exit() 50 | }) 51 | 52 | pack(mainConfig).then(result => { 53 | results += result + '\n\n' 54 | m.success('main') 55 | }).catch(err => { 56 | m.error('main') 57 | console.log(`\n ${errorLog}failed to build main process`) 58 | console.error(`\n${err}\n`) 59 | process.exit(1) 60 | }) 61 | 62 | pack(rendererConfig).then(result => { 63 | results += result + '\n\n' 64 | m.success('renderer') 65 | }).catch(err => { 66 | m.error('renderer') 67 | console.log(`\n ${errorLog}failed to build renderer process`) 68 | console.error(`\n${err}\n`) 69 | process.exit(1) 70 | }) 71 | } 72 | 73 | function pack (config) { 74 | return new Promise((resolve, reject) => { 75 | webpack(config, (err, stats) => { 76 | if (err) reject(err.stack || err) 77 | else if (stats.hasErrors()) { 78 | let err = '' 79 | 80 | stats.toString({ 81 | chunks: false, 82 | colors: true 83 | }) 84 | .split(/\r?\n/) 85 | .forEach(line => { 86 | err += ` ${line}\n` 87 | }) 88 | 89 | reject(err) 90 | } else { 91 | resolve(stats.toString({ 92 | chunks: false, 93 | colors: true 94 | })) 95 | } 96 | }) 97 | }) 98 | } 99 | 100 | function web () { 101 | del.sync(['dist/web/*', '!.gitkeep']) 102 | webpack(webConfig, (err, stats) => { 103 | if (err || stats.hasErrors()) console.log(err) 104 | 105 | console.log(stats.toString({ 106 | chunks: false, 107 | colors: true 108 | })) 109 | 110 | process.exit() 111 | }) 112 | } 113 | 114 | function greeting () { 115 | const cols = process.stdout.columns 116 | let text = '' 117 | 118 | if (cols > 85) text = 'lets-build' 119 | else if (cols > 60) text = 'lets-|build' 120 | else text = false 121 | 122 | if (text && !isCI) { 123 | say(text, { 124 | colors: ['yellow'], 125 | font: 'simple3d', 126 | space: false 127 | }) 128 | } else console.log(chalk.yellow.bold('\n lets-build')) 129 | console.log() 130 | } 131 | -------------------------------------------------------------------------------- /.electron-vue/dev-client.js: -------------------------------------------------------------------------------- 1 | const hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true') 2 | 3 | hotClient.subscribe(event => { 4 | /** 5 | * Reload browser when HTMLWebpackPlugin emits a new index.html 6 | * 7 | * Currently disabled until jantimon/html-webpack-plugin#680 is resolved. 8 | * https://github.com/SimulatedGREG/electron-vue/issues/437 9 | * https://github.com/jantimon/html-webpack-plugin/issues/680 10 | */ 11 | // if (event.action === 'reload') { 12 | // window.location.reload() 13 | // } 14 | 15 | /** 16 | * Notify `mainWindow` when `main` process is compiling, 17 | * giving notice for an expected reload of the `electron` process 18 | */ 19 | if (event.action === 'compiling') { 20 | document.body.innerHTML += ` 21 | 34 | 35 |
36 | Compiling Main Process... 37 |
38 | ` 39 | } 40 | }) 41 | -------------------------------------------------------------------------------- /.electron-vue/dev-runner.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const chalk = require('chalk') 4 | const electron = require('electron') 5 | const path = require('path') 6 | const { say } = require('cfonts') 7 | const { spawn } = require('child_process') 8 | const webpack = require('webpack') 9 | const WebpackDevServer = require('webpack-dev-server') 10 | const webpackHotMiddleware = require('webpack-hot-middleware') 11 | 12 | const mainConfig = require('./webpack.main.config') 13 | const rendererConfig = require('./webpack.renderer.config') 14 | 15 | let electronProcess = null 16 | let manualRestart = false 17 | let hotMiddleware 18 | 19 | function logStats (proc, data) { 20 | let log = '' 21 | 22 | log += chalk.yellow.bold(`┏ ${proc} Process ${new Array((19 - proc.length) + 1).join('-')}`) 23 | log += '\n\n' 24 | 25 | if (typeof data === 'object') { 26 | data.toString({ 27 | colors: true, 28 | chunks: false 29 | }).split(/\r?\n/).forEach(line => { 30 | log += ' ' + line + '\n' 31 | }) 32 | } else { 33 | log += ` ${data}\n` 34 | } 35 | 36 | log += '\n' + chalk.yellow.bold(`┗ ${new Array(28 + 1).join('-')}`) + '\n' 37 | 38 | console.log(log) 39 | } 40 | 41 | function startRenderer () { 42 | return new Promise((resolve, reject) => { 43 | rendererConfig.entry.renderer = [path.join(__dirname, 'dev-client')].concat(rendererConfig.entry.renderer) 44 | 45 | const compiler = webpack(rendererConfig) 46 | hotMiddleware = webpackHotMiddleware(compiler, { 47 | log: false, 48 | heartbeat: 2500 49 | }) 50 | 51 | compiler.plugin('compilation', compilation => { 52 | compilation.plugin('html-webpack-plugin-after-emit', (data, cb) => { 53 | hotMiddleware.publish({ action: 'reload' }) 54 | cb() 55 | }) 56 | }) 57 | 58 | compiler.plugin('done', stats => { 59 | logStats('Renderer', stats) 60 | }) 61 | 62 | const server = new WebpackDevServer( 63 | compiler, 64 | { 65 | contentBase: path.join(__dirname, '../'), 66 | quiet: true, 67 | before (app, ctx) { 68 | app.use(hotMiddleware) 69 | ctx.middleware.waitUntilValid(() => { 70 | resolve() 71 | }) 72 | } 73 | } 74 | ) 75 | 76 | server.listen(9080) 77 | }) 78 | } 79 | 80 | function startMain () { 81 | return new Promise((resolve, reject) => { 82 | mainConfig.entry.main = [path.join(__dirname, '../src/main/index.dev.js')].concat(mainConfig.entry.main) 83 | 84 | const compiler = webpack(mainConfig) 85 | 86 | compiler.plugin('watch-run', (compilation, done) => { 87 | logStats('Main', chalk.white.bold('compiling...')) 88 | hotMiddleware.publish({ action: 'compiling' }) 89 | done() 90 | }) 91 | 92 | compiler.watch({}, (err, stats) => { 93 | if (err) { 94 | console.log(err) 95 | return 96 | } 97 | 98 | logStats('Main', stats) 99 | 100 | if (electronProcess && electronProcess.kill) { 101 | manualRestart = true 102 | process.kill(electronProcess.pid) 103 | electronProcess = null 104 | startElectron() 105 | 106 | setTimeout(() => { 107 | manualRestart = false 108 | }, 5000) 109 | } 110 | 111 | resolve() 112 | }) 113 | }) 114 | } 115 | 116 | function startElectron () { 117 | electronProcess = spawn(electron, ['--inspect=5858', path.join(__dirname, '../dist/electron/main.js')]) 118 | 119 | electronProcess.stdout.on('data', data => { 120 | electronLog(data, 'blue') 121 | }) 122 | electronProcess.stderr.on('data', data => { 123 | electronLog(data, 'red') 124 | }) 125 | 126 | electronProcess.on('close', () => { 127 | if (!manualRestart) process.exit() 128 | }) 129 | } 130 | 131 | function electronLog (data, color) { 132 | let log = '' 133 | data = data.toString().split(/\r?\n/) 134 | data.forEach(line => { 135 | log += ` ${line}\n` 136 | }) 137 | if (/[0-9A-z]+/.test(log)) { 138 | console.log( 139 | chalk[color].bold('┏ Electron -------------------') + 140 | '\n\n' + 141 | log + 142 | chalk[color].bold('┗ ----------------------------') + 143 | '\n' 144 | ) 145 | } 146 | } 147 | 148 | function greeting () { 149 | const cols = process.stdout.columns 150 | let text = '' 151 | 152 | if (cols > 104) text = 'electron-vue' 153 | else if (cols > 76) text = 'electron-|vue' 154 | else text = false 155 | 156 | if (text) { 157 | say(text, { 158 | colors: ['yellow'], 159 | font: 'simple3d', 160 | space: false 161 | }) 162 | } else console.log(chalk.yellow.bold('\n electron-vue')) 163 | console.log(chalk.blue(' getting ready...') + '\n') 164 | } 165 | 166 | function init () { 167 | greeting() 168 | 169 | Promise.all([startRenderer(), startMain()]) 170 | .then(() => { 171 | startElectron() 172 | }) 173 | .catch(err => { 174 | console.error(err) 175 | }) 176 | } 177 | 178 | init() 179 | -------------------------------------------------------------------------------- /.electron-vue/revision.js: -------------------------------------------------------------------------------- 1 | let fs = require('fs') 2 | let git = require('git-rev-sync') 3 | let app = require('../package.json') 4 | 5 | let revision = { 6 | rev: git.short(), 7 | version: app.version + '.' + git.short(), 8 | date: (new Date()).toLocaleString() 9 | }; 10 | 11 | fs.writeFileSync('revision.json', JSON.stringify(revision, null, 2)); 12 | 13 | console.log(revision.version); -------------------------------------------------------------------------------- /.electron-vue/webpack.main.config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | process.env.BABEL_ENV = 'main' 4 | 5 | const path = require('path') 6 | const { dependencies } = require('../package.json') 7 | const webpack = require('webpack') 8 | 9 | const BabiliWebpackPlugin = require('babili-webpack-plugin') 10 | 11 | let mainConfig = { 12 | entry: { 13 | main: path.join(__dirname, '../src/main/index.js') 14 | }, 15 | externals: [ 16 | ...Object.keys(dependencies || {}) 17 | ], 18 | module: { 19 | rules: [ 20 | { 21 | test: /\.(js)$/, 22 | enforce: 'pre', 23 | exclude: /node_modules/, 24 | use: { 25 | loader: 'eslint-loader', 26 | options: { 27 | formatter: require('eslint-friendly-formatter') 28 | } 29 | } 30 | }, 31 | { 32 | test: /\.js$/, 33 | use: 'babel-loader', 34 | exclude: /node_modules/ 35 | }, 36 | { 37 | test: /\.node$/, 38 | use: 'node-loader' 39 | } 40 | ] 41 | }, 42 | node: { 43 | __dirname: process.env.NODE_ENV !== 'production', 44 | __filename: process.env.NODE_ENV !== 'production' 45 | }, 46 | output: { 47 | filename: '[name].js', 48 | libraryTarget: 'commonjs2', 49 | path: path.join(__dirname, '../dist/electron') 50 | }, 51 | plugins: [ 52 | new webpack.NoEmitOnErrorsPlugin() 53 | ], 54 | resolve: { 55 | extensions: ['.js', '.json', '.node'] 56 | }, 57 | target: 'electron-main' 58 | } 59 | 60 | /** 61 | * Adjust mainConfig for development settings 62 | */ 63 | if (process.env.NODE_ENV !== 'production') { 64 | mainConfig.plugins.push( 65 | new webpack.DefinePlugin({ 66 | '__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"` 67 | }) 68 | ) 69 | } 70 | 71 | /** 72 | * Adjust mainConfig for production settings 73 | */ 74 | if (process.env.NODE_ENV === 'production') { 75 | mainConfig.plugins.push( 76 | new BabiliWebpackPlugin(), 77 | new webpack.DefinePlugin({ 78 | 'process.env.NODE_ENV': '"production"' 79 | }) 80 | ) 81 | } 82 | 83 | module.exports = mainConfig 84 | -------------------------------------------------------------------------------- /.electron-vue/webpack.renderer.config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | process.env.BABEL_ENV = 'renderer' 4 | 5 | const path = require('path') 6 | const { dependencies } = require('../package.json') 7 | const webpack = require('webpack') 8 | 9 | const BabiliWebpackPlugin = require('babili-webpack-plugin') 10 | const CopyWebpackPlugin = require('copy-webpack-plugin') 11 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 12 | const HtmlWebpackPlugin = require('html-webpack-plugin') 13 | 14 | /** 15 | * List of node_modules to include in webpack bundle 16 | * 17 | * Required for specific packages like Vue UI libraries 18 | * that provide pure *.vue files that need compiling 19 | * https://simulatedgreg.gitbooks.io/electron-vue/content/en/webpack-configurations.html#white-listing-externals 20 | */ 21 | let whiteListedModules = ['vue'] 22 | 23 | let rendererConfig = { 24 | devtool: '#cheap-module-eval-source-map', 25 | entry: { 26 | renderer: path.join(__dirname, '../src/renderer/main.js') 27 | }, 28 | externals: [ 29 | ...Object.keys(dependencies || {}).filter(d => !whiteListedModules.includes(d)) 30 | ], 31 | module: { 32 | rules: [ 33 | { 34 | test: /\.(js|vue)$/, 35 | enforce: 'pre', 36 | exclude: /node_modules/, 37 | use: { 38 | loader: 'eslint-loader', 39 | options: { 40 | formatter: require('eslint-friendly-formatter') 41 | } 42 | } 43 | }, 44 | { 45 | test: /\.css$/, 46 | use: ExtractTextPlugin.extract({ 47 | fallback: 'style-loader', 48 | use: 'css-loader' 49 | }) 50 | }, 51 | { 52 | test: /\.html$/, 53 | use: 'vue-html-loader' 54 | }, 55 | { 56 | test: /\.js$/, 57 | use: 'babel-loader', 58 | exclude: /node_modules/ 59 | }, 60 | { 61 | test: /\.node$/, 62 | use: 'node-loader' 63 | }, 64 | { 65 | test: /\.vue$/, 66 | use: { 67 | loader: 'vue-loader', 68 | options: { 69 | extractCSS: process.env.NODE_ENV === 'production', 70 | loaders: { 71 | sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1', 72 | scss: 'vue-style-loader!css-loader!sass-loader' 73 | } 74 | } 75 | } 76 | }, 77 | { 78 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 79 | use: { 80 | loader: 'url-loader', 81 | query: { 82 | limit: 10000, 83 | name: 'imgs/[name]--[folder].[ext]' 84 | } 85 | } 86 | }, 87 | { 88 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, 89 | loader: 'url-loader', 90 | options: { 91 | limit: 10000, 92 | name: 'media/[name]--[folder].[ext]' 93 | } 94 | }, 95 | { 96 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 97 | use: { 98 | loader: 'url-loader', 99 | query: { 100 | limit: 10000, 101 | name: 'fonts/[name]--[folder].[ext]' 102 | } 103 | } 104 | } 105 | ] 106 | }, 107 | node: { 108 | __dirname: process.env.NODE_ENV !== 'production', 109 | __filename: process.env.NODE_ENV !== 'production' 110 | }, 111 | plugins: [ 112 | new ExtractTextPlugin('styles.css'), 113 | new HtmlWebpackPlugin({ 114 | filename: 'index.html', 115 | template: path.resolve(__dirname, '../src/index.ejs'), 116 | minify: { 117 | collapseWhitespace: true, 118 | removeAttributeQuotes: true, 119 | removeComments: true 120 | }, 121 | nodeModules: process.env.NODE_ENV !== 'production' 122 | ? path.resolve(__dirname, '../node_modules') 123 | : false 124 | }), 125 | new webpack.HotModuleReplacementPlugin(), 126 | new webpack.NoEmitOnErrorsPlugin() 127 | ], 128 | output: { 129 | filename: '[name].js', 130 | libraryTarget: 'commonjs2', 131 | path: path.join(__dirname, '../dist/electron') 132 | }, 133 | resolve: { 134 | alias: { 135 | '@': path.join(__dirname, '../src/renderer'), 136 | 'vue$': 'vue/dist/vue.esm.js' 137 | }, 138 | extensions: ['.js', '.vue', '.json', '.css', '.node'] 139 | }, 140 | target: 'electron-renderer' 141 | } 142 | 143 | /** 144 | * Adjust rendererConfig for development settings 145 | */ 146 | if (process.env.NODE_ENV !== 'production') { 147 | rendererConfig.plugins.push( 148 | new webpack.DefinePlugin({ 149 | '__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"` 150 | }) 151 | ) 152 | } 153 | 154 | /** 155 | * Adjust rendererConfig for production settings 156 | */ 157 | if (process.env.NODE_ENV === 'production') { 158 | rendererConfig.devtool = '' 159 | 160 | rendererConfig.plugins.push( 161 | new BabiliWebpackPlugin(), 162 | new CopyWebpackPlugin([ 163 | { 164 | from: path.join(__dirname, '../static'), 165 | to: path.join(__dirname, '../dist/electron/static'), 166 | ignore: ['.*'] 167 | } 168 | ]), 169 | new webpack.DefinePlugin({ 170 | 'process.env.NODE_ENV': '"production"' 171 | }), 172 | new webpack.LoaderOptionsPlugin({ 173 | minimize: true 174 | }) 175 | ) 176 | } 177 | 178 | module.exports = rendererConfig 179 | -------------------------------------------------------------------------------- /.electron-vue/webpack.web.config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | process.env.BABEL_ENV = 'web' 4 | 5 | const path = require('path') 6 | const webpack = require('webpack') 7 | 8 | const BabiliWebpackPlugin = require('babili-webpack-plugin') 9 | const CopyWebpackPlugin = require('copy-webpack-plugin') 10 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 11 | const HtmlWebpackPlugin = require('html-webpack-plugin') 12 | 13 | let webConfig = { 14 | devtool: '#cheap-module-eval-source-map', 15 | entry: { 16 | web: path.join(__dirname, '../src/renderer/main.js') 17 | }, 18 | module: { 19 | rules: [ 20 | { 21 | test: /\.(js|vue)$/, 22 | enforce: 'pre', 23 | exclude: /node_modules/, 24 | use: { 25 | loader: 'eslint-loader', 26 | options: { 27 | formatter: require('eslint-friendly-formatter') 28 | } 29 | } 30 | }, 31 | { 32 | test: /\.css$/, 33 | use: ExtractTextPlugin.extract({ 34 | fallback: 'style-loader', 35 | use: 'css-loader' 36 | }) 37 | }, 38 | { 39 | test: /\.html$/, 40 | use: 'vue-html-loader' 41 | }, 42 | { 43 | test: /\.js$/, 44 | use: 'babel-loader', 45 | include: [ path.resolve(__dirname, '../src/renderer') ], 46 | exclude: /node_modules/ 47 | }, 48 | { 49 | test: /\.vue$/, 50 | use: { 51 | loader: 'vue-loader', 52 | options: { 53 | extractCSS: true, 54 | loaders: { 55 | sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1', 56 | scss: 'vue-style-loader!css-loader!sass-loader' 57 | } 58 | } 59 | } 60 | }, 61 | { 62 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 63 | use: { 64 | loader: 'url-loader', 65 | query: { 66 | limit: 10000, 67 | name: 'imgs/[name].[ext]' 68 | } 69 | } 70 | }, 71 | { 72 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 73 | use: { 74 | loader: 'url-loader', 75 | query: { 76 | limit: 10000, 77 | name: 'fonts/[name].[ext]' 78 | } 79 | } 80 | } 81 | ] 82 | }, 83 | plugins: [ 84 | new ExtractTextPlugin('styles.css'), 85 | new HtmlWebpackPlugin({ 86 | filename: 'index.html', 87 | template: path.resolve(__dirname, '../src/index.ejs'), 88 | minify: { 89 | collapseWhitespace: true, 90 | removeAttributeQuotes: true, 91 | removeComments: true 92 | }, 93 | nodeModules: false 94 | }), 95 | new webpack.DefinePlugin({ 96 | 'process.env.IS_WEB': 'true' 97 | }), 98 | new webpack.HotModuleReplacementPlugin(), 99 | new webpack.NoEmitOnErrorsPlugin() 100 | ], 101 | output: { 102 | filename: '[name].js', 103 | path: path.join(__dirname, '../dist/web') 104 | }, 105 | resolve: { 106 | alias: { 107 | '@': path.join(__dirname, '../src/renderer'), 108 | 'vue$': 'vue/dist/vue.esm.js' 109 | }, 110 | extensions: ['.js', '.vue', '.json', '.css'] 111 | }, 112 | target: 'web' 113 | } 114 | 115 | /** 116 | * Adjust webConfig for production settings 117 | */ 118 | if (process.env.NODE_ENV === 'production') { 119 | webConfig.devtool = '' 120 | 121 | webConfig.plugins.push( 122 | new BabiliWebpackPlugin(), 123 | new CopyWebpackPlugin([ 124 | { 125 | from: path.join(__dirname, '../static'), 126 | to: path.join(__dirname, '../dist/web/static'), 127 | ignore: ['.*'] 128 | } 129 | ]), 130 | new webpack.DefinePlugin({ 131 | 'process.env.NODE_ENV': '"production"' 132 | }), 133 | new webpack.LoaderOptionsPlugin({ 134 | minimize: true 135 | }) 136 | ) 137 | } 138 | 139 | module.exports = webConfig 140 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ridaamirini/ImapSyncClient/23070ab730323ce1f3e1238dfbee5a07579a41c4/.eslintignore -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: 'babel-eslint', 4 | parserOptions: { 5 | sourceType: 'module' 6 | }, 7 | env: { 8 | browser: true, 9 | node: true 10 | }, 11 | extends: 'standard', 12 | globals: { 13 | __static: true 14 | }, 15 | plugins: [ 16 | 'html' 17 | ], 18 | 'rules': { 19 | // allow paren-less arrow functions 20 | 'arrow-parens': 0, 21 | // allow async-await 22 | 'generator-star-spacing': 0, 23 | // allow debugger during development 24 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, 25 | 'indent': 'off', 26 | 'handle-callback-err': 'off', 27 | 'brace-style': 'off', 28 | 'semi': ['error', 'always'], 29 | 'no-trailing-spaces': 'off', 30 | 'space-before-function-paren': 'off' 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | imapsync_binaries/* linguist-vendored 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | dist/electron/* 3 | dist/web/* 4 | build/* 5 | !build/icons 6 | /node_modules/ 7 | npm-debug.log 8 | npm-debug.log.* 9 | thumbs.db 10 | !.gitkeep 11 | /.idea/ 12 | .imapsync/downloads -------------------------------------------------------------------------------- /.imapsync/upgrade.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const axios = require('axios'); 6 | const impasyncVersion = require('../imapsync_binaries/version'); 7 | const child = require('child_process'); 8 | const del = require('del'); 9 | 10 | const downloadsPath = path.resolve(__dirname, 'downloads'); 11 | const currentVersion = impasyncVersion.version; 12 | let latestVersion = null; 13 | 14 | axios.get('https://imapsync.lamiral.info/VERSION') 15 | .then(({data}) => { 16 | latestVersion = data; 17 | 18 | if (currentVersion != latestVersion) { 19 | upgrade(); 20 | } else { 21 | console.log('Already up-to-date'); 22 | } 23 | }) 24 | .catch(error => console.error(error)); 25 | 26 | function init() { 27 | cleanup(); 28 | 29 | if (!fs.existsSync(downloadsPath)) { 30 | fs.mkdir(downloadsPath, {}, err => { 31 | if (err) throw err 32 | }); 33 | } 34 | 35 | } 36 | 37 | function upgrade() { 38 | init(); 39 | 40 | console.log(`Upgrade from v${currentVersion} to v${latestVersion}`); 41 | 42 | let sourcePath = null; 43 | // Download perl script 44 | messageDownloading('perl script'); 45 | sourcePath = downloadBinary('imapsync'); 46 | move(sourcePath, 'bin/imapsync'); 47 | messageUpgraded('perl script'); 48 | 49 | // Download darwin binary 50 | messageDownloading('darwin binary'); 51 | sourcePath = downloadBinary('imapsync_bin_Darwin'); 52 | move(sourcePath, 'darwin/imapsync'); 53 | messageUpgraded('darwin binary'); 54 | 55 | // Download windows binary 56 | messageDownloading('windows binary'); 57 | const zipFilename = `imapsync_${latestVersion}`; 58 | const extractFolder = path.resolve(downloadsPath, 'imapsync_extracted/'); 59 | sourcePath = downloadBinary(zipFilename + '.zip'); 60 | child.execSync(`unzip -q ${sourcePath} -d ${extractFolder}`); 61 | move(path.resolve(extractFolder, zipFilename, 'imapsync.exe'), 'windows/imapsync.exe', false); 62 | messageUpgraded('windows binary'); 63 | 64 | finalize(); 65 | } 66 | 67 | function downloadBinary(fileName) { 68 | child.execSync(`cd ${downloadsPath} && curl -LO# https://imapsync.lamiral.info/dist/${fileName}`); 69 | return path.resolve(downloadsPath, fileName); 70 | } 71 | 72 | function move(source, target, setPermissions=true) { 73 | if (setPermissions) { 74 | child.execSync('chmod a+x ' + source); 75 | } 76 | 77 | fs.renameSync( 78 | source, 79 | path.resolve(__dirname, '../imapsync_binaries/', target) 80 | ); 81 | } 82 | 83 | function finalize() { 84 | fs.writeFileSync( 85 | path.resolve(__dirname, '../imapsync_binaries/version.json'), 86 | JSON.stringify({version: latestVersion.toString()}, null, 2) 87 | ); 88 | 89 | cleanup(); 90 | } 91 | 92 | function cleanup() { 93 | del.sync([downloadsPath]); 94 | } 95 | 96 | function messageDownloading(binName) { 97 | console.log(`Downloading ${binName}...`); 98 | } 99 | 100 | function messageUpgraded(binName) { 101 | console.log(`Successfully upgraded ${binName}!`); 102 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | matrix: 2 | include: 3 | - os: osx 4 | osx_image: xcode9.2 5 | language: node_js 6 | node_js: "10" 7 | env: 8 | - ELECTRON_CACHE=$HOME/.cache/electron 9 | - ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder 10 | 11 | - os: linux 12 | services: docker 13 | language: generic 14 | 15 | cache: 16 | directories: 17 | - node_modules 18 | - $HOME/.cache/electron 19 | - $HOME/.cache/electron-builder 20 | 21 | before_install: 22 | - | 23 | if [ "$TRAVIS_OS_NAME" == "osx" ]; then 24 | mkdir -p /tmp/git-lfs && curl -L https://github.com/github/git-lfs/releases/download/v2.3.1/git-lfs-$([ "$TRAVIS_OS_NAME" == "linux" ] && echo "linux" || echo "darwin")-amd64-2.3.1.tar.gz | tar -xz -C /tmp/git-lfs --strip-components 1 25 | export PATH="/tmp/git-lfs:$PATH" 26 | fi 27 | before_script: 28 | - git lfs pull 29 | 30 | script: 31 | - | 32 | if [ "$TRAVIS_OS_NAME" == "linux" ]; then 33 | docker run --rm \ 34 | $(env | \ 35 | grep -Eo '^[^\s=]*(DEBUG|NODE_|ELECTRON_|YARN_|NPM_|CI|CIRCLE|TRAVIS|APPVEYOR_|CSC_|_TOKEN|_KEY|AWS_|STRIP|BUILD_)[^\s=]*' | \ 36 | sed '/^$/d;s/^/-e /' | \ 37 | paste -sd ' ' \ 38 | ) \ 39 | -v ${PWD}:/project \ 40 | -v ~/.cache/electron:/root/.cache/electron \ 41 | -v ~/.cache/electron-builder:/root/.cache/electron-builder \ 42 | electronuserland/builder:wine \ 43 | /bin/bash -c "yarn --link-duplicates --pure-lockfile && yarn release --linux --win" 44 | else 45 | yarn release 46 | fi 47 | before_cache: 48 | - rm -rf $HOME/.cache/electron-builder/wine 49 | 50 | branches: 51 | except: 52 | - "/^v\\d+\\.\\d+\\.\\d+$/" 53 | only: 54 | - master -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Rida Amirini 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 DEALInNGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ImapSync Client [![Build Status](https://travis-ci.org/ridaamirini/ImapSyncClient.svg?branch=master)](https://travis-ci.org/ridaamirini/ImapSyncClient) 2 | 3 | [![forthebadge](http://forthebadge.com/images/badges/built-with-love.svg)](http://forthebadge.com) 4 | [![forthebadge](http://forthebadge.com/images/badges/uses-html.svg)](http://forthebadge.com) 5 | [![forthebadge](http://forthebadge.com/images/badges/uses-css.svg)](http://forthebadge.com) 6 | [![forthebadge](http://forthebadge.com/images/badges/uses-js.svg)](http://forthebadge.com) 7 | [![forthebadge](http://forthebadge.com/images/badges/powered-by-electricity.svg)](http://forthebadge.com) 8 | 9 | > It's only an Internet Message Access Protocol Synchronization Client 10 | 11 | ## Overview 12 | 13 | ImapSync Client is an Email IMAP tool for syncing, copying and migrating email 14 | mailboxes between two imap servers, one way, and without duplicates. 15 | 16 | ## Download 17 | Download the latest version from the [releases](https://github.com/ridaamirini/imapsyncclient/releases/latest) page. 18 | 19 | _**Warning:** Note that versions below 1.0 will not work anymore. Please update to the latest version._ 20 | 21 | ## Demo 22 | 23 | ![image](https://github.com/ridaamirini/ImapSyncClient/blob/dev/docs/media/preview.gif?raw=true) 24 | 25 | ### Import Mailboxes from CSV :page_facing_up: 26 | - [Example file *.csv](https://raw.githubusercontent.com/ridaamirini/ImapSyncClient/1.0/docs/example/example.csv) 27 | - [Example file *.txt](https://raw.githubusercontent.com/ridaamirini/ImapSyncClient/1.0/docs/example/example.txt) 28 | 29 | #### Build Setup 30 | 31 | ``` bash 32 | # install dependencies 33 | npm install 34 | 35 | # serve with hot reload at localhost:9080 36 | npm run dev 37 | 38 | # build electron application for production 39 | npm run build 40 | 41 | 42 | # lint all JS/Vue component files in `src/` 43 | npm run lint 44 | 45 | ``` 46 | ## Thanks 47 | ### Front-End 48 | - [Vue.js](https://github.com/vuejs/vue) 49 | - [Electron](https://github.com/electron/electron) 50 | - [ElectronVue](https://github.com/SimulatedGREG/electron-vue) 51 | - [Element](https://github.com/ElemeFE/element) 52 | ### Design inspired by 53 | - [imapsync.love](http://imapsync.love) 54 | 55 | ## TODO 56 | - [x] ~~Remove API dependency~~ 57 | - [x] ~~Import Mailboxes from CSV~~ 58 | 59 | ## License 60 | 61 | ImapSync Client is licensed under the [MIT License](LICENSE) 62 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 0.1.{build} 2 | 3 | branches: 4 | only: 5 | - master 6 | 7 | image: Visual Studio 2017 8 | platform: 9 | - x64 10 | 11 | cache: 12 | - node_modules 13 | - '%APPDATA%\npm-cache' 14 | - '%USERPROFILE%\.electron' 15 | - '%USERPROFILE%\AppData\Local\Yarn\cache' 16 | 17 | init: 18 | - git config --global core.autocrlf input 19 | 20 | install: 21 | - ps: Install-Product node 8 x64 22 | - choco install yarn --ignore-dependencies 23 | - git reset --hard HEAD 24 | - yarn 25 | - node --version 26 | 27 | build_script: 28 | - yarn build 29 | 30 | test: off 31 | -------------------------------------------------------------------------------- /build/icons/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ridaamirini/ImapSyncClient/23070ab730323ce1f3e1238dfbee5a07579a41c4/build/icons/256x256.png -------------------------------------------------------------------------------- /build/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ridaamirini/ImapSyncClient/23070ab730323ce1f3e1238dfbee5a07579a41c4/build/icons/icon.icns -------------------------------------------------------------------------------- /build/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ridaamirini/ImapSyncClient/23070ab730323ce1f3e1238dfbee5a07579a41c4/build/icons/icon.ico -------------------------------------------------------------------------------- /dist/electron/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ridaamirini/ImapSyncClient/23070ab730323ce1f3e1238dfbee5a07579a41c4/dist/electron/.gitkeep -------------------------------------------------------------------------------- /dist/web/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ridaamirini/ImapSyncClient/23070ab730323ce1f3e1238dfbee5a07579a41c4/dist/web/.gitkeep -------------------------------------------------------------------------------- /docs/example/example.csv: -------------------------------------------------------------------------------- 1 | # Example example.csv for imapsync massive migration. 2 | # 3 | # Each line contains 6 columns, columns are parameters for 4 | # --host1 --user1 --password1 --host2 --user2 --password2 5 | # and a trailing empty fake column to avoid CR LF part going 6 | # in the 6th parameter password2. Don't forget the last semicolon. 7 | # 8 | # Lines starting with a # are comments and ignored 9 | # Blank lines are ignored as well 10 | 11 | 12 | # Now the data example 13 | host001_1;user001_1;password001_1;host001_2;user001_2;password001_2; 14 | host002_1;user002_1;password002_1;host002_2;user002_2;password002_2; 15 | host003_1;user003_1;password003_1;host003_2;user003_2;password003_2; 16 | 17 | # Another comment blabla 18 | host004_1;user004_1;password004_1;host004_2;user004_2;password004_2; 19 | 20 | # This last example is a real one, ie, truly working in the real world. 21 | test1.lamiral.info;test1;secret1;test2.lamiral.info;test2;secret2; -------------------------------------------------------------------------------- /docs/example/example.txt: -------------------------------------------------------------------------------- 1 | # Example example.txt for imapsync massive migration. 2 | # 3 | # Each line contains 6 columns, columns are parameters for 4 | # --host1 --user1 --password1 --host2 --user2 --password2 5 | # and a trailing empty fake column to avoid CR LF part going 6 | # in the 6th parameter password2. Don't forget the last semicolon. 7 | # 8 | # Lines starting with a # are comments and ignored 9 | # Blank lines are ignored as well 10 | 11 | 12 | # Now the data example 13 | host001_1;user001_1;password001_1;host001_2;user001_2;password001_2; 14 | host002_1;user002_1;password002_1;host002_2;user002_2;password002_2; 15 | host003_1;user003_1;password003_1;host003_2;user003_2;password003_2; 16 | 17 | # Another comment blabla 18 | host004_1;user004_1;password004_1;host004_2;user004_2;password004_2; 19 | 20 | # This last example is a real one, ie, truly working in the real world. 21 | test1.lamiral.info;test1;secret1;test2.lamiral.info;test2;secret2; -------------------------------------------------------------------------------- /docs/media/preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ridaamirini/ImapSyncClient/23070ab730323ce1f3e1238dfbee5a07579a41c4/docs/media/preview.gif -------------------------------------------------------------------------------- /imapsync_binaries/darwin/imapsync: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ridaamirini/ImapSyncClient/23070ab730323ce1f3e1238dfbee5a07579a41c4/imapsync_binaries/darwin/imapsync -------------------------------------------------------------------------------- /imapsync_binaries/ubuntu/after_installation.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # ================================================= 4 | # = Installing imapsync on Ubuntu 16.04 or higher = 5 | # ================================================= 6 | 7 | # Here is the command to install imapsync dependencies, 8 | # you need root privilege to run them. 9 | # This command installs standard Ubuntu packages: 10 | 11 | sudo apt-get install \ 12 | libauthen-ntlm-perl \ 13 | libclass-load-perl \ 14 | libcrypt-ssleay-perl \ 15 | libdata-uniqid-perl \ 16 | libdigest-hmac-perl \ 17 | libdist-checkconflicts-perl \ 18 | libfile-copy-recursive-perl \ 19 | libio-compress-perl \ 20 | libio-socket-inet6-perl \ 21 | libio-socket-ssl-perl \ 22 | libio-tee-perl \ 23 | libmail-imapclient-perl \ 24 | libmodule-scandeps-perl \ 25 | libnet-ssleay-perl \ 26 | libpar-packer-perl \ 27 | libreadonly-perl \ 28 | libregexp-common-perl \ 29 | libssl-dev \ 30 | libsys-meminfo-perl \ 31 | libterm-readkey-perl \ 32 | libtest-fatal-perl \ 33 | libtest-mock-guard-perl \ 34 | libtest-pod-perl \ 35 | libtest-requires-perl \ 36 | libtest-simple-perl \ 37 | libunicode-string-perl \ 38 | liburi-perl \ 39 | libtest-nowarnings-perl \ 40 | libtest-deep-perl \ 41 | libtest-warn-perl \ 42 | make \ 43 | cpanminus 44 | 45 | # You can easily detect any missing Perl modules via the prerequisites_imapsync script 46 | # wget -N https://imapsync.lamiral.info/prerequisites_imapsync 47 | # sh prerequisites_imapsync -------------------------------------------------------------------------------- /imapsync_binaries/version.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.977" 3 | } -------------------------------------------------------------------------------- /imapsync_binaries/windows/Cook/build_exe.bat: -------------------------------------------------------------------------------- 1 | 2 | REM $Id: build_exe.bat,v 1.48 2018/02/06 13:12:47 gilles Exp gilles $ 3 | 4 | @SETLOCAL 5 | @ECHO Currently running through %0 %* 6 | 7 | @ECHO Building imapsync.exe 8 | 9 | @REM the following command change current directory to the dirname of the current batch pathname 10 | CD /D %~dp0 11 | 12 | REM Remove the error file because its existence means an error occurred during this script execution 13 | IF EXIST LOG_bat\%~nx0.txt DEL LOG_bat\%~nx0.txt 14 | 15 | 16 | CALL :handle_error CALL :detect_perl 17 | CALL :handle_error CALL :check_modules 18 | CALL :handle_error CALL :pp_exe 19 | 20 | 21 | @ENDLOCAL 22 | EXIT /B 23 | 24 | 25 | :pp_exe 26 | @SETLOCAL 27 | @REM CALL pp -o imapsync.exe --link libeay32_.dll --link zlib1_.dll --link ssleay32_.dll .\imapsync 28 | IF %PROCESSOR_ARCHITECTURE% == x86 ( 29 | CALL pp -o imapsync.exe -M Test2::Formatter -M Test2::Formatter::TAP -M Test2::Event -M Test2::Event::Info --link zlib1_.dll --link libcrypto-1_1_.dll --link libssl-1_1_.dll .\imapsync 30 | REM CALL pp -o imapsync.exe -M Test2::Formatter -M Test2::Formatter::TAP -M Test2::Event -M Test2::Event::Info --link zlib1_.dll .\imapsync 31 | ) ELSE ( 32 | CALL pp -o imapsync.exe -M Test2::Formatter -M Test2::Formatter::TAP -M Test2::Event -M Test2::Event::Info .\imapsync 33 | ) 34 | @ENDLOCAL 35 | EXIT /B 36 | 37 | 38 | 39 | 40 | ::------------------------------------------------------ 41 | ::--------------- Detect Perl -------------------------- 42 | :detect_perl 43 | @SETLOCAL 44 | perl -v 45 | @ENDLOCAL 46 | EXIT /B 47 | ::------------------------------------------------------ 48 | 49 | 50 | ::------------------------------------------------------ 51 | ::--------------- Check modules are here -------------- 52 | :check_modules 53 | @SETLOCAL 54 | perl ^ 55 | -mTest::MockObject ^ 56 | -mPAR::Packer ^ 57 | -mReadonly ^ 58 | -mAuthen::NTLM ^ 59 | -mData::Dumper ^ 60 | -mData::Uniqid ^ 61 | -mDigest::HMAC_MD5 ^ 62 | -mDigest::HMAC_SHA1 ^ 63 | -mDigest::MD5 ^ 64 | -mFile::Copy::Recursive ^ 65 | -mFile::Spec ^ 66 | -mIO::Socket ^ 67 | -mIO::Socket::INET ^ 68 | -mIO::Socket::IP ^ 69 | -mIO::Socket::SSL ^ 70 | -mIO::Tee ^ 71 | -mMail::IMAPClient ^ 72 | -mRegexp::Common ^ 73 | -mTerm::ReadKey ^ 74 | -mTime::Local ^ 75 | -mUnicode::String ^ 76 | -mURI::Escape ^ 77 | -mJSON::WebToken ^ 78 | -mLWP::UserAgent ^ 79 | -mHTML::Entities ^ 80 | -mJSON ^ 81 | -mCrypt::OpenSSL::RSA ^ 82 | -mEncode::Byte ^ 83 | -e '' 84 | IF ERRORLEVEL 1 CALL .\install_modules.bat 85 | @ENDLOCAL 86 | EXIT /B 87 | ::------------------------------------------------------ 88 | 89 | 90 | 91 | 92 | ::------------------------------------------------------ 93 | ::--------------- Handle error ------------------------- 94 | :handle_error 95 | SETLOCAL 96 | ECHO IN %0 with parameters %* 97 | %* 98 | SET CMD_RETURN=%ERRORLEVEL% 99 | 100 | IF %CMD_RETURN% EQU 0 ( 101 | ECHO GOOD END 102 | ) ELSE ( 103 | ECHO BAD END 104 | IF NOT EXIST LOG_bat MKDIR LOG_bat 105 | ECHO Failure calling: %* >> LOG_bat\%~nx0.txt 106 | ) 107 | ENDLOCAL 108 | EXIT /B 109 | ::------------------------------------------------------ 110 | -------------------------------------------------------------------------------- /imapsync_binaries/windows/Cook/install_modules.bat: -------------------------------------------------------------------------------- 1 | REM $Id: install_modules.bat,v 1.34 2018/04/10 00:10:52 gilles Exp gilles $ 2 | 3 | ::------------------------------------------------------ 4 | ::--------------- Main of install_modules.bat ---------- 5 | @SETLOCAL 6 | @ECHO OFF 7 | ECHO Currently running through %0 %* 8 | 9 | @REM Needed with remote ssh 10 | SET SHELL= 11 | SET 12 | ECHO Installing Perl modules for imapsync 13 | 14 | CD /D %~dp0 15 | 16 | CALL :handle_error CALL :detect_perl 17 | CALL :handle_error CALL :update_modules 18 | 19 | @ENDLOCAL 20 | EXIT /B 21 | ::------------------------------------------------------ 22 | 23 | 24 | ::------------------------------------------------------ 25 | ::--------------- Detect Perl -------------------------- 26 | :detect_perl 27 | @SETLOCAL 28 | perl -v 29 | IF ERRORLEVEL 1 ECHO Perl needed. Install Strawberry Perl. Get it at http://strawberryperl.com/ ^ 30 | && PAUSE && EXIT 3 31 | ECHO perl is there 32 | @ENDLOCAL 33 | EXIT /B 34 | ::------------------------------------------------------ 35 | 36 | 37 | ::------------------------------------------------------ 38 | ::---------------- Update modules ---------------------- 39 | :update_modules 40 | @SETLOCAL 41 | FOR %%M in ( ^ 42 | Regexp::Common ^ 43 | Sys::MemInfo ^ 44 | Test::MockObject ^ 45 | Readonly ^ 46 | Authen::NTLM ^ 47 | Crypt::SSLeay ^ 48 | Data::Uniqid ^ 49 | Digest::HMAC_MD5 ^ 50 | Digest::HMAC_SHA1 ^ 51 | Digest::MD5 ^ 52 | File::Copy::Recursive ^ 53 | Getopt::ArgvFile ^ 54 | Socket6 ^ 55 | IO::Socket::INET ^ 56 | IO::Socket::INET6 ^ 57 | IO::Socket::SSL ^ 58 | IO::Tee ^ 59 | Mail::IMAPClient ^ 60 | Module::ScanDeps ^ 61 | Net::SSL ^ 62 | PAR::Packer ^ 63 | Pod::Usage ^ 64 | Test::Pod ^ 65 | Unicode::String ^ 66 | URI::Escape ^ 67 | Crypt::OpenSSL::RSA ^ 68 | JSON ^ 69 | JSON::WebToken ^ 70 | LWP ^ 71 | HTML::Entities ^ 72 | Encode::Byte ^ 73 | ) DO @perl -m%%M -e "print qq{Updating %%M $%%M::VERSION \n}" ^ 74 | & cpanm %%M 75 | 76 | ECHO Perl modules for imapsync updated 77 | REM PAUSE 78 | @ECHO Net::SSLeay not updated 79 | 80 | @ENDLOCAL 81 | EXIT /B 82 | 83 | 84 | ::------------------------------------------------------ 85 | 86 | 87 | ::------------------------------------------------------ 88 | ::----------- Handle errors in LOG_bat\ directory ------ 89 | :handle_error 90 | SETLOCAL 91 | ECHO IN %0 with parameters %* 92 | %* 93 | SET CMD_RETURN=%ERRORLEVEL% 94 | 95 | IF %CMD_RETURN% EQU 0 ( 96 | ECHO GOOD END 97 | ) ELSE ( 98 | ECHO BAD END 99 | IF NOT EXIST LOG_bat MKDIR LOG_bat 100 | ECHO Failure calling with extra %* >> LOG_bat\%~nx0.txt 101 | ) 102 | ENDLOCAL 103 | EXIT /B 104 | ::------------------------------------------------------ 105 | 106 | -------------------------------------------------------------------------------- /imapsync_binaries/windows/Cook/test_cook_exe.bat: -------------------------------------------------------------------------------- 1 | REM $Id: test_cook_exe.bat,v 1.4 2017/09/07 00:59:35 gilles Exp gilles $ 2 | 3 | cd /D %~dp0 4 | 5 | .\imapsync.exe 6 | @PAUSE 7 | .\imapsync.exe --tests 8 | @PAUSE 9 | .\imapsync.exe --testslive --nossl2 10 | @PAUSE 11 | .\imapsync.exe --testslive6 --nossl2 12 | @ECHO The previous test fails with "Invalid argument" usually (August 2017) 13 | @ECHO Tests ended, bye. 14 | @PAUSE 15 | 16 | @REM EXIT 17 | -------------------------------------------------------------------------------- /imapsync_binaries/windows/Cook/test_cook_src.bat: -------------------------------------------------------------------------------- 1 | REM $Id: test_cook_src.bat,v 1.3 2017/09/07 00:59:26 gilles Exp gilles $ 2 | 3 | cd /D %~dp0 4 | 5 | 6 | 7 | perl .\imapsync 8 | @PAUSE 9 | perl .\imapsync --tests 10 | @PAUSE 11 | perl .\imapsync --testslive 12 | @PAUSE 13 | perl .\imapsync --testslive6 14 | @ECHO Tests for imapsync script are finished, bye! 15 | @PAUSE -------------------------------------------------------------------------------- /imapsync_binaries/windows/README.txt: -------------------------------------------------------------------------------- 1 | NAME 2 | 3 | imapsync - Email IMAP tool for syncing, copying and migrating email 4 | mailboxes between two imap servers, one way, and without duplicates. 5 | 6 | VERSION 7 | 8 | This documentation refers to Imapsync $Revision: 1.882 $ 9 | 10 | USAGE 11 | 12 | To synchronize the source imap account 13 | "test1" on server "test1.lamiral.info" with password "secret1" 14 | to the destination imap account 15 | "test2" on server "test2.lamiral.info" with password "secret2" 16 | do: 17 | 18 | imapsync \ 19 | --host1 test1.lamiral.info --user1 test1 --password1 secret1 \ 20 | --host2 test2.lamiral.info --user2 test2 --password2 secret2 21 | 22 | DESCRIPTION 23 | 24 | We sometimes need to transfer mailboxes from one imap server to one 25 | another. 26 | 27 | Imapsync command is a tool allowing incremental and recursive imap 28 | transfers from one mailbox to another. 29 | 30 | By default all folders are transferred, recursively, meaning the whole 31 | folder hierarchy is taken, all messages in them, and all messages flags 32 | (\Seen \Answered \Flagged etc.) are synced too. 33 | 34 | Imapsync reduces the amount of data transferred by not transferring a 35 | given message if it resides already on both sides. 36 | 37 | Same specific headers and the transfer is done only once. By default, 38 | the identification headers are "Message-Id:" and "Received:" lines but 39 | this choice can be changed with the --useheader option. 40 | 41 | All flags are preserved, unread messages will stay unread, read ones 42 | will stay read, deleted will stay deleted. 43 | 44 | You can stop the transfer at any time and restart it later, imapsync 45 | works well with bad connections and interruptions, by design. 46 | 47 | You can decide to delete the messages from the source mailbox after a 48 | successful transfer, it can be a good feature when migrating live 49 | mailboxes since messages will be only on one side. 50 | 51 | In that case, use the --delete1 option. Option --delete1 implies also 52 | option --expunge1 so all messages marked deleted on host1 will be really 53 | deleted. 54 | 55 | You can also decide to remove empty folders once all of their messages 56 | have been transferred. Add --delete1emptyfolders to obtain this 57 | behavior. 58 | 59 | A different scenario is synchronizing a mailbox B from another mailbox A 60 | in case you just want to keep a "live" copy of A in B. 61 | 62 | For this, option --delete2 has to be used, it deletes messages in host2 63 | folder B that are not in host1 folder A. If you also need to destroy 64 | host2 folders that are not in host1 then use --delete2folders. See also 65 | --delete2foldersonly and --delete2foldersbutnot. 66 | 67 | Imapsync is not adequate for maintaining two active imap accounts in 68 | synchronization when the user plays independently on both sides. Use 69 | offlineimap (written by John Goerzen) or mbsync (written by Michael R. 70 | Elkins) for a 2 ways synchronization. 71 | 72 | OPTIONS 73 | 74 | usage: imapsync [options] 75 | 76 | Mandatory options are the six values, three on each sides, needed to log 77 | in into the IMAP servers, ie, a host, a username, and a password, two 78 | times. 79 | 80 | Conventions used: 81 | 82 | str means string 83 | int means integer 84 | reg means regular expression 85 | cmd means command 86 | 87 | --dry : Makes imapsync doing nothing for real, just print what 88 | would be done without --dry. 89 | 90 | OPTIONS/credentials 91 | 92 | --host1 str : Source or "from" imap server. Mandatory. 93 | --port1 int : Port to connect on host1. 94 | Optional since default port is 143 or 993 if --ssl1 95 | --user1 str : User to login on host1. Mandatory. 96 | --password1 str : Password for the user1. 97 | 98 | --host2 str : "destination" imap server. Mandatory. 99 | --port2 int : Port to connect on host2. 100 | Optional since default port is 143 or 993 if --ssl2 101 | --user2 str : User to login on host2. Mandatory. 102 | --password2 str : Password for the user2. 103 | 104 | --showpasswords : Shows passwords on output instead of "MASKED". 105 | Useful to restart a complete run by just reading the log, 106 | or to debug passwords. It's not a secure practice. 107 | 108 | --passfile1 str : Password file for the user1. It must contain the 109 | password on the first line. This option avoids to show 110 | the password on the command line like --password1 does. 111 | --passfile2 str : Password file for the user2. Contains the password. 112 | 113 | OPTIONS/encryption 114 | 115 | --nossl1 : Do not use a SSL connection on host1. 116 | --ssl1 : Use a SSL connection on host1. On by default if possible. 117 | 118 | --nossl2 : Do not use a SSL connection on host2. 119 | --ssl2 : Use a SSL connection on host2. On by default if possible. 120 | 121 | --notls1 : Do not use a TLS connection on host1. 122 | --tls1 : Use a TLS connection on host1. On by default if possible. 123 | 124 | --notls2 : Do not use a TLS connection on host2. 125 | --tls2 : Use a TLS connection on host2. On by default if possible. 126 | 127 | --debugssl int : SSL debug mode from 0 to 4. 128 | 129 | --sslargs1 str : Pass any ssl parameter for host1 ssl or tls connection. Example: 130 | --sslargs1 SSL_verify_mode=1 --sslargs1 SSL_version=SSLv3 131 | See all possibilities in the new() method of IO::Socket::SSL 132 | http://search.cpan.org/perldoc?IO::Socket::SSL#Description_Of_Methods 133 | --sslargs2 str : Pass any ssl parameter for host2 ssl or tls connection. 134 | See --sslargs1 135 | 136 | --timeout1 int : Connection timeout in seconds for host1. 137 | Default is 120 and 0 means no timeout at all. 138 | --timeout2 int : Connection timeout in seconds for host2. 139 | Default is 120 and 0 means no timeout at all. 140 | 141 | OPTIONS/authentication 142 | 143 | --authmech1 str : Auth mechanism to use with host1: 144 | PLAIN, LOGIN, CRAM-MD5 etc. Use UPPERCASE. 145 | --authmech2 str : Auth mechanism to use with host2. See --authmech1 146 | 147 | --authuser1 str : User to auth with on host1 (admin user). 148 | Avoid using --authmech1 SOMETHING with --authuser1. 149 | --authuser2 str : User to auth with on host2 (admin user). 150 | --proxyauth1 : Use proxyauth on host1. Requires --authuser1. 151 | Required by Sun/iPlanet/Netscape IMAP servers to 152 | be able to use an administrative user. 153 | --proxyauth2 : Use proxyauth on host2. Requires --authuser2. 154 | 155 | --authmd51 : Use MD5 authentication for host1. 156 | --authmd52 : Use MD5 authentication for host2. 157 | --domain1 str : Domain on host1 (NTLM authentication). 158 | --domain2 str : Domain on host2 (NTLM authentication). 159 | 160 | OPTIONS/folders 161 | 162 | --folder str : Sync this folder. 163 | --folder str : and this one, etc. 164 | --folderrec str : Sync this folder recursively. 165 | --folderrec str : and this one, etc. 166 | 167 | --folderfirst str : Sync this folder first. --folderfirst "Work" 168 | --folderfirst str : then this one, etc. 169 | --folderlast str : Sync this folder last. --folderlast "[Gmail]/All Mail" 170 | --folderlast str : then this one, etc. 171 | 172 | --nomixfolders : Do not merge folders when host1 is case-sensitive 173 | while host2 is not (like Exchange). Only the first 174 | similar folder is synced (ex: with Sent SENT sent 175 | on host1 only Sent will be synced to host2). 176 | 177 | --skipemptyfolders : Empty host1 folders are not created on host2. 178 | 179 | --include reg : Sync folders matching this regular expression 180 | --include reg : or this one, etc. 181 | If both --include --exclude options are used, then 182 | include is done before. 183 | --exclude reg : Skips folders matching this regular expression 184 | Several folders to avoid: 185 | --exclude 'fold1|fold2|f3' skips fold1, fold2 and f3. 186 | --exclude reg : or this one, etc. 187 | 188 | --automap : guesses folders mapping, for folders well known as 189 | "Sent", "Junk", "Drafts", "All", "Archive", "Flagged". 190 | 191 | --f1f2 str1=str2 : Force folder str1 to be synced to str2, 192 | --f1f2 overrides --automap and --regextrans2. 193 | 194 | --subfolder2 str : Move whole host1 folders hierarchy under this 195 | host2 folder str . 196 | It does it by adding two --regextrans2 options before 197 | all others. Add --debug to see what's really going on. 198 | 199 | --subscribed : Transfers subscribed folders. 200 | --subscribe : Subscribe to the folders transferred on the 201 | host2 that are subscribed on host1. On by default. 202 | --subscribeall : Subscribe to the folders transferred on the 203 | host2 even if they are not subscribed on host1. 204 | 205 | --prefix1 str : Remove prefix str to all destination folders, 206 | usually INBOX. or INBOX/ or an empty string "". 207 | imapsync guesses the prefix if host1 imap server 208 | does not have NAMESPACE capability. This option 209 | should not be used, most of the time. 210 | --prefix2 str : Add prefix to all host2 folders. See --prefix1 211 | --sep1 str : Host1 separator in case NAMESPACE is not supported. 212 | --sep2 str : Host2 separator in case NAMESPACE is not supported. 213 | 214 | --regextrans2 reg : Apply the whole regex to each destination folders. 215 | --regextrans2 reg : and this one. etc. 216 | When you play with the --regextrans2 option, first 217 | add also the safe options --dry --justfolders 218 | Then, when happy, remove --dry, remove --justfolders. 219 | Have in mind that --regextrans2 is applied after prefix 220 | and separator inversion. For examples see 221 | https://imapsync.lamiral.info/FAQ.d/FAQ.Folders_Mapping.txt 222 | 223 | OPTIONS/folders sizes 224 | 225 | --nofoldersizes : Do not calculate the size of each folder at the 226 | beginning of the sync. Default is to calculate them. 227 | --nofoldersizesatend: Do not calculate the size of each folder at the 228 | end of the sync. Default is to calculate them. 229 | --justfoldersizes : Exit after having printed the initial folder sizes. 230 | 231 | OPTIONS/tmp 232 | 233 | --tmpdir str : Where to store temporary files and subdirectories. 234 | Will be created if it doesn't exist. 235 | Default is system specific, Unix is /tmp but 236 | /tmp is often too small and deleted at reboot. 237 | --tmpdir /var/tmp should be better. 238 | --pidfile str : The file where imapsync pid is written, 239 | it can be dirname/filename. 240 | Default name is imapsync.pid in tmpdir. 241 | --pidfilelocking : Abort if pidfile already exists. Useful to avoid 242 | concurrent transfers on the same mailbox. 243 | 244 | OPTIONS/log 245 | 246 | --nolog : Turn off logging on file 247 | --logfile str : Change the default log filename (can be dirname/filename). 248 | --logdir str : Change the default log directory. Default is LOG_imapsync/ 249 | 250 | OPTIONS/messages 251 | 252 | --skipmess reg : Skips messages matching the regex. 253 | Example: 'm/[\x80-ff]/' # to avoid 8bits messages. 254 | --skipmess is applied before --regexmess 255 | --skipmess reg : or this one, etc. 256 | 257 | --pipemess cmd : Apply this cmd command to each message content 258 | before the copy. 259 | --pipemess cmd : and this one, etc. 260 | 261 | --disarmreadreceipts : Disarms read receipts (host2 Exchange issue) 262 | 263 | --regexmess reg : Apply the whole regex to each message before transfer. 264 | Example: 's/\000/ /g' # to replace null by space. 265 | --regexmess reg : and this one, etc. 266 | 267 | OPTIONS/flags 268 | 269 | --regexflag reg : Apply the whole regex to each flags list. 270 | Example: 's/"Junk"//g' # to remove "Junk" flag. 271 | --regexflag reg : then this one, etc. 272 | 273 | --resyncflags : Resync flags for already transferred messages. 274 | On by default. 275 | --noresyncflags : Do not resync flags for already transferred messages. 276 | May be useful when a user has already started to play 277 | with its host2 account. 278 | 279 | OPTIONS/deletions 280 | 281 | --delete1 : Deletes messages on host1 server after a successful 282 | transfer. Option --delete1 has the following behavior: 283 | it marks messages as deleted with the IMAP flag 284 | \Deleted, then messages are really deleted with an 285 | EXPUNGE IMAP command. If expunging after each message 286 | slows down too much the sync then use 287 | --noexpungeaftereach to speed up. 288 | --expunge1 : Expunge messages on host1 just before syncing a folder. 289 | Expunge is done per folder. 290 | Expunge aims is to really delete messages marked deleted. 291 | An expunge is also done after each message copied 292 | if option --delete1 is set. 293 | --noexpunge1 : Do not expunge messages on host1. 294 | --delete1emptyfolders : Deletes empty folders on host1, INBOX excepted. 295 | Useful with --delete1 since what remains on host1 296 | is only what failed to be synced. 297 | 298 | --delete2 : Delete messages in host2 that are not in 299 | host1 server. Useful for backup or pre-sync. 300 | --delete2duplicates : Delete messages in host2 that are duplicates. 301 | Works only without --useuid since duplicates are 302 | detected with an header part of each message. 303 | 304 | --delete2folders : Delete folders in host2 that are not in host1 server. 305 | For safety, first try it like this (it is safe): 306 | --delete2folders --dry --justfolders --nofoldersizes 307 | --delete2foldersonly reg : Deleted only folders matching regex. 308 | Example: --delete2foldersonly "/^Junk$|^INBOX.Junk$/" 309 | --delete2foldersbutnot reg : Do not delete folders matching regex. 310 | Example: --delete2foldersbutnot "/Tasks$|Contacts$|Foo$/" 311 | 312 | --expunge2 : Expunge messages on host2 after messages transfer. 313 | --uidexpunge2 : uidexpunge messages on the host2 account 314 | that are not on the host1 account, requires --delete2 315 | 316 | OPTIONS/dates 317 | 318 | --syncinternaldates : Sets the internal dates on host2 same as host1. 319 | Turned on by default. Internal date is the date 320 | a message arrived on a host (mtime). 321 | --idatefromheader : Sets the internal dates on host2 same as the 322 | "Date:" headers. 323 | If you encounter problems with dates see also 324 | https://imapsync.lamiral.info/FAQ.d/FAQ.Dates.txt 325 | 326 | OPTIONS/message selection 327 | 328 | --maxsize int : Skip messages larger (or equal) than int bytes 329 | --minsize int : Skip messages smaller (or equal) than int bytes 330 | --maxage int : Skip messages older than int days. 331 | final stats (skipped) don't count older messages 332 | see also --minage 333 | --minage int : Skip messages newer than int days. 334 | final stats (skipped) don't count newer messages 335 | You can do (+ are the messages selected): 336 | past|----maxage+++++++++++++++>now 337 | past|+++++++++++++++minage---->now 338 | past|----maxage+++++minage---->now (intersection) 339 | past|++++minage-----maxage++++>now (union) 340 | 341 | --search str : Selects only messages returned by this IMAP SEARCH 342 | command. Applied on both sides. 343 | For a complete of what can be search see 344 | https://imapsync.lamiral.info/FAQ.d/FAQ.Messages_Selection.txt 345 | 346 | --search1 str : Same as --search but for selecting host1 messages only. 347 | --search2 str : Same as --search but for selecting host2 messages only. 348 | --search CRIT equals --search1 CRIT --search2 CRIT 349 | 350 | --maxlinelength int : skip messages with a line length longer than int bytes. 351 | RFC 2822 says it must be no more than 1000 bytes. 352 | 353 | 354 | --useheader str : Use this header to compare messages on both sides. 355 | Ex: Message-ID or Subject or Date. 356 | --useheader str and this one, etc. 357 | 358 | --usecache : Use cache to speed up the sync. 359 | --nousecache : Do not use cache. Caveat: --useuid --nousecache creates 360 | duplicates on multiple runs. 361 | --useuid : Use UIDs instead of headers as a criterium to recognize 362 | messages. Option --usecache is then implied unless 363 | --nousecache is used. 364 | 365 | OPTIONS/miscellaneous 366 | 367 | --syncacls : Synchronizes acls (Access Control Lists). 368 | --nosyncacls : Does not synchronize acls. This is the default. 369 | Acls in IMAP are not standardized, be careful. 370 | 371 | OPTIONS/debugging 372 | 373 | --debug : Debug mode. 374 | --debugfolders : Debug mode for the folders part only. 375 | --debugcontent : Debug content of the messages transferred. Huge output. 376 | --debugflags : Debug mode for flags. 377 | --debugimap1 : IMAP debug mode for host1. Very verbose. 378 | --debugimap2 : IMAP debug mode for host2. Very verbose. 379 | --debugimap : IMAP debug mode for host1 and host2. Twice very verbose. 380 | --debugmemory : Debug mode showing memory consumption after each copy. 381 | 382 | --errorsmax int : Exit when int number of errors is reached. Default is 50. 383 | 384 | --tests : Run local non-regression tests. Exit code 0 means all ok. 385 | --testslive : Run a live test with test1.lamiral.info imap server. 386 | Useful to check the basics. Needs internet connection. 387 | --testslive6 : Run a live test with ks2ipv6.lamiral.info imap server. 388 | Useful to check the ipv6 connectivity. Needs internet. 389 | 390 | OPTIONS/specific 391 | 392 | --gmail1 : sets --host1 to Gmail and options from FAQ.Gmail.txt 393 | --gmail2 : sets --host2 to Gmail and options from FAQ.Gmail.txt 394 | 395 | --office1 : sets --host1 to Office365 options from FAQ.Exchange.txt 396 | --office2 : sets --host2 to Office365 options from FAQ.Exchange.txt 397 | 398 | --exchange1 : sets options from FAQ.Exchange.txt, account1 part 399 | --exchange2 : sets options from FAQ.Exchange.txt, account2 part 400 | 401 | --domino1 : sets options from FAQ.Domino.txt, account1 part 402 | --domino2 : sets options from FAQ.Domino.txt, account2 part 403 | 404 | OPTIONS/behavior 405 | 406 | --maxmessagespersecond int : limits the number of messages transferred per second. 407 | 408 | --maxbytespersecond int : limits the average transfer rate per second. 409 | --maxbytesafter int : starts --maxbytespersecond limitation only after 410 | --maxbytesafter amount of data transferred. 411 | 412 | --maxsleep int : do not sleep more than int seconds. 413 | On by default, 2 seconds max, like --maxsleep 2 414 | 415 | --abort : terminates a previous call still running. 416 | It uses the pidfile to know what process to abort. 417 | 418 | --exitwhenover int : Stop syncing when total bytes transferred reached. 419 | 420 | --version : Print only software version. 421 | --noreleasecheck : Do not check for new imapsync release (a http request). 422 | --releasecheck : Check for new imapsync release (a http request). 423 | --noid : Do not send/receive ID command to imap servers. 424 | --justconnect : Just connect to both servers and print useful 425 | information. Need only --host1 and --host2 options. 426 | --justlogin : Just login to both host1 and host2 with users 427 | credentials, then exit. 428 | --justfolders : Do only things about folders (ignore messages). 429 | 430 | --help : print this help. 431 | 432 | Example: to synchronize imap account "test1" on "test1.lamiral.info" 433 | to imap account "test2" on "test2.lamiral.info" 434 | with test1 password "secret1" 435 | and test2 password "secret2" 436 | 437 | imapsync \ 438 | --host1 test1.lamiral.info --user1 test1 --password1 secret1 \ 439 | --host2 test2.lamiral.info --user2 test2 --password2 secret2 440 | 441 | SECURITY 442 | 443 | You can use --passfile1 instead of --password1 to give the password 444 | since it is safer. With --password1 option, any user on your host can 445 | see the password by using the 'ps auxwwww' command. Using a variable 446 | (like $PASSWORD1) is also dangerous because of the 'ps auxwwwwe' 447 | command. So, saving the password in a well protected file (600 or 448 | rw-------) is the best solution. 449 | 450 | Imapsync activates ssl or tls encryption by default, if possible. What 451 | detailed behavior is under this "if possible"? Imapsync activates ssl if 452 | the well known port imaps port (993) is open on the imap servers. If the 453 | imaps port is closed then it open a normal (clear) connection on port 454 | 143 but it looks for TLS support in the CAPABILITY list of the servers. 455 | If TLS is supported then imapsync goes to encryption. 456 | 457 | If the automatic ssl/tls detection fails then imapsync will not protect 458 | against sniffing activities on the network, especially for passwords. 459 | 460 | If you want to force ssl or tls just use --ssl1 --ssl2 or --tls1 --tls2 461 | 462 | See also the document FAQ.Security.txt in the FAQ.d/ directory or at 463 | https://imapsync.lamiral.info/FAQ.d/FAQ.Security.txt 464 | 465 | EXIT STATUS 466 | 467 | Imapsync will exit with a 0 status (return code) if everything went 468 | good. Otherwise, it exits with a non-zero status. 469 | 470 | LICENSE AND COPYRIGHT 471 | 472 | Imapsync is free, open, public but not always gratis software cover by 473 | the NOLIMIT Public License. See the LICENSE file included in the 474 | distribution or just read this simple sentence as it IS the licence 475 | text: 476 | 477 | "No limits to do anything with this work and this license." 478 | 479 | In case it is not long enough, I repeat: 480 | 481 | "No limits to do anything with this work and this license." 482 | 483 | https://imapsync.lamiral.info/LICENSE 484 | 485 | AUTHOR 486 | 487 | Gilles LAMIRAL 488 | 489 | Feedback good or bad is very often welcome. 490 | 491 | Gilles LAMIRAL earns his living by writing, installing, configuring and 492 | teaching free, open and often gratis software. Imapsync used to be 493 | "always gratis" but now it is only "often gratis" because imapsync is 494 | sold by its author, a good way to maintain and support free open public 495 | software over decades. 496 | 497 | BUGS AND LIMITATIONS 498 | 499 | See https://imapsync.lamiral.info/FAQ.d/FAQ.Reporting_Bugs.txt 500 | 501 | IMAP SERVERS supported 502 | 503 | See https://imapsync.lamiral.info/S/imapservers.shtml 504 | 505 | HUGE MIGRATION 506 | 507 | Pay special attention to options --subscribed --subscribe --delete1 508 | --delete1emptyfolders --delete2 --delete2folders --maxage --minage 509 | --maxsize --useuid --usecache 510 | 511 | If you have many mailboxes to migrate think about a little shell 512 | program. Write a file called file.txt (for example) containing users and 513 | passwords. The separator used in this example is ';' 514 | 515 | The file.txt file contains: 516 | 517 | user001_1;password001_1;user001_2;password001_2 518 | user002_1;password002_1;user002_2;password002_2 519 | user003_1;password003_1;user003_2;password003_2 520 | user004_1;password004_1;user004_2;password004_2 521 | user005_1;password005_1;user005_2;password005_2 ... 522 | 523 | On Unix the shell program can be: 524 | 525 | { while IFS=';' read u1 p1 u2 p2; do 526 | imapsync --host1 imap.side1.org --user1 "$u1" --password1 "$p1" \ 527 | --host2 imap.side2.org --user2 "$u2" --password2 "$p2" ... 528 | done ; } < file.txt 529 | 530 | On Windows the batch program can be: 531 | 532 | FOR /F "tokens=1,2,3,4 delims=; eol=#" %%G IN (file.txt) DO imapsync ^ 533 | --host1 imap.side1.org --user1 %%G --password1 %%H ^ 534 | --host2 imap.side2.org --user2 %%I --password2 %%J ... 535 | 536 | The ... have to be replaced by nothing or any imapsync option. Welcome 537 | in shell or batch programming ! 538 | 539 | You will find already written scripts at 540 | https://imapsync.lamiral.info/examples/ 541 | 542 | INSTALL 543 | 544 | Imapsync works under any Unix with perl. 545 | 546 | Imapsync works under most Windows (2000, XP, Vista, Seven, Eight, Ten 547 | and all Server releases 2000, 2003, 2008 and R2, 2012 and R2) 548 | as a standalone binary software called imapsync.exe, 549 | usually launched from a batch file in order to avoid always typing 550 | the options. 551 | 552 | Imapsync works under OS X as a standalone binary 553 | software called imapsync_bin_Darwin 554 | 555 | Purchase latest imapsync at 556 | https://imapsync.lamiral.info/ 557 | 558 | You'll receive a link to a compressed tarball called imapsync-x.xx.tgz 559 | where x.xx is the version number. Untar the tarball where 560 | you want (on Unix): 561 | 562 | tar xzvf imapsync-x.xx.tgz 563 | 564 | Go into the directory imapsync-x.xx and read the INSTALL file. 565 | As mentioned at https://imapsync.lamiral.info/#install 566 | the INSTALL file can also be found at 567 | https://imapsync.lamiral.info/INSTALL.d/INSTALL.ANY.txt 568 | It is now split in several files for each system 569 | https://imapsync.lamiral.info/INSTALL.d/ 570 | 571 | CONFIGURATION 572 | 573 | There is no specific configuration file for imapsync, everything is 574 | specified by the command line parameters and the default behavior. 575 | 576 | HACKING 577 | 578 | Feel free to hack imapsync as the NOLIMIT license permits it. 579 | 580 | SIMILAR SOFTWARE 581 | 582 | See also https://imapsync.lamiral.info/S/external.shtml 583 | for a better up to date list. 584 | 585 | imap_tools : https://github.com/andrewnimmo/rick-sanders-imap-tools 586 | offlineimap : https://github.com/nicolas33/offlineimap 587 | Doveadm-Sync : http://wiki2.dovecot.org/Tools/Doveadm/Sync 588 | ( Dovecot sync tool ) 589 | mbsync : http://isync.sourceforge.net/ 590 | mailsync : http://mailsync.sourceforge.net/ 591 | mailutil : http://www.washington.edu/imap/ 592 | part of the UW IMAP tookit. 593 | imaprepl : http://www.bl0rg.net/software/ 594 | http://freecode.com/projects/imap-repl/ 595 | imapcopy : http://www.ardiehl.de/imapcopy/ 596 | migrationtool : http://sourceforge.net/projects/migrationtool/ 597 | imapmigrate : http://sourceforge.net/projects/cyrus-utils/ 598 | wonko_imapsync: http://wonko.com/article/554 599 | see also file W/tools/wonko_ruby_imapsync 600 | exchange-away : http://exchange-away.sourceforge.net/ 601 | pop2imap : http://www.linux-france.org/prj/pop2imap/ 602 | 603 | Feedback (good or bad) will often be welcome. 604 | 605 | HISTORY 606 | 607 | I wrote imapsync because an enterprise (basystemes) paid me to install a 608 | new imap server without losing huge old mailboxes located in a far away 609 | remote imap server, accessible by a low-bandwidth often broken link. The 610 | tool imapcp (written in python) could not help me because I had to 611 | verify every mailbox was well transferred, and then delete it after a 612 | good transfer. Imapsync started its life as a patch of the 613 | copy_folder.pl script. The script copy_folder.pl comes from the 614 | Mail-IMAPClient-2.1.3 perl module tarball source (more precisely in the 615 | examples/ directory of the Mail-IMAPClient tarball). So many happened 616 | since then that I wonder if it remains any lines of the original 617 | copy_folder.pl in imapsync source code. 618 | 619 | -------------------------------------------------------------------------------- /imapsync_binaries/windows/README_Windows.txt: -------------------------------------------------------------------------------- 1 | # $Id: README_Windows.txt,v 1.11 2018/05/05 22:46:01 gilles Exp gilles $ 2 | # 3 | # This is the README_Windows.txt file for imapsync 4 | # imapsync : IMAP sync and migrate tool. 5 | 6 | WINDOWS 7 | ======= 8 | 9 | There is two ways to install and use imapsync on Windows systems: A) or B). 10 | 11 | Standard users should take the A) way, the simplest way. 12 | 13 | Developers, or powerful users that want to build their own imapsync.exe 14 | or modify it, have to consider the B) way, the complex but powerful way. 15 | 16 | A) Simplest way 17 | --------------- 18 | 19 | A.1) Get imapsync. 20 | 21 | Get imapsync at https://imapsync.lamiral.info/ 22 | You'll then have access to a zip archive file named imapsync_1.xxx.zip 23 | where 1.xxx is the imapsync release number. 24 | 25 | A.2) Extract the zip file in a folder where you will work with imapsync. 26 | 27 | You can work on the Desktop since the zip file extraction creates 28 | a unique folder named imapsync_1.xxx (where 1.xxx is the imapsync 29 | release number). 30 | 31 | A.3) Check the folder 32 | 33 | In the folder extracted imapsync_1.xxx you see 6 files and 2 directories: 34 | 35 | * README_Windows.txt is the current file you are reading. 36 | * README.txt is the imapsync general document. 37 | * FAQ.d/* FAQs are a good read when something goes wrong. 38 | * imapsync_example.bat is a simple batch file example you will copy and edit. 39 | * sync_loop_windows.bat is a batch file example for syncing many accounts. 40 | * file.txt is an input file example for syncing many accounts. 41 | * imapsync.exe is the imapsync binary. You don't have to run it directly. 42 | * Cook/ is the directory to build imapsync.exe from its source, 43 | for the B) way and expert users. 44 | 45 | You can copy or rename imapsync_example.bat as you wish as long as 46 | its extension remains ".bat". On Windows systems .bat extension 47 | means "I'm a batch script". Same thing for sync_loop_windows.bat. 48 | The batch scripts have to stay in the same directory than 49 | imapsync.exe because of the way they call imapsync.exe, 50 | they use ".\imapsync.exe", so let them be in the same directory. 51 | Or change the path .\ to whatever you want if you understand what 52 | you're doing. 53 | 54 | For the rest of this documentation I assume you copied 55 | imapsync_example.bat to a file named imapsync_stuff.bat 56 | 57 | A.4) Edit the batch file 58 | 59 | Edit imapsync_stuff.bat and change the values with yours. 60 | In order to edit it you have do a right click on it and select "modify" 61 | in the list presented in the small window menu. 62 | Notepad or Notepadd++ are very good editors to modify it. 63 | Office Word or any powerful text processor are not good for that job, 64 | don't use them! 65 | 66 | Files FAQ.txt and FAQ.d/* contain many tips and special options sometimes 67 | needed by specific imap server softwares like Exchange or Gmail. 68 | 69 | 70 | A.5) Run the batch file 71 | 72 | To run imapsync with your values just double-click on 73 | the batch file imapsync_stuff.bat 74 | 75 | You do not need to have administrator privileges to run imapsync. 76 | 77 | A.6) Loop on A.5) A.6) edit, run, edit, run etc. 78 | 79 | Loop the process of editing and running imapsync until 80 | you solve all issues and all values suit your needs. 81 | 82 | A.7) Look the sync running. You can abort it at any time with a 83 | quick double ctrl-c, hit ctrl-c twice within one second. 84 | (a single ctrl-c will reconnect to both imap servers) 85 | 86 | A.8) When the sync is finished you can find the whole log of the output 87 | in the folder named "LOG_imapsync", the logfile name is based 88 | on the launching date, hour, minute, second, miliseconds and the 89 | user2 parameter. There is one logfile per run. 90 | The logfile name is printed at the end of the imapsync run. 91 | If you do not want logging to a file then use option --nolog 92 | 93 | 94 | B) Hard way. It is the hard way because it installs all software 95 | dependencies. This is the way for modifying imapsync.exe if needed. 96 | 97 | B.1) Install Perl if it isn't already installed. 98 | Strawberry Perl is a very good candidate 99 | http://strawberryperl.com/ 100 | I use 5.26.0.1 (31 may 2017) but previous and later releases 101 | should work (5.18 and 5.20 do) as well. 102 | 103 | B.2) Go into the Cook/ directory 104 | B.3) Double-clic build_exe.bat 105 | 106 | It should create a binary imapsync.exe in the current Cook/ directory. 107 | 108 | B.4) Move imapsync.exe in the upper directory and follow instructions 109 | from A.3) to A.8) 110 | 111 | -------------------------------------------------------------------------------- /imapsync_binaries/windows/file.txt: -------------------------------------------------------------------------------- 1 | # Example file.txt for imapsync massive migration. 2 | # 3 | # $Id: file.txt,v 1.14 2018/02/11 13:42:58 gilles Exp gilles $ 4 | # 5 | # Each line contains 6 columns, columns are parameters for 6 | # --host1 --user1 --password1 --host2 --user2 --password2 7 | # and a trailing empty fake column to avoid CR LF part going 8 | # in the 6th parameter password2. Don't forget the last semicolon. 9 | # 10 | # Windows: see the script examples/sync_loop_windows.bat 11 | # Unix: see the script examples/sync_loop_unix.sh 12 | # 13 | # Lines starting with a # are comments and ignored 14 | # Blank lines are ignored as well 15 | 16 | 17 | # Now the data example 18 | host001_1;user001_1;password001_1;host001_2;user001_2;password001_2; 19 | host002_1;user002_1;password002_1;host002_2;user002_2;password002_2; 20 | host003_1;user003_1;password003_1;host003_2;user003_2;password003_2; 21 | 22 | # Another comment blabla 23 | host004_1;user004_1;password004_1;host004_2;user004_2;password004_2; 24 | 25 | # This last example is a real one, ie, truly working in the real world. 26 | test1.lamiral.info;test1;secret1;test2.lamiral.info;test2;secret2; 27 | -------------------------------------------------------------------------------- /imapsync_binaries/windows/imapsync.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ridaamirini/ImapSyncClient/23070ab730323ce1f3e1238dfbee5a07579a41c4/imapsync_binaries/windows/imapsync.exe -------------------------------------------------------------------------------- /imapsync_binaries/windows/imapsync_example.bat: -------------------------------------------------------------------------------- 1 | @REM $Id: imapsync_example.bat,v 1.10 2016/04/07 23:14:09 gilles Exp gilles $ 2 | 3 | @REM imapsync example batch for Windows users 4 | @REM lines beginning with @REM are just comments 5 | 6 | @REM See http://imapsync.lamiral.info/#doc 7 | @REM for more details on how to use imapsync. 8 | 9 | @REM Replace below the 6 parameters 10 | @REM "test1.lamiral.info" "test1" "secret1" "test2.lamiral.info" "test2" "secret2" 11 | @REM with your own values 12 | @REM Double quotes are necessary if a value contain one or more blanks. 13 | 14 | @REM value "test1.lamiral.info" for --host1 is the IMAP source server hostname or IP address 15 | @REM value "test1" for --user1 is the IMAP source user login 16 | @REM value "secret1" for --password1 is the IMAP source user password 17 | 18 | @REM value "test2.lamiral.info" for --host2 is the IMAP destination server hostname or IP address 19 | @REM value "test2" for --user2 is the IMAP destination user login 20 | @REM value "secret2" for --password2 is the IMAP destination user password 21 | 22 | @REM Character ^ at the end of the first line is essential and means 23 | @REM "this command continues on the next line". You can add other lines 24 | @REM but don't forget ^ character lasting each line, except the last one. 25 | 26 | 27 | @REM Three other options are in this example because they are good to start with 28 | @REM 29 | @REM --dry makes imapsync doing nothing, just print what would be done without --dry. 30 | @REM 31 | @REM --justfolders does only things about folders (ignore messages). It is good 32 | @REM to verify the folder mapping is good for you. 33 | @REM 34 | @REM --automap guesses folders mapping, for folders like 35 | @REM "Sent", "Junk", "Drafts", "All", "Archive", "Flagged". 36 | @REM 37 | @REM I suggest to start with --automap --justfolders --dry. 38 | @REM If the folder mapping is not good then add some --f1f2 folder1=folder2 39 | @REM to fix it. 40 | @REM Then remove --dry and have a run to create folders on host2. 41 | @REM If everything goes well so far then remove --justfolders to 42 | @REM start syncing messages. 43 | 44 | .\imapsync.exe --host1 test1.lamiral.info --user1 test1 --password1 "secret1" ^ 45 | --host2 test2.lamiral.info --user2 test2 --password2 "secret2" ^ 46 | --automap --justfolders --dry 47 | 48 | 49 | @PAUSE 50 | 51 | -------------------------------------------------------------------------------- /imapsync_binaries/windows/sync_loop_windows.bat: -------------------------------------------------------------------------------- 1 | @REM 2 | @REM $Id: sync_loop_windows.bat,v 1.18 2018/05/24 11:45:42 gilles Exp gilles $ 3 | @REM 4 | @REM imapsync massive sync example batch for Windows users 5 | @REM lines beginning with @REM are just comments 6 | @REM 7 | @REM See also http://imapsync.lamiral.info/FAQ.d/FAQ.Massive.txt 8 | @REM 9 | @REM You should get familiar with a simple and single imapsync transfer before 10 | @REM playing with this loop batch file. See and play with imapsync_example.bat 11 | 12 | @REM ==== How it works ==== 13 | @REM 14 | @REM The files 15 | @REM * sync_loop_windows.bat 16 | @REM * imapsync or imapsync.exe and 17 | @REM * file.txt 18 | @REM are supposed to be in the same directory. 19 | 20 | 21 | @REM ==== Credentials file ==== 22 | @REM 23 | @REM Credentials data are supposed to be in the file named "file.txt" in the following format: 24 | @REM host001_1;user001_1;password001_1;host001_2;user001_2;password001_2; 25 | @REM ... 26 | @REM Separator is character semi-colon ; it can be replaced with any character by changing 27 | @REM the part "delims=;" in the FOR loop below. 28 | @REM 29 | @REM Each data line contains 6 columns, columns are parameter values for 30 | @REM --host1 --user1 --password1 --host2 --user2 --password2 31 | @REM and a fake parameter to avoid CRLF part going into the 6th parameter password2. 32 | @REM The credentials filename "file.txt" used for the loop can be renamed 33 | @REM by changing "SET csvfile=file.txt" below. 34 | 35 | @REM ==== Log files ==== 36 | @REM 37 | @REM Log files are in the LOG_imapsync sub-folder 38 | 39 | @REM ==== Parallel executions ==== 40 | @REM 41 | @REM If you want to do parallel runs of imapsync then this current script is a good start. 42 | @REM Just copy it several times and replace, on each copy, the csvfile variable value. 43 | @REM Instead of SET csvfile=file.txt write for example 44 | @REM SET csvfile=file01.txt in the first copy 45 | @REM then also 46 | @REM SET csvfile=file02.txt in the second copy etc. 47 | @REM Of course you also have to split data contained in file.txt 48 | @REM into file01.txt file02.txt etc. 49 | @REM After that, just double-clic on each batch file to launch each process 50 | @REM 51 | @REM Be aware that imapsync can be a cpu/memory cruncher on the remote imap servers, 52 | @REM especially in parallel runs. The best practice rule to answer the question 53 | @REM "how many processes in parallel can we run?" is: 54 | @REM 1) Measure the total transfer rate by adding each one printed in each run. 55 | @REM 2) Launch new parallel runs as long as the total transfer rate increase. 56 | @REM 3) When the total transfer rate starts to diminish, stop new launches. 57 | @REM Note N as the number of parallel runs you got until then. 58 | @REM 4) Only keep N-2 parallel runs for the future. 59 | 60 | @REM For Parallel executions, there is also a PowerShell script written by 61 | @REM CARTER Alex explained and located on the imapsync archive list: 62 | @REM http://www.linux-france.org/prj/imapsync_list/msg02137.html 63 | 64 | @REM ==== The real stuff is below ==== 65 | 66 | @REM @echo off 67 | 68 | @REM First let's go in the directory this batch is 69 | CD /D %~dp0 70 | 71 | @REM Let's get arguments of this batch, they will be added to imapsync arguments, if any. 72 | @REM Do not touch this part to add arguments to imapsync, do that in the FOR loop below 73 | @SET arguments= & @SET command=%~0 74 | @IF %1. EQU . GOTO args_end 75 | :args_loop 76 | @SET arguments=%arguments% %1 & @SHIFT 77 | @IF %1. NEQ . GOTO args_loop 78 | @ECHO Command and arguments: %command% %arguments% 79 | :args_end 80 | 81 | @REM Now the loop on the csv file. 82 | SET csvfile=file.txt 83 | 84 | @FOR /F "tokens=1,2,3,4,5,6,7 delims=; eol=#" %%G IN (%csvfile%) DO ( 85 | @REM Blank lines are usually ignored. Dumping the tokens in [] in case debugging is needed 86 | @ECHO GOT those values from %csvfile% presented inside brackets: [%%G] [%%H] [%%I] [%%J] [%%K] [%%L] [%%M] 87 | @REM You can add extra arguments to imapsync after the variable named %arguments% 88 | @ECHO ==== Starting imapsync from --host1 %%G --user1 %%H to --host2 %%J --user2 %%K ==== 89 | @imapsync ^ 90 | --host1 %%G --user1 %%H --password1 %%I ^ 91 | --host2 %%J --user2 %%K --password2 %%L %%M %arguments% 92 | @ECHO ==== Ended imapsync from --host1 %%G --user1 %%H to --host2 %%J --user2 %%K ==== 93 | @ECHO. 94 | ) 95 | 96 | @ECHO Loop finished! 97 | @ECHO Log files are in LOG_imapsync directory 98 | @PAUSE 99 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "imapsyncclient", 3 | "displayName": "ImapSync Client", 4 | "productName": "ImapSync Client", 5 | "homepage": "https://github.com/ridaamirini/ImapSyncClient", 6 | "version": "1.0.1", 7 | "author": "Rida A. ", 8 | "description": "It's only an Internet Message Access Protocol Synchronization Client", 9 | "license": "MIT", 10 | "main": "./dist/electron/main.js", 11 | "scripts": { 12 | "revision": "node .electron-vue/revision.js", 13 | "release": "node .electron-vue/build.js && electron-builder", 14 | "build": "CSC_IDENTITY_AUTO_DISCOVERY=false node .electron-vue/build.js && electron-builder", 15 | "build:dir": "node .electron-vue/build.js && electron-builder --dir", 16 | "build:clean": "cross-env BUILD_TARGET=clean node .electron-vue/build.js", 17 | "build:web": "cross-env BUILD_TARGET=web node .electron-vue/build.js", 18 | "dev": "node .electron-vue/dev-runner.js", 19 | "lint": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter src", 20 | "lint:fix": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter --fix src", 21 | "pack": "npm run pack:main && npm run pack:renderer", 22 | "pack:main": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.main.config.js", 23 | "pack:renderer": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.renderer.config.js", 24 | "postinstall": "npm run lint:fix", 25 | "impasync:upgrade:binaries": "node .imapsync/upgrade.js" 26 | }, 27 | "build": { 28 | "productName": "ImapSync Client", 29 | "appId": "de.ridasmartapps.imapsyncclient", 30 | "directories": { 31 | "output": "build" 32 | }, 33 | "files": [ 34 | "dist/electron/**/*", 35 | "package.json", 36 | "revision.json" 37 | ], 38 | "extraResources": [ 39 | { 40 | "from": "imapsync_binaries/", 41 | "to": "imapsync_binaries/", 42 | "filter": [ 43 | "**/*" 44 | ] 45 | } 46 | ], 47 | "dmg": { 48 | "contents": [ 49 | { 50 | "x": 410, 51 | "y": 150, 52 | "type": "link", 53 | "path": "/Applications" 54 | }, 55 | { 56 | "x": 130, 57 | "y": 150, 58 | "type": "file" 59 | } 60 | ] 61 | }, 62 | "mac": { 63 | "icon": "build/icons/icon.icns" 64 | }, 65 | "win": { 66 | "icon": "build/icons/icon.ico", 67 | "target": [ 68 | { 69 | "target": "nsis", 70 | "arch": [ 71 | "x64", 72 | "ia32" 73 | ] 74 | } 75 | ] 76 | }, 77 | "linux": { 78 | "icon": "build/icons", 79 | "executableName": "imapsyncclient", 80 | "synopsis": "ImapSync Client", 81 | "category": "Development", 82 | "target": [ 83 | "AppImage", 84 | "deb" 85 | ] 86 | } 87 | }, 88 | "dependencies": { 89 | "axios": "^0.18.1", 90 | "electron-is": "^3.0.0", 91 | "electron-log": "^1.3.0", 92 | "electron-updater": "^4.0.0", 93 | "element-ui": "^2.0.5", 94 | "git-rev-sync": "^1.9.1", 95 | "vue": "^2.3.3", 96 | "vue-electron": "^1.0.6", 97 | "vue-router": "^2.5.3", 98 | "vuex": "^2.3.1" 99 | }, 100 | "devDependencies": { 101 | "babel-core": "^6.25.0", 102 | "babel-eslint": "^7.2.3", 103 | "babel-loader": "^7.1.1", 104 | "babel-plugin-transform-runtime": "^6.23.0", 105 | "babel-preset-env": "^1.6.0", 106 | "babel-preset-stage-0": "^6.24.1", 107 | "babel-register": "^6.24.1", 108 | "babili-webpack-plugin": "^0.1.2", 109 | "cfonts": "^1.1.3", 110 | "chalk": "^2.1.0", 111 | "copy-webpack-plugin": "^4.0.1", 112 | "cross-env": "^5.0.5", 113 | "css-loader": "^0.28.4", 114 | "del": "^3.0.0", 115 | "devtron": "^1.4.0", 116 | "electron": "^3.0.16", 117 | "electron-builder": "^22.3.2", 118 | "electron-debug": "^1.4.0", 119 | "electron-devtools-installer": "^2.2.0", 120 | "eslint": "^4.4.1", 121 | "eslint-config-standard": "^10.2.1", 122 | "eslint-friendly-formatter": "^3.0.0", 123 | "eslint-loader": "^1.9.0", 124 | "eslint-plugin-html": "^3.1.1", 125 | "eslint-plugin-import": "^2.7.0", 126 | "eslint-plugin-node": "^5.1.1", 127 | "eslint-plugin-promise": "^3.5.0", 128 | "eslint-plugin-standard": "^3.0.1", 129 | "extract-text-webpack-plugin": "^3.0.0", 130 | "file-loader": "^0.11.2", 131 | "html-webpack-plugin": "^2.30.1", 132 | "multispinner": "^0.2.1", 133 | "node-loader": "^0.6.0", 134 | "style-loader": "^0.18.2", 135 | "url-loader": "^0.5.9", 136 | "vue-html-loader": "^1.2.4", 137 | "vue-loader": "^13.0.5", 138 | "vue-style-loader": "^3.0.1", 139 | "vue-template-compiler": "^2.4.2", 140 | "webpack": "^3.5.2", 141 | "webpack-dev-server": "^2.7.1", 142 | "webpack-hot-middleware": "^2.18.2" 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /revision.json: -------------------------------------------------------------------------------- 1 | { 2 | "rev": "25bd519", 3 | "version": "1.0.1.25bd519", 4 | "date": "1/28/2020, 12:07:45 PM" 5 | } -------------------------------------------------------------------------------- /src/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ImapSync Client 7 | 8 | 9 | <% if (htmlWebpackPlugin.options.nodeModules) { %> 10 | 11 | 14 | <% } %> 15 | 16 | 17 |
18 | 19 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/main/imapsync.js: -------------------------------------------------------------------------------- 1 | import is from 'electron-is'; 2 | import fs from 'fs'; 3 | import path from 'path'; 4 | import {execFile} from 'child_process'; 5 | import {remote} from 'electron'; 6 | 7 | // Childprocess id 8 | let childProcess = null; 9 | const pidFilePath = path.join(remote.app.getPath('temp'), '/imapsync.pid'); 10 | 11 | // Imapsync command options 12 | const commandOptions = [ 13 | '--nolog', 14 | '--noreleasecheck', 15 | '--pidfile=' + pidFilePath, 16 | '--pidfilelocking' 17 | ]; 18 | 19 | function getExecutablePath() { 20 | let binaryPath = null; 21 | 22 | if (is.windows()) { 23 | binaryPath = 'windows/imapsync.exe'; 24 | } 25 | else if (is.macOS()) { 26 | binaryPath = 'darwin/imapsync'; 27 | } 28 | else if (is.linux()) { 29 | binaryPath = 'bin/imapsync'; 30 | } 31 | 32 | if (is.production()) { 33 | return path.join(process.resourcesPath, 'imapsync_binaries/', binaryPath); 34 | } 35 | 36 | return path.join(__dirname, '../../imapsync_binaries/', binaryPath); 37 | } 38 | 39 | function mapImapsyncCommandArgs(mailbox) { 40 | return [ 41 | '--host1=' + mailbox.imap_from, 42 | '--user1=' + mailbox.mailbox_from, 43 | '--password1=' + mailbox.password_from, 44 | '--host2=' + mailbox.imap_to, 45 | '--user2=' + mailbox.mailbox_to, 46 | '--password2=' + mailbox.password_to 47 | ]; 48 | } 49 | 50 | function imapsync(mailbox) { 51 | return new Promise((resolve, reject) => { 52 | let messages = { 53 | success: null, 54 | failure: null 55 | }; 56 | 57 | childProcess = execFile( 58 | getExecutablePath(), 59 | [...mapImapsyncCommandArgs(mailbox), ...commandOptions], 60 | {encoding: 'utf8'}, 61 | (error, stdout, stderr) => { 62 | messages.success = stdout; 63 | if (stderr || error) { 64 | messages.failure = stderr || stdout || error; 65 | } 66 | } 67 | ); 68 | 69 | childProcess.on('close', code => { 70 | if (code !== 0 && messages.failure) { 71 | reject(messages.failure); 72 | } 73 | 74 | resolve(messages.success); 75 | }); 76 | }); 77 | } 78 | 79 | function abortImapsync() { 80 | childProcess.kill('SIGINT'); 81 | 82 | if (fs.existsSync(pidFilePath)) { 83 | fs.unlink(pidFilePath, error => { 84 | if (error) console.error(error); 85 | }); 86 | } 87 | } 88 | 89 | export { 90 | abortImapsync, 91 | imapsync 92 | }; 93 | -------------------------------------------------------------------------------- /src/main/index.dev.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is used specifically and only for development. It installs 3 | * `electron-debug` & `vue-devtools`. There shouldn't be any need to 4 | * modify this file, but it can be used to extend your development 5 | * environment. 6 | */ 7 | 8 | /* eslint-disable */ 9 | 10 | // Set environment for development 11 | process.env.NODE_ENV = 'development' 12 | 13 | // Install `electron-debug` with `devtron` 14 | require('electron-debug')({ showDevTools: true }) 15 | 16 | // Install `vue-devtools` 17 | require('electron').app.on('ready', () => { 18 | let installExtension = require('electron-devtools-installer') 19 | installExtension.default(installExtension.VUEJS_DEVTOOLS) 20 | .then(() => {}) 21 | .catch(err => { 22 | console.log('Unable to install `vue-devtools`: \n', err) 23 | }) 24 | }) 25 | 26 | // Require `main` process to boot app 27 | require('./index') 28 | -------------------------------------------------------------------------------- /src/main/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { app, BrowserWindow, ipcMain } from 'electron'; 4 | import { checkForUpdates } from './updater'; 5 | 6 | /** 7 | * Set `__static` path to static files in production 8 | * https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-static-assets.html 9 | */ 10 | if (process.env.NODE_ENV !== 'development') { 11 | global.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\'); 12 | } 13 | 14 | let mainWindow; 15 | const winURL = process.env.NODE_ENV === 'development' 16 | ? `http://localhost:9080` 17 | : `file://${__dirname}/index.html`; 18 | 19 | function createWindow () { 20 | /** 21 | * Initial window options 22 | */ 23 | 24 | // Linux & Windows 25 | let options = { 26 | title: app.getName(), 27 | height: 680, 28 | width: 1050, 29 | useContentSize: true, 30 | resizable: false, 31 | fullscreen: false, 32 | backgroundColor: '#272d33', 33 | show: false 34 | }; 35 | 36 | if (process.platform === 'darwin') { 37 | options = { 38 | title: app.getName(), 39 | height: 680, 40 | width: 1050, 41 | useContentSize: true, 42 | resizable: false, 43 | frame: true, 44 | titleBarStyle: 'hiddenInset', 45 | fullscreen: false, 46 | backgroundColor: '#272d33', 47 | show: false 48 | }; 49 | } 50 | 51 | mainWindow = new BrowserWindow(options); 52 | 53 | mainWindow.loadURL(winURL); 54 | 55 | mainWindow.on('closed', () => { 56 | mainWindow = null; 57 | }); 58 | 59 | mainWindow.once('ready-to-show', () => { 60 | mainWindow.show(); 61 | }); 62 | } 63 | 64 | // Single Instance 65 | const instanceToQuit = app.requestSingleInstanceLock(); 66 | 67 | if (!instanceToQuit) { 68 | app.quit(); 69 | } else { 70 | app.on('second-instance', () => { 71 | if (mainWindow) { 72 | if (mainWindow.isMinimized()) mainWindow.restore(); 73 | mainWindow.focus(); 74 | } 75 | }); 76 | } 77 | 78 | app.on('window-all-closed', () => { 79 | if (process.platform !== 'darwin') { 80 | app.quit(); 81 | } 82 | }); 83 | 84 | app.on('activate', () => { 85 | if (mainWindow === null) { 86 | createWindow(); 87 | } 88 | }); 89 | 90 | app.on('ready', createWindow); 91 | 92 | ipcMain.once('check-update', checkForUpdates); 93 | -------------------------------------------------------------------------------- /src/main/menu.js: -------------------------------------------------------------------------------- 1 | const {app, Menu} = require('electron'); 2 | 3 | const template = [ 4 | { 5 | role: 'help', 6 | submenu: [ 7 | { 8 | label: 'Learn More', 9 | click () { require('electron').shell.openExternal('https://github.com/ridaamirini/ImapSyncClient'); } 10 | } 11 | ] 12 | } 13 | ]; 14 | 15 | if (process.platform === 'darwin') { 16 | template.unshift({ 17 | label: app.getName(), 18 | submenu: [ 19 | {role: 'about'}, 20 | {role: 'quit'} 21 | ] 22 | }); 23 | 24 | // Window menu 25 | template[3].submenu = [ 26 | {role: 'close'}, 27 | {role: 'minimize'}, 28 | {role: 'zoom'}, 29 | {type: 'separator'}, 30 | {role: 'front'} 31 | ]; 32 | } 33 | 34 | const menu = Menu.buildFromTemplate(template); 35 | Menu.setApplicationMenu(menu); 36 | -------------------------------------------------------------------------------- /src/main/updater.js: -------------------------------------------------------------------------------- 1 | import { autoUpdater } from 'electron-updater'; 2 | import log from 'electron-log'; 3 | import { app, ipcMain } from 'electron'; 4 | 5 | let isAutomaticallyCheckingForUpdates = false; 6 | 7 | app.once('browser-window-focus', (event, win) => { 8 | let __updateWin; 9 | 10 | // Set logger 11 | autoUpdater.logger = log; 12 | autoUpdater.logger.transports.file.level = 'info'; 13 | 14 | // Configure updater 15 | autoUpdater.allowPrerelease = false; 16 | autoUpdater.autoDownload = false; 17 | 18 | autoUpdater.on('update-available', ({version, releaseNotes}) => { 19 | if (__updateWin) { 20 | return; 21 | } 22 | 23 | win.webContents.send('update-available', { 24 | version, 25 | releaseNotes, 26 | currentVersion: app.getVersion() 27 | }); 28 | 29 | __updateWin = win; 30 | 31 | __updateWin.on('close', () => { 32 | __updateWin = null; 33 | }); 34 | }); 35 | 36 | autoUpdater.on('update-not-available', () => { 37 | if (__updateWin) return; 38 | 39 | if (isAutomaticallyCheckingForUpdates) { 40 | isAutomaticallyCheckingForUpdates = false; 41 | return; 42 | } 43 | 44 | win.webContents.send('update-not-available'); 45 | }); 46 | 47 | autoUpdater.on('update-downloaded', () => { 48 | autoUpdater.quitAndInstall(); 49 | }); 50 | 51 | autoUpdater.on('download-progress', progress => { 52 | if (__updateWin) { 53 | __updateWin.webContents.send('download-progress', progress); 54 | } 55 | }); 56 | 57 | autoUpdater.on('error', error => { 58 | if (__updateWin) { 59 | __updateWin.webContents.send('update-error', error); 60 | } 61 | }); 62 | 63 | ipcMain.on('download-update', () => { 64 | if (process.env.NODE_ENV === 'production') autoUpdater.downloadUpdate(); 65 | }); 66 | 67 | ipcMain.on('check-update-manually', () => { 68 | if (process.env.NODE_ENV === 'production') autoUpdater.checkForUpdates(); 69 | }); 70 | }); 71 | 72 | export function checkForUpdates () { 73 | if (process.env.NODE_ENV !== 'production') return; 74 | 75 | isAutomaticallyCheckingForUpdates = true; 76 | autoUpdater.checkForUpdates(); 77 | } 78 | -------------------------------------------------------------------------------- /src/renderer/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 48 | -------------------------------------------------------------------------------- /src/renderer/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ridaamirini/ImapSyncClient/23070ab730323ce1f3e1238dfbee5a07579a41c4/src/renderer/assets/.gitkeep -------------------------------------------------------------------------------- /src/renderer/assets/app/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ridaamirini/ImapSyncClient/23070ab730323ce1f3e1238dfbee5a07579a41c4/src/renderer/assets/app/icons/128x128.png -------------------------------------------------------------------------------- /src/renderer/assets/app/icons/16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ridaamirini/ImapSyncClient/23070ab730323ce1f3e1238dfbee5a07579a41c4/src/renderer/assets/app/icons/16x16.png -------------------------------------------------------------------------------- /src/renderer/assets/app/icons/24x24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ridaamirini/ImapSyncClient/23070ab730323ce1f3e1238dfbee5a07579a41c4/src/renderer/assets/app/icons/24x24.png -------------------------------------------------------------------------------- /src/renderer/assets/app/icons/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ridaamirini/ImapSyncClient/23070ab730323ce1f3e1238dfbee5a07579a41c4/src/renderer/assets/app/icons/256x256.png -------------------------------------------------------------------------------- /src/renderer/assets/app/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ridaamirini/ImapSyncClient/23070ab730323ce1f3e1238dfbee5a07579a41c4/src/renderer/assets/app/icons/32x32.png -------------------------------------------------------------------------------- /src/renderer/assets/app/icons/48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ridaamirini/ImapSyncClient/23070ab730323ce1f3e1238dfbee5a07579a41c4/src/renderer/assets/app/icons/48x48.png -------------------------------------------------------------------------------- /src/renderer/assets/app/icons/512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ridaamirini/ImapSyncClient/23070ab730323ce1f3e1238dfbee5a07579a41c4/src/renderer/assets/app/icons/512x512.png -------------------------------------------------------------------------------- /src/renderer/assets/app/icons/64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ridaamirini/ImapSyncClient/23070ab730323ce1f3e1238dfbee5a07579a41c4/src/renderer/assets/app/icons/64x64.png -------------------------------------------------------------------------------- /src/renderer/assets/app/icons/96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ridaamirini/ImapSyncClient/23070ab730323ce1f3e1238dfbee5a07579a41c4/src/renderer/assets/app/icons/96x96.png -------------------------------------------------------------------------------- /src/renderer/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ridaamirini/ImapSyncClient/23070ab730323ce1f3e1238dfbee5a07579a41c4/src/renderer/assets/logo.png -------------------------------------------------------------------------------- /src/renderer/assets/logo_heart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/renderer/assets/logo_sync.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/renderer/components/About.vue: -------------------------------------------------------------------------------- 1 | 47 | 116 | -------------------------------------------------------------------------------- /src/renderer/components/Add/MailboxForm.vue: -------------------------------------------------------------------------------- 1 | 60 | 61 | 275 | -------------------------------------------------------------------------------- /src/renderer/components/ImportCsvButton.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /src/renderer/components/MainComponent.vue: -------------------------------------------------------------------------------- 1 | 45 | 46 | 113 | 114 | -------------------------------------------------------------------------------- /src/renderer/components/QueueTable/QueueTable.vue: -------------------------------------------------------------------------------- 1 | 61 | 62 | 88 | 89 | -------------------------------------------------------------------------------- /src/renderer/components/QueueTable/QueueTablePassword.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 41 | -------------------------------------------------------------------------------- /src/renderer/components/ShellAlikeBox.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 83 | -------------------------------------------------------------------------------- /src/renderer/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { webFrame } from 'electron'; 3 | import axios from 'axios'; 4 | import ElementUI from 'element-ui'; 5 | import 'element-ui/lib/theme-chalk/index.css'; 6 | 7 | import App from './App'; 8 | import router from './router'; 9 | import store from './store'; 10 | 11 | Vue.use(ElementUI); 12 | 13 | if (!process.env.IS_WEB) Vue.use(require('vue-electron')); 14 | Vue.http = Vue.prototype.$http = axios; 15 | Vue.config.productionTip = false; 16 | 17 | /* eslint-disable no-new */ 18 | new Vue({ 19 | components: { App }, 20 | router, 21 | store, 22 | template: '' 23 | }).$mount('#app'); 24 | 25 | // WebFrame disable zoom in/out 26 | webFrame.setVisualZoomLevelLimits(1, 1); 27 | webFrame.setLayoutZoomLevelLimits(0, 0); 28 | -------------------------------------------------------------------------------- /src/renderer/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Router from 'vue-router'; 3 | 4 | Vue.use(Router); 5 | 6 | export default new Router({ 7 | routes: [ 8 | { 9 | path: '/', 10 | name: 'main-component', 11 | component: require('@/components/MainComponent').default 12 | }, 13 | { 14 | path: '*', 15 | redirect: '/' 16 | } 17 | ] 18 | }); 19 | -------------------------------------------------------------------------------- /src/renderer/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuex from 'vuex'; 3 | 4 | import modules from './modules'; 5 | 6 | Vue.use(Vuex); 7 | 8 | export default new Vuex.Store({ 9 | modules, 10 | strict: process.env.NODE_ENV !== 'production' 11 | }); 12 | -------------------------------------------------------------------------------- /src/renderer/store/modules/Counter.js: -------------------------------------------------------------------------------- 1 | const state = { 2 | main: 0 3 | }; 4 | 5 | const mutations = { 6 | DECREMENT_MAIN_COUNTER (state) { 7 | state.main--; 8 | }, 9 | INCREMENT_MAIN_COUNTER (state) { 10 | state.main++; 11 | } 12 | }; 13 | 14 | const actions = { 15 | someAsyncTask ({ commit }) { 16 | // do something async 17 | commit('INCREMENT_MAIN_COUNTER'); 18 | } 19 | }; 20 | 21 | export default { 22 | state, 23 | mutations, 24 | actions 25 | }; 26 | -------------------------------------------------------------------------------- /src/renderer/store/modules/EventBus.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | export default new Vue(); 3 | -------------------------------------------------------------------------------- /src/renderer/store/modules/Mailboxes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by ridaam on 26.11.17. 3 | */ 4 | 5 | const state = { 6 | list: [] 7 | }; 8 | 9 | if (process.env.NODE_ENV !== 'production') { 10 | state.list = [ 11 | { 12 | mailbox_from: 'test1', 13 | password_from: 'secret1', 14 | imap_from: 'test1.lamiral.info', 15 | mailbox_to: 'test2', 16 | password_to: 'secret2', 17 | imap_to: 'test2.lamiral.info' 18 | } 19 | ]; 20 | } 21 | 22 | const mutations = { 23 | resetList (state, list) { 24 | state.list = []; 25 | state.list = state.list.concat(list); 26 | }, 27 | addMailbox (state, mailbox) { 28 | state.list.push(mailbox); 29 | }, 30 | removeMailbox (state, index) { 31 | state.list.splice(index, 1); 32 | } 33 | }; 34 | 35 | const actions = { 36 | resetList ({ commit }, payload) { 37 | commit('resetList', payload); 38 | }, 39 | addMailbox ({ commit }, payload) { 40 | commit('addMailbox', payload); 41 | }, 42 | removeMailbox ({ commit }, payload) { 43 | commit('removeMailbox', payload); 44 | } 45 | }; 46 | 47 | export default { 48 | state, 49 | mutations, 50 | actions 51 | }; 52 | -------------------------------------------------------------------------------- /src/renderer/store/modules/ShellHistory.js: -------------------------------------------------------------------------------- 1 | // import app from '../../../../package.json'; 2 | import revision from '../../../../revision.json'; 3 | 4 | const WelcomeLine = { 5 | html: 'Welcome to imapsync ', 6 | color: null 7 | }; 8 | const VersionLine = { 9 | html: 'Version ' + revision.version, 10 | color: null 11 | }; 12 | 13 | const state = { 14 | output: [ 15 | WelcomeLine, 16 | VersionLine 17 | ] 18 | }; 19 | 20 | const mutations = { 21 | resetOutput (state, output) { 22 | state.output = []; 23 | state.output = state.output.concat(output); 24 | state.output.unshift(WelcomeLine, VersionLine); 25 | }, 26 | addLine (state, line) { 27 | state.output.push({ 28 | html: line, 29 | color: null 30 | }); 31 | }, 32 | addWarning (state, line) { 33 | state.output.push({ 34 | html: line, 35 | color: 'yellow' 36 | }); 37 | }, 38 | addError (state, line) { 39 | state.output.push({ 40 | html: line, 41 | color: 'red' 42 | }); 43 | } 44 | }; 45 | 46 | const actions = { 47 | resetOutput ({ commit }, payload) { 48 | commit('resetOutput', payload); 49 | }, 50 | addLine ({ commit }, payload) { 51 | commit('addLine', payload); 52 | }, 53 | addWarning ({ commit }, payload) { 54 | commit('addWarning', payload); 55 | }, 56 | addError ({ commit }, payload) { 57 | commit('addError', payload); 58 | } 59 | }; 60 | 61 | export default { 62 | state, 63 | mutations, 64 | actions 65 | }; 66 | -------------------------------------------------------------------------------- /src/renderer/store/modules/Version.js: -------------------------------------------------------------------------------- 1 | import app from '../../../../package.json'; 2 | 3 | const state = { 4 | version: app.version, 5 | latest: app.version 6 | }; 7 | 8 | const mutations = { 9 | setLatestVersion (state, version) { 10 | state.latest = version; 11 | } 12 | }; 13 | 14 | const actions = {}; 15 | 16 | export default { 17 | state, 18 | mutations, 19 | actions 20 | }; 21 | -------------------------------------------------------------------------------- /src/renderer/store/modules/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The file enables `@/store/index.js` to import all vuex modules 3 | * in a one-shot manner. There should not be any reason to edit this file. 4 | */ 5 | 6 | const files = require.context('.', false, /\.js$/); 7 | const modules = {}; 8 | 9 | files.keys().forEach(key => { 10 | if (key === './index.js') return; 11 | modules[key.replace(/(\.\/|\.js)/g, '')] = files(key).default; 12 | }); 13 | 14 | export default modules; 15 | -------------------------------------------------------------------------------- /static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ridaamirini/ImapSyncClient/23070ab730323ce1f3e1238dfbee5a07579a41c4/static/.gitkeep -------------------------------------------------------------------------------- /test.csv: -------------------------------------------------------------------------------- 1 | test1.lamiral.info;test1;secret1;test2.lamiral.info;test2;secret2 2 | --------------------------------------------------------------------------------