diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000..65db2f45 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,84 @@ + { + "env": { + "browser": true, + "es6": true, + "mozilla/jsm": true, + "commonjs": true + }, + + "extends": [ + "eslint:recommended", + "plugin:mozilla/recommended" + ], + + "overrides": [ + { + "files": "./src/chrome/content/*.js" + } + ], + "plugins": [ + "deprecate", + "mozilla" + ], + + "globals": { + "EXPORTED_SYMBOLS": "readonly", + "ChromeUtils": "readonly", + "XPCOMUtils": "readonly", + "sizeToContent": "readonly", + "Cc": "readonly", + "Cu": "readonly", + "Ci": "readonly", + "Cr": "readonly", + + "messenger": "readonly", + "MailServices": "readonly", + "gDBView": "readonly", + "OS": "readonly", + "window": "readonly", + "msgWindow": "readonly", + "gFolderDisplay": "readonly", + "BatchMessageMover": "readonly" + + }, + "rules": { + "no-irregular-whitespace": "error", + "space-in-parens": "error", + "no-unused-vars":"off", + "space-before-function-paren":"off", + "no-array-constructor": "warn", + "eqeqeq":"error", + + "mozilla/import-globals": "off", + "no-tabs":"off", + "no-useless-return":"off", + "object-shorthand":"off", + "padded-blocks":"off", + "mozilla/use-cc-etc": "error", + "mozilla/no-useless-parameters": "off", + "mozilla/use-services": "off", + "mozilla/use-includes-instead-of-indexOf": "warn", + "mozilla/avoid-removeChild": "warn", + "mozilla/use-chromeutils-generateqi": "off", + + "quotes":"off", + "spaced-comment": [ + 2, + "always" + ], + "semi": "error", + "no-restricted-properties": [ + 1, + { + "property": "nsIStringBundleService" + } + ], + "deprecate/function": [ + "error", + { + "name": "createBundle", + "use": "Replace with Services.createBundle" + } + ] + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..2d727fe9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +notes.txt +cmds.txt +.vscode/bookmarks.json \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..567de9dc --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,30 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "Build TB Extension -BAT", + "type": "shell", + "command": ".\\scripts\\build-xpi.bat", + "group": "build" + }, + { + "label": "Build TB Extension - XUL (package)", + "type": "npm", + "script": "build-xpi", + "problemMatcher": [], + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "label": "Build TB Extension - XUL (package) - viaBAT", + "type": "npm", + "script": "build-xpi-bat", + "problemMatcher": [], + "group": "build" + } + ] +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..d38c2e9d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,10 @@ +# Import/Export Tools Changelog + +## Versions + +4.0.0 - Thunderbird Team Update - ? + - Updated for TB68 + +3.3.2 - Update for TB60 - January 27, 2019 + - Last version update by "Kaosmos" + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..d2e3ad68 --- /dev/null +++ b/README.md @@ -0,0 +1,45 @@ +# ![IET icon] Import Export Tools + +Import Export Tools is a Thunderbird Add-On that adds import and export functions for both mbox messages and folders. + +The add-on is the original work of Paolo "Kaosmos". The add-on is being updated for Thunderbird 68 by the Thunderbird team so that users can continue to enjoy the functionality of this great add-on. + +![IET_version](https://img.shields.io/badge/version-v4.0.0-darkorange.png?label=ImportExportTools) +[![MAO_tb_version](https://img.shields.io/badge/version-v4.0.0-blue.png?label=Thunderbird%20Add-On)](https://addons.thunderbird.net/en-US/thunderbird/addon/) +![Thunderbird_version](https://img.shields.io/badge/version-v60.0--68.*-blue.png?label=Thunderbird) +[![License: MPL 2.0](https://img.shields.io/badge/License-GPL%203.0-red.png)](https://opensource.org/licenses/MPL-2.0) +![Release Status](https://img.shields.io/badge/Release%20Status-v4.0.0%20In%20Progress-brightgreen.png) +# + +## Import Export Tools Add-On Installation + + +Normal install (requires Internet access) from [Thunderbird Add-on site](https://addons.thunderbird.net/): +- Download and install [ATN version](https://addons.thunderbird.net/addon/import-export-tools2/) via the ``Add-ons Manager``. +- From the [Thunderbird Menu Bar](https://support.mozilla.org/en-US/kb/display-thunderbird-menus-and-toolbar), select ``Tools`` then ``Add-ons`` to open the ``Add-ons Manager``. Choose the ``Extensions`` tab, search for “TBD”, select ``+ Add to Thunderbird`` and follow the prompts to install and then restart. + +Install (with or without Internet access) XPI directly: +- Download and install [GitHub XPI version](xpi) via the ``Add-ons Manager``. +- From the [Thunderbird Menu Bar](https://support.mozilla.org/en-US/kb/display-thunderbird-menus-and-toolbar), select ``Tools`` then ``Add-ons`` to open the ``Add-ons Manager``. Choose the ``Extensions`` tab, click the gear icon and choose ``Install Add-on From File…`` +- Choose [XPI file](xpi), install and restart. + +## XPI Add-on Package Build instructions + +Visual Studio Code: + Build Default Task + +Basic Command Line Build: (requires 7zip CLI version) +7z a ./xpi/import-export-tools-4.0.0-tb.xpi ./src/* + +## Issues & Questions +Post any issues or questions for Import Export Tools under [Issues](https://github.com/thundernest/import-export-tools/issues) + +## Changelog + Import Export Tools' changes are logged [here](CHANGELOG.md). + +## Credits +Original Author: [Paolo "Kaosmos"](https://addons.thunderbird.net/en-US/thunderbird/user/Paolo_Kaosmos/) +Developing Author: [Jonathan Kamens](https://addons.thunderbird.net/en-US/thunderbird/user/jikamens/) +Developing Author: [Christopher Leidigh](https://github.com/cleidigh/) + +[IET icon]: rep-resources/images/import-export-tools-icon-32px.png \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 00000000..dc06407c --- /dev/null +++ b/package.json @@ -0,0 +1,26 @@ +{ + "title": "ImportExportTools", + "name": "import-export-tools", + "version": "4.0.0", + "description": "Import and export tools for messages and folders", + "author": "Paolo Kaosmos", + "engines": { + "": ">=38.0a1" + }, + "license": "GPLv3", + "config": { + "source_dir": "./src", + "target_dir": "./xpi", + "target_suffix": "-tb", + "target_extension": ".xpi", + "target_include_manifest": "true" + }, + "scripts": { + "build-xpi-bat": ".\\scripts\\build-xpi.bat", + "build-xpi": "node ./scripts/build-xpi.js" + }, + "keywords": [ + "jetpack" + ], + "dependencies": {} +} \ No newline at end of file diff --git a/rep-resources/images/import-export-tools-icon-32px.png b/rep-resources/images/import-export-tools-icon-32px.png new file mode 100644 index 00000000..8ccee50f Binary files /dev/null and b/rep-resources/images/import-export-tools-icon-32px.png differ diff --git a/scripts/build-xpi.bat b/scripts/build-xpi.bat new file mode 100644 index 00000000..10923332 --- /dev/null +++ b/scripts/build-xpi.bat @@ -0,0 +1,42 @@ + +if not defined npm_package_name ( + set targetBaseName=%1 +) else ( + set targetBaseName=%npm_package_name% +) + +if not defined npm_package_version ( + set targetVersion=%2 +) else ( + set targetVersion=%npm_package_version% +) + +echo %npm_package_name% %npm_package_version% + +rem get RDF version +FOR /F "tokens=* USEBACKQ" %%F IN (`node .\scripts\xml-util -get Description[\"em:version\"]`) DO ( +SET installRDFVer=%%F +) + +REM echo after %installRDFVer% + +if %installRDFVer% neq %targetVersion% ( + echo Version Mismatch: %installRDFVer% != %targetVersion% + exit 1 +) + +set sourcePath=.\src +set targetPath=.\xpi + +set targetName=%targetPath%\%targetBaseName%-%targetVersion%-tb.xpi + +del "%targetName%" + +call 7z a %targetName% %sourcePath%\* -x@%sourcePath%\.jpmignore + +rem call 7z d %targetName% ./src/manifest.json + +call 7z a %targetName% .\LICENSE +call 7z a %targetName% .\CHANGELOG.md + +call 7z l %targetName% \ No newline at end of file diff --git a/scripts/build-xpi.js b/scripts/build-xpi.js new file mode 100644 index 00000000..5d0c236c --- /dev/null +++ b/scripts/build-xpi.js @@ -0,0 +1,121 @@ +// cleidigh - build Thunderbird add-on - Use package configuration for XUL or hybrid with manifest +/* global process */ + +const util = require('util'); +const fs = require('fs'); +const _7z = require('7zip-min'); +const xml_util = require('./xml-util.js'); +const loadJsonFile = require('load-json-file'); + +// Configuration args + +let targetName = ''; + +const targetBaseName = process.env.npm_package_name; +const targetVersion = process.env.npm_package_version; +const targetSuffix = process.env.npm_package_config_target_suffix || ''; +const targetExtension = process.env.npm_package_config_target_extension || ''; +const includeManifest = (process.env.npm_package_config_target_include_manifest === 'true') ? true : false; + +const sourceDir = process.env.npm_package_config_source_dir; +const targetDir = process.env.npm_package_config_target_dir; + +// Validate configuration properties + +try { + if (typeof targetBaseName !== 'string') { + throw 'No targetBasedName'; + } + if (typeof targetVersion !== 'string') { + throw 'No targetVersion'; + } + if (typeof sourceDir !== 'string') { + throw 'No Source Directory'; + } + if (typeof targetDir !== 'string') { + throw 'No targetDir'; + } + + targetName = `${targetBaseName}-${targetVersion}${targetSuffix}${targetExtension}`; +} catch (error) { + console.error('Build Error: ' + error); + return 1; +} + +console.log('Building Target::\n'); +console.log('TargetName:\t\t' + targetName + ` [ manifest: ${includeManifest} ]`); + +// 7z adds to existing archive - must delete old first +if (fs.existsSync(`${targetDir}/${targetName}`)) { + console.log('Target Exists:\t\tRemoving old target'); + fs.unlinkSync(`${targetDir}/${targetName}`); +} + +const installRDFVersion = xml_util.rdfGetValue(`${sourceDir}/install.rdf`, 'Description[\"em:version\"]'); +const manifestVersion = loadJsonFile.sync(`${sourceDir}/manifest.json`).version; +const manifestName = loadJsonFile.sync(`${sourceDir}/manifest.json`)["xpi-name"]; +const ignoreFile = (includeManifest ? null : `-x!${sourceDir}/manifest.json`); + +// const extraFiles = ['LICENSE', 'CHANGELOG.md']; +const extraFiles = ['LICENSE']; + +console.log('\nVersioning:\n Target:\t\t' + targetVersion + '\n install.rdf:\t\t' + installRDFVersion + '\n manifest.json:\t' + manifestVersion); + +if (installRDFVersion !== targetVersion) { + console.log(`\nVersion Mismatch: [Error]\n install.rdf: ${installRDFVersion} != package.json: ${targetVersion}`); + return 1; +} + +if (includeManifest && manifestVersion !== targetVersion) { + console.log(`\nVersion Mismatch:\n manifest.json: ${manifestVersion} != package.json: ${targetVersion}`); + return 1; +} + +if (includeManifest && (manifestName.toLowerCase() + '-' + targetVersion + targetSuffix + targetExtension) !== targetName) { + console.log(`\nName Mismatch:\n manifest.json: ${manifestName} != package.json: ${targetName}`); + return 1; +} + + +let _7zCommand = ['a', `${targetDir}/${targetName}`, `${sourceDir}/*`, `-x@./src/.tb-hybrid-ignore`]; + +if (ignoreFile) { + _7zCommand.push(`${ignoreFile}`); +} + +function _7CmdSync(_7zCommand) { + return new Promise((resolve, reject) => { + + // console.error(_7zCommand); + _7z.cmd(_7zCommand, err => { + if (err) reject(err); + else resolve(); + }); + + }); +} + +// Create xpi archive using exclude file + +async function buildArchive() { + + console.log(`\nCreating XPI archive: ${targetName}\n`); + + try { + await _7CmdSync(_7zCommand); + + console.log(`Add Extra Files:`); + for (const file of extraFiles) { + _7zCommand = ['a', `${targetDir}/${targetName}`, `${file}`]; + await _7CmdSync(_7zCommand); + console.log(` ${file}`); + } + + console.log('\nArchive Complete: ' + targetName + ` [ manifest: ${includeManifest} ]`); + + } catch (error) { + console.error('Archive Error:\n ' + error); + } +} + +buildArchive(); diff --git a/scripts/dtd-test1.js b/scripts/dtd-test1.js new file mode 100644 index 00000000..0d7c7136 --- /dev/null +++ b/scripts/dtd-test1.js @@ -0,0 +1,285 @@ +const fs = require('fs'); + +const localeDir = "./src/chrome/locale"; +const referenceLocaleId = "en-US"; + +loadLocales = function (localeDir, referenceLocale, localeIds) { + + // GetLocaleFiles + const localeFolders = _getAllFilesOrFolders(localeDir, true); + + if (localeFolders.indexOf(referenceLocale) === -1) { + return []; + } + + let locales = []; + + localeFolders.forEach((localeFolder, index) => { + locales.push({ localeId: localeFolder, localePath: `${localeDir}/${localeFolder}`, referenceLocale: ((localeFolder === referenceLocale) ? true : false) }); + let localeFiles = _getAllFilesOrFolders(`${localeDir}/${localeFolder}`, false); + + locales[index].localeFiles = localeFiles; + locales[index].missingFiles = []; + locales[index].missingEntities = []; + locales[index].extraEntities = []; + + }); + + // console.log(locales); + + // console.log(locales[0]); + return locales; +}; + + +var _getAllFilesOrFolders = function (dir, foldersOnly) { + + var filesystem = require("fs"); + var files = []; + var folders = []; + + filesystem.readdirSync(dir).forEach(function (fileObj) { + + file = dir + '/' + fileObj; + var stat = filesystem.statSync(file); + + if (stat && stat.isDirectory()) { + // results = results.concat(_getAllFilesFromFolder(file)); + folders.push(fileObj); + } else files.push(fileObj); + }); + + if (foldersOnly) { + return folders; + } else { + return files; + } + return results; + +}; + +compareLocaleEntities = function (locales, referenceLocaleId, localeSet) { + + // const refIndex = localeFolders.indexOf(referenceLocale); + + const refIndex = 1; + if (refIndex === -1) { + return 1; + } + + locales.forEach((locale, index) => { + if (index === refIndex) { + // Skip reference locale + // const results = compareDTDFiles(`${locales[refIndex].localePath}/${file}`, null); + // locale.entities = results.entities; + return; + } + + + locales[refIndex].localeFiles.forEach(file => { + if (locale.localeFiles.indexOf(file) === -1) { + // console.log(`File Missing (${locale.localeId}) ${file}`); + locale.missingFiles.push(file); + return; + } else { + if (file.substr(-4).toLowerCase() === '.dtd') { + console.log(`Processing DTD: (${locale.localeId}) ${file}`); + const results = compareDTDFiles(`${locales[refIndex].localePath}/${file}`, `${locale.localePath}/${file}`); + locale.missingEntities = locale.missingEntities.concat(results.missingEntities).sort(); + locale.extraEntities = locale.extraEntities.concat(results.extraEntities).sort(); + } else if (file.substr(-11).toLowerCase() === '.properties') { + console.log(`Processing Properties: (${locale.localeId}) ${file}`); + const results = comparePropertyFiles(`${locales[refIndex].localePath}/${file}`, `${locale.localePath}/${file}`); + locale.missingEntities = locale.missingEntities.concat(results.missingEntities).sort(); + locale.extraEntities = locale.extraEntities.concat(results.extraEntities).sort(); + } + } + }); + + }); + + +}; + +compareDTDFiles = function (referenceFile, comparisonFile) { + var parser = require("dtd-file"); + + // console.log(referenceFile+' '+comparisonFile); + let refEntities = Object.keys(parser.parse(fs.readFileSync(referenceFile, 'utf-8'))); + let compEntities = Object.keys(parser.parse(fs.readFileSync(comparisonFile, 'utf-8'))); + + let missingEntities = []; + + refEntities.forEach(entityKey => { + // console.log('Key: '+entityKey); + const i = compEntities.indexOf(entityKey); + // console.log(i); + if (i !== -1) { + compEntities.splice(i, 1); + } else { + missingEntities.push(`${comparisonFile}$${entityKey}`); + console.log(`Missing element: ${entityKey}`); + } + }); + + return { missingEntities: missingEntities, extraEntities: compEntities }; +}; + + + +comparePropertyFiles = function (referenceFile, comparisonFile) { + + console.log(referenceFile + ' ' + comparisonFile); + let refProperties = fs.readFileSync(referenceFile, 'utf-8').split('\n').map(l => l.split('=')[0]); + let compProperties = fs.readFileSync(comparisonFile, 'utf-8').split('\n').map(l => l.split('=')[0]);; + + // console.log(compProperties); + + let missingEntities = []; + + refProperties.forEach(property => { + // const property = propertyLine.split('=')[0]; + + // console.log('Property: '+ property); + + const i = compProperties.indexOf(property); + // console.log(i); + if (i !== -1) { + compProperties.splice(i, 1); + } else { + missingEntities.push(`${comparisonFile}$${property}`); + console.log(`Missing element: ${property}`); + } + }); + + return { missingEntities: missingEntities, extraEntities: compProperties }; +}; + +compareDTDEntities = function (referenceSet, comparisonSet) { + + let extraEntities = []; + let missingEntities = []; + + referenceSet.forEach(entityKey => { + // console.log('Key: '+entityKey); + const i = comparisonSet.indexOf(entityKey); + console.log(i); + if (i !== -1) { + comparisonSet.splice(i, 1); + } else { + missingEntities.push(entityKey); + console.log(`Missing element: ${entityKey}`); + } + }); + + // console.log(comparisonSet); + // console.log(missingEntities); + return { missingEntities: missingEntities, extraEntities: comparisonSet }; +}; + +// Reading data in utf-8 format +// which is a type of character set. +// Instead of 'utf-8' it can be +// other character set also like 'ascii' +// fs.readFileSync('./src/chrome/locale/en-US/mzcw-settings-customcolseditor.dtd', 'utf-8', (err, data) => { +let data = fs.readFileSync('./src/chrome/locale/en-US/mzcw-settings-customcolseditor.dtd', 'utf-8'); +let data2 = fs.readFileSync('./src/chrome/locale/nl/mzcw-settings-customcolseditor.dtd', 'utf-8'); + +// Converting Raw Buffer to text +// data using tostring function. +// console.log(data); + +// var res = parser.parse(data); +// var res2 = parser.parse(data2); + +// // console.log(res); +// let r1 = Object.keys(res); +// let r2 = Object.keys(res2); + +// const results = compareDTDEntities(r1, r2); +// console.log(results.missingEntities); +// console.log(results.extraEntities); + +let locales = loadLocales(localeDir, referenceLocaleId, null); +if (locales.length === 0) { + return 1; +} + +// Compare files + +const localeSet = null; +// let result = compareDTDFiles(`${locales[1].localePath}/${locales[1].localeFiles[0]}`, `${locales[4].localePath}/${locales[4].localeFiles[0]}`); +let result = compareLocaleEntities(locales); +console.log(locales); + +localeReport = function (referenceLocaleId, compLocaleId, details) { + + let compLocaleIndex = -1; + + console.log('Reference Locale:' + referenceLocaleId); + + locales.forEach((locale, index) => { + if (locale.localeId === compLocaleId) { + compLocaleIndex = index; + return; + } + }); + + const compLocale = locales[compLocaleIndex]; + console.log('Locale: ' + compLocaleId + '\n'); + + if (compLocale.missingFiles.length) { + console.log(' Missing Files:'); + compLocale.missingFiles.forEach(file => { + console.log(' ' + file); + }); + + } + + if (compLocale.missingEntities.length) { + + let file = ''; + let missingEntitySet = new Object(); + + console.log(' Missing Entities:'); + let refEntity = 'English'; + + compLocale.missingEntities.forEach(entityLine => { + entityLineElements = entityLine.split('$'); + + if (locales[referenceLocaleId]) { + + } + + missingEntitySet[`${entityLineElements[1]}`] = refEntity; + + if (file !== entityLineElements[0]) { + file = entityLineElements[0]; + console.log(`\n File: ${entityLineElements[0]}`); + if (file !== '') { + writeMissingEntitiesFile(`${file}.missing`, missingEntitySet); + missingEntitySet = {}; + } + } + console.log(` ${entityLineElements[1]}`); + }); + } +}; + +writeMissingEntitiesFile = function (file, entities) { + var parser = require("dtd-file"); + let fileData = ''; + + fileData = parser.stringify(entities); + console.log('\n' + fileData); + + fs.writeFile(file, fileData, function (err) { + if (err) { + return console.log(err); + } + + console.log("The file was saved!"); + }) +}; + +localeReport(referenceLocaleId, 'zh-CN'); diff --git a/scripts/translate1.js b/scripts/translate1.js new file mode 100644 index 00000000..bae72b45 --- /dev/null +++ b/scripts/translate1.js @@ -0,0 +1,14 @@ +const translate = require('@vitalets/google-translate-api'); + +// translate('Mail Header Editor', {from: 'en', to: 'zh-CH'}).then(res => { + translate('email header editor.', {from: 'en', to: 'de'}).then(res => { + console.log(res.text); + console.log(res); + //=> I speak English + console.log(res.from.language.iso); + //=> nl +}).catch(err => { + console.error(err); +}); + +// \ No newline at end of file diff --git a/scripts/xml-util.js b/scripts/xml-util.js new file mode 100644 index 00000000..7ddb2119 --- /dev/null +++ b/scripts/xml-util.js @@ -0,0 +1,14 @@ +// cleidigh - Utility to get install.rdf values + +exports.rdfGetValue = function(file, valuePath) { + + var convert = require('xml-js'); + var xml = require('fs').readFileSync(file, 'utf8'); + var options = { ignoreComment: true, alwaysChildren: true, compact: true }; + var result = convert.xml2js(xml, options); + + var rdfXMLPath = 'result.RDF.' + valuePath + '._text'; + // console.log(eval(rdfXMLPath)); + + return eval(rdfXMLPath); +} diff --git a/src/chrome.manifest b/src/chrome.manifest new file mode 100644 index 00000000..8acf5211 --- /dev/null +++ b/src/chrome.manifest @@ -0,0 +1,31 @@ +content mboximport chrome/content/mboximport/ + +overlay chrome://messenger/content/mailWindowOverlay.xul chrome://mboximport/content/mboximport.xul +overlay chrome://messenger/content/messenger.xul chrome://mboximport/content/messengerOverlay.xul +overlay chrome://messenger/content/SearchDialog.xul chrome://mboximport/content/sdOverlay.xul +overlay chrome://messenger/content/msgPrintEngine.xul chrome://mboximport/content/printEngineWindowOverlay.xul + + +locale mboximport en-US chrome/locale/en-US/mboximport/ +locale mboximport ca chrome/locale/ca/mboximport/ +locale mboximport da chrome/locale/da/mboximport/ +locale mboximport de chrome/locale/de/mboximport/ +locale mboximport es-ES chrome/locale/es-ES/mboximport/ +locale mboximport fr chrome/locale/fr/mboximport/ +locale mboximport gl-ES chrome/locale/gl-ES/mboximport/ +locale mboximport hu-HU chrome/locale/hu-HU/mboximport/ +locale mboximport hy-AM chrome/locale/hu-HU/mboximport/ +locale mboximport it chrome/locale/it/mboximport/ +locale mboximport ja chrome/locale/ja/mboximport/ +locale mboximport ko-KR chrome/locale/ko-KR/mboximport/ +locale mboximport nl chrome/locale/nl/mboximport/ +locale mboximport pl chrome/locale/pl/mboximport/ +locale mboximport pt-PT chrome/locale/pt-PT/mboximport/ +locale mboximport ru chrome/locale/ru/mboximport/ +locale mboximport sk-SK chrome/locale/sk-SK/mboximport/ +locale mboximport sl-SI chrome/locale/sl-SI/mboximport/ +locale mboximport sv-SE chrome/locale/sv-SE/mboximport/ +locale mboximport zh-CN chrome/locale/zh-CN/mboximport/ + + + diff --git a/src/chrome/content/mboximport/autobackup.js b/src/chrome/content/mboximport/autobackup.js new file mode 100644 index 00000000..d76e5593 --- /dev/null +++ b/src/chrome/content/mboximport/autobackup.js @@ -0,0 +1,266 @@ +var gBackupPrefBranch = Components.classes["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefBranch); + +var autoBackup = { + + onOK : function() { + setTimeout(autoBackup.start, 500); + document.getElementById("start").removeAttribute("collapsed"); + document.getElementById("go").collapsed = true; + document.documentElement.getButton("accept").disabled = true; + autoBackup.time = window.arguments[1]; + autoBackup.now = window.arguments[2]; + // saveMode values: + // 0 = save all; 1 = save just if new; + // 2 = save just if new with custom name, save all with unique name + autoBackup.saveMode = gBackupPrefBranch.getIntPref("extensions.importexporttools.autobackup.save_mode"); + autoBackup.type = gBackupPrefBranch.getIntPref("extensions.importexporttools.autobackup.type"); + return false; + }, + + load : function() { + var os = navigator.platform.toLowerCase(); + if (os.indexOf("mac") > -1) + document.getElementById("macWarn").removeAttribute("collapsed"); + var label = document.getElementById("last").textContent; + autoBackup.last = window.arguments[0]; + if (autoBackup.last > 0) { + var last = autoBackup.last * 1000; + var time = new Date(last); + var localTime = time.toLocaleString(); + document.getElementById("last").textContent = label.replace("$t", localTime); + } + else + document.getElementById("last").textContent = label.replace("$t", ""); + }, + + getDir : function() { + try { + var dir = gBackupPrefBranch.getCharPref("extensions.importexporttools.autobackup.dir"); + var file = Components.classes["@mozilla.org/file/local;1"] + .createInstance(Components.interfaces.nsIFile); + file.initWithPath(dir); + if (! file.exists() || ! file.isDirectory()) + file = null; + } + catch(e) { + var file = null; + } + if (! file) { + file = IETgetPickerModeFolder(); + autoBackup.filePicker = true; + } + return file; + }, + + writeLog : function(data,append) { + var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"] + .createInstance(Components.interfaces.nsIFileOutputStream); + if (append) + foStream.init(autoBackup.logFile, 0x02 | 0x08 | 0x10, 0664, 0); + else + foStream.init(autoBackup.logFile, 0x02 | 0x08 | 0x20, 0666, 0); + foStream.write(data,data.length); + foStream.close(); + }, + + start : function() { + // "dir" is the target directory for the backup + var dir = autoBackup.getDir(); + if (! dir) + return; + var strbundle = document.getElementById("backupStr"); + if (! dir.exists() || ! dir.isWritable) { + alert(strbundle.getString("noBackup")); + window.close(); + return; + } + var nameType = gBackupPrefBranch.getIntPref("extensions.importexporttools.autobackup.dir_name_type"); + if (nameType == 1) { + try { + var dirName = gBackupPrefBranch.getCharPref("extensions.importexporttools.autobackup.dir_custom_name"); + } + catch(e) { + var dirName = null; + } + } + else + var dirName = null; + + autoBackup.IETmaxRunTime = gBackupPrefBranch.getIntPref("dom.max_chrome_script_run_time"); + IETrunTimeDisable(); + try { + var offlineManager = Components.classes["@mozilla.org/messenger/offline-manager;1"] + .getService(Components.interfaces.nsIMsgOfflineManager); + offlineManager.synchronizeForOffline(false, false, false, true, msgWindow); + } + catch(e) {} + + var clone = dir.clone(); + autoBackup.profDir = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("ProfD", Components.interfaces.nsIFile); + + if (dirName && ! autoBackup.filePicker) { + clone.append(dirName); + if (! clone.exists()) + clone.create(1,0755); + } + else { + var date = buildContainerDirName(); + clone.append(autoBackup.profDir.leafName+"-"+date); + clone.createUnique(1,0755); + autoBackup.unique = true; + } + + // Here "clone" is the container directory for the backup + + var str = "Backup date: "+autoBackup.now.toLocaleString()+"\r\n\r\n"+"Saved files:\r\n"; + autoBackup.logFile = clone.clone(); + autoBackup.logFile.append("Backup.log"); + autoBackup.writeLog(str,false); + + var oldLogFile = clone.clone(); + oldLogFile.append("BackupTime.txt"); + if (oldLogFile.exists()) + oldLogFile.remove(false); + + autoBackup.array1 = []; + autoBackup.array2 = []; + + autoBackup.scanExternal(clone); + + if (autoBackup.type == 1) { // just mail + var profDirMail = autoBackup.profDir.clone(); + profDirMail.append("Mail"); + autoBackup.scanDir(profDirMail,clone,autoBackup.profDir); + profDirMail = autoBackup.profDir.clone(); + profDirMail.append("ImapMail"); + if (profDirMail.exists()) + autoBackup.scanDir(profDirMail,clone,autoBackup.profDir); + } + else + autoBackup.scanDir(autoBackup.profDir, clone, autoBackup.profDir); + autoBackup.write(0); + }, + + end : function(sec) { + if (sec == 0) + window.close(); + else + window.setTimeout(autoBackup.end,1000,sec-1); + }, + + save : function(entry,destDir,root) { + if ((autoBackup.unique && autoBackup.saveMode !=1) || autoBackup.saveMode ==0) + var force = true; + else + var force = false; + var lmt = entry.lastModifiedTime / 1000; + // Check if exists a older file to replace in the backup directory + if (force || lmt > autoBackup.last) { + var entrypath = entry.parent.path; + var filepath = destDir.path; + var newpath = entrypath.replace(root.path,filepath); + var LF = Components.classes["@mozilla.org/file/local;1"] + .createInstance(Components.interfaces.nsIFile); + LF.initWithPath(newpath); + var LFclone = LF.clone(); + LFclone.append(entry.leafName); + if (LFclone.exists()) + LFclone.remove(false); + try { + autoBackup.array1.push(entry); + autoBackup.array2.push(LF); + } + catch(e) {} + } + }, + + // dirToScan is the directory to scan + // destDir is the target directory for the backup + // root is the root directory of the files to save --> it's the profile directory or the external directory of the account + scanDir : function(dirToScan,destDir,root) { + if (! dirToScan.exists()) + return; + var entries = dirToScan.directoryEntries; + while(entries.hasMoreElements()) { + var entry = entries.getNext(); + entry.QueryInterface(Components.interfaces.nsIFile); + if (entry.exists()) { + if (entry.leafName != "lock" && entry.leafName != "parent.lock" && entry.leafName != ".parentlock") { + if (entry.isDirectory()) + autoBackup.scanDir(entry,destDir,root); + else + autoBackup.save(entry,destDir,root); + } + } + else { + var error = "\r\n***Error - non-existent file: "+entry.path+"\r\n"; + autoBackup.writeLog(error,true); + } + } + }, + + write : function(index) { + try { + autoBackup.array1[index].copyTo(autoBackup.array2[index], ""); + var logline = autoBackup.array1[index].path + "\r\n"; + autoBackup.writeLog(logline,true); + } + catch(e) { + if (autoBackup.array1[index]) + var error = "\r\n***Error with file "+ autoBackup.array1[index].path + "\r\nError Type: "+e+"\r\n\r\n"; + else + var error = "\r\n***Error Type: "+e+"\r\n\r\n"; + autoBackup.writeLog(error,true); + } + index++; + if (autoBackup.array1.length > index) { + var c = index / autoBackup.array1.length * 100; + document.getElementById("pm").value = parseInt(c); + window.setTimeout(autoBackup.write,50,index); + } + else { + document.getElementById("pm").value = 100; + gBackupPrefBranch.setIntPref("extensions.importexporttools.autobackup.last", autoBackup.time); + IETrunTimeEnable(autoBackup.IETmaxRunTime); + document.getElementById("start").collapsed = true; + document.getElementById("done").removeAttribute("collapsed"); + autoBackup.end(2); + } + }, + + scanExternal : function(destDir) { + var file = destDir.clone(); + file.append("ExternalMailFolders"); + if (! file.exists()) + file.create(1,0775); + var servers = Components.classes["@mozilla.org/messenger/account-manager;1"] + .getService(Components.interfaces.nsIMsgAccountManager).allServers; + if (servers.Count) + var cntServers = servers.Count(); + else + // Thunderbird >17 return nsIArray + var cntServers = servers.length; + // Scan servers storage path on disk + for (var i = 0; i < cntServers; ++i) { + var parentDir = null; + if (servers.Count) + var serverFile = servers.GetElementAt(i).QueryInterface(Components.interfaces.nsIMsgIncomingServer).localPath; + else + var serverFile = servers.queryElementAt(i, Components.interfaces.nsIMsgIncomingServer).localPath; + if (serverFile.parent && serverFile.parent.parent) + parentDir = serverFile.parent.parent; + var clone = file.clone(); + clone.append(serverFile.leafName); + // Now "clone" path is --> /ExternalMailFolder/ + if (! parentDir || ! autoBackup.profDir.equals(parentDir)) + autoBackup.scanDir(serverFile,clone,serverFile); + } + } +}; + + + + diff --git a/src/chrome/content/mboximport/autobackup.xul b/src/chrome/content/mboximport/autobackup.xul new file mode 100644 index 00000000..e7921bf1 --- /dev/null +++ b/src/chrome/content/mboximport/autobackup.xul @@ -0,0 +1,28 @@ + + + + + + + diff --git a/src/chrome/content/mboximport/mboximport.js b/src/chrome/content/mboximport/mboximport.js new file mode 100644 index 00000000..819393fc --- /dev/null +++ b/src/chrome/content/mboximport/mboximport.js @@ -0,0 +1,1367 @@ +// Code rewritten from 0.5.3 version, to avoid the call to initwithpath (TB 1.5 only) +var MBstrBundleService = Components.classes["@mozilla.org/intl/stringbundle;1"] + .getService(Components.interfaces.nsIStringBundleService); +var mboximportbundle = MBstrBundleService.createBundle("chrome://mboximport/locale/mboximport.properties"); +var mboximportbundle2 = MBstrBundleService.createBundle("chrome://messenger/locale/mime.properties"); +var gEMLimported; +var gEMLtotal; +var gFileEMLarray; +var gFileEMLarrayIndex; +var IETtempfilesize; +var IETcount; +var gNeedCompact; +var gMsgFolderImported; + +var IETprintPDFmain = { + + print : function(allMessages) { + if (navigator.platform.toLowerCase().indexOf("mac") > -1 || navigator.userAgent.indexOf("Postbox") > -1 ) { + alert(mboximportbundle.GetStringFromName("noPDFmac")); + return; + } + try { + var printSvc = Components.classes["@mozilla.org/gfx/printsettings-service;1"].getService(Components.interfaces.nsIPrintSettingsService); + if (printSvc.defaultPrinterName == "") { + alert(mboximportbundle.GetStringFromName("noPDFnoPrinter")); + return; + } + } + catch(e) {} + + var msgFolders = GetSelectedMsgFolders(); + if (msgFolders.length > 1) { + alert(mboximportbundle.GetStringFromName("noPDFmultipleFolders")); + return; + } + var question = IETformatWarning(1); + if (! question) + return; + question = IETformatWarning(0); + if (! question) + return; + if (! allMessages) + IETprintPDFmain.uris = IETgetSelectedMessages(); + else { + IETprintPDFmain.uris = []; + msgFolder = msgFolders[0]; + var isVirtFol = msgFolder ? msgFolder.flags & 0x0020 : false; + if (isVirtFol) { + var total = msgFolder.getTotalMessages(false); + for (var i=0;i500) { + alert("Can't find a good name"); + return false; + } + clonex = clonex.parent; + clonex.append(newfilename); + } + + // 1. add a subfolder with the name of the folder to import + var newFolder = msgFolder.addSubfolder(newfilename); + if (restoreChar) { + var reg = new RegExp(safeChar,"g"); + newFolder.name = newfilename.replace(reg, "#"); + } + + // 2. find the MAILDIR directory created above + var filex = msgFolder2LocalFile(newFolder); + try { + var destFileClone = destFile.clone(); + destFileClone.append("cur"); + if (! destFileClone.exists() || ! destFileClone.isDirectory()) { + alert(mboximportbundle.GetStringFromName("isNotMaildir")); + return; + } + destFileClone = destFileClone.parent; + destFileClone.append("tmp"); + if (! destFileClone.exists() || ! destFileClone.isDirectory()) { + alert(mboximportbundle.GetStringFromName("isNotMaildir")); + return; + } + var allfiles = destFile.directoryEntries; + // copy all the files inside the MAILDIR directory to import in MAILDIR directory created above + while(allfiles.hasMoreElements()) { + var singlefile= allfiles.getNext(); + singlefile = singlefile.QueryInterface(Components.interfaces.nsIFile); + singlefile.copyTo(filex,null); + } + } + catch(e) { + return false; + } + + // 3. update the database by selecting the folder and rebuilding the index + try { + msgFolder.NotifyItemAdded(newFolder); + SelectFolder(newFolder.URI); + IETupdateFolder(newFolder); + } + catch(e) {} +} + +// The arguments of trytocopy are +// file = the file to import as nsIFile +// filename = the name of the file to import +// msgFolder = the folder as nsImsgFolder + +function trytocopy(file,filename,msgFolder,keepstructure) { + // If the file isn't mbox format, alert, but doesn't exit (it did in pre 0.5.8 version and lower) + // In fact sometimes TB can import also corrupted mbox files + var isMbx = isMbox(file); + if (isMbx != 1) { + if (isMbx == 0) { + var continuebundle = MBstrBundleService.createBundle("chrome://messenger/locale/filter.properties"); + // We take the "Continue" label from another file... + var continuelabel = continuebundle.GetStringFromName("continueButtonLabel"); + var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService); + var flags = prompts. BUTTON_TITLE_CANCEL * prompts.BUTTON_POS_0 + + prompts.BUTTON_TITLE_IS_STRING *prompts.BUTTON_POS_1 + prompts.BUTTON_POS_0_DEFAULT; + var string = ("\"" + filename + "\" " + mboximportbundle.GetStringFromName("nomboxfile")); + var button = prompts.confirmEx(window, "ImportExportTools", string , flags, "Button 0", continuelabel, "", null, {}); + if (button==0) + return false; + } + else { + if (! confirm(mboximportbundle.GetStringFromName("isNotStandard"))) + return false; + } + } + + var filex = msgFolder2LocalFile(msgFolder); + var clonex = filex.clone(); + var newfilename = filename; + var restoreChar = false; + if (newfilename.match(/#/)) { + var safeChar = Math.floor(Math.random()*99999).toString(); + newfilename = newfilename.replace(/#/g, safeChar); + restoreChar = true; + } + clonex.append(newfilename); + + // add to the original filename a random number in range 0-999 + if (IETprefs.getBoolPref("extensions.importexporttools.import.name_add_number")) + newfilename = newfilename+Math.floor(Math.random()*999); + var k = 0; + // if exists a subfolder with this name, we change the random number, with max. 500 tests + // while (msgFolder.containsChildNamed(newfilename)) { + while (msgFolder.containsChildNamed(newfilename) || clonex.exists()) { + newfilename = filename+Math.floor(Math.random()*999); + k++; + if (k >500) { + alert("Can't find a good name"); + return false; + } + clonex = clonex.parent; + clonex.append(newfilename); + } + // This is a little strange code, but it can find the destintation folder as nsIFile + // without calling nsIFile.initwithPath. This is done creating a new subfolder, + // finding the parent of this temp new subfolder and deleting the subfolder itself. + // The 0.5.3 version did this scanning all the files into the directory, to find the directory + // called "msgfoldername.sbd". But this doesn't work, because there is a case when + // this directory can miss: when you've deleted before all the subfolders from the GUI, + // without restarting. + // This is a dirty hack, I hope to find in the future something better... + // + // 1. add a subfolder with the name of the folder to import + var tempfolder = msgFolder.addSubfolder(newfilename); + if (restoreChar) { + var reg = new RegExp(safeChar,"g"); + tempfolder.name = newfilename.replace(reg, "#"); + } + // 2. find the nsIFile of the directory where the file will be copied + if (! msgFolder.isServer) { + var tempfolderNS = msgFolder2LocalFile(tempfolder); + filex = tempfolderNS.parent; + } + // 3. delete the new subfolder, to delete all the files inside "msgfoldername.sbd" directory + tempfolder.Delete(); + if (! filex) { + alert(mboximportbundle.GetStringFromName("internalerror")); + return false; + } + try { + // Finally copy the mbox file in the "msgfoldername.sbd" directory + file.copyTo(filex,newfilename); + // If this is an export with structure, we try also to export the directory mbox-filename.sbd + if (keepstructure) { + var sbd = file.parent; + sbd.append(file.leafName+".sbd"); + if (sbd.exists()) + sbd.copyTo(filex,newfilename+".sbd"); + } + } + catch(e) { + return false; + } + // inizialize as nsIFile the folder imported in TB and check if it's writable and readable. + // if not (for ex. a file imported from a cdrom), change the permissions + filex.append(newfilename) + if (! filex.isReadable() || ! filex.isWritable() ) + filex.permissions = 420; + // the following code of this subfunction has been written with the help of Frank Ausdilecce + // really thanks for his help + var newFolder = tempfolder; + // this notifies listeners that a folder has been added; + // the code is different for TB-1.0 and TB > 1.0 because the syntax of + // NotifyItemAdded seems to have been modified + try { + msgFolder.NotifyItemAdded(msgFolder,newFolder,"Folder Added"); // This is for TB1.0 + } + catch(e) {} + try { + msgFolder.NotifyItemAdded(newFolder); // This is for TB > 1.0 + } + catch(e) {} + + var forceCompact = addEmptyMessageToForceCompact(newFolder); + if (forceCompact && ! gNeedCompact) + gNeedCompact = true; + var obj = new Object; + obj.msgFolder = newFolder; + obj.forceCompact = forceCompact; + + if (keepstructure) { + gMsgFolderImported.push(obj); + if (newFolder.hasSubFolders) + setTimeout(storeImportedSubFolders,1000,newFolder); + } + else + gMsgFolderImported.push(obj); + + return newfilename; +} + +function storeImportedSubFolders(msgFolder) { + if (msgFolder.GetSubFolders) { + var subfolders = msgFolder.GetSubFolders(); + while(true) { + var next = subfolders.currentItem(); + var subfolder = next.QueryInterface(Components.interfaces.nsIMsgFolder); + var obj = new Object; + obj.msgFolder = subfolder; + obj.forceCompact = false; + gMsgFolderImported.push(obj); + // If the subfolder has subfodlers, the function calls itself + if (subfolder.hasSubFolders) + storeImportedSubFolders(subfolder); + try { subfolders.next(); } + catch(ex) { break; } + } + } + // Gecko 1.9 + else { + var subfolders = msgFolder.subFolders; + while(subfolders.hasMoreElements()) { + var next = subfolders.getNext(); + var subfolder = next.QueryInterface(Components.interfaces.nsIMsgFolder); + var obj = new Object; + obj.msgFolder = subfolder; + obj.forceCompact = false; + gMsgFolderImported.push(obj); + // If the subfolder has subfodlers, the function calls itself + if (subfolder.hasSubFolders) + storeImportedSubFolders(subfolder); + } + } +} + +function addEmptyMessageToForceCompact(msgFolder) { + var file = msgFolder2LocalFile(msgFolder); + var istream = Components.classes["@mozilla.org/network/file-input-stream;1"] + .createInstance(Components.interfaces.nsIFileInputStream); + istream.init(file, 0x01, 0444, 0); + istream.QueryInterface(Components.interfaces.nsILineInputStream); + var line = {}; + var first3lines = ""; + for (var i=0;i<4;i++) { + istream.readLine(line); + first3lines = first3lines + line.value + "\n"; + } + istream.close(); + if (first3lines.indexOf("X-Mozilla-Status") > -1) { + return false; + } + + // Probably this is not a Thunderbird/Mozilla mbox file, because is missing of X-Mozilla-Status fields + // The only way to write X-Mozilla-Status in all messages is to force compacting after import + // Thunderbird will compact just there are bites to expunge, so we add at the end of the mbox file + // a fake deleted message + + var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"]. + createInstance(Components.interfaces.nsIFileOutputStream); + var data = "\n\nFrom Moon\nX-Mozilla-Status: 0009\nX-Mozilla-Status2: 00800000\nDate: Fri, 08 Feb 2008 10:30:48 +0100\nFrom: nomail@nomail.no\nMIME-Version: 1.0\nTo: nomail@nomail.no\nSubject: empty\nContent-Type: text/plain\n\n\n\n"; + foStream.init(file, 0x02 | 0x08 | 0x10, 0666, 0); + foStream.write(data,data.length); + foStream.close(); + return true; +} + +// these lines *should* create the msf file +function buildMSGfile(scan) { + for (var i=0;i< gMsgFolderImported.length;i++) { + try { + var folder = gMsgFolderImported[i].msgFolder; + IETupdateFolder(folder); + } + catch(e) {} + setTimeout(updateImportedFolder, 2000, folder, gMsgFolderImported[i].forceCompact); + } + gMsgFolderImported = []; + if (scan) + IETwritestatus(mboximportbundle.GetStringFromName("endscan")); +} + +function updateImportedFolder(msgFolder,forceCompact) { + try { + msgFolder.updateSummaryTotals(true); + } + catch(e) {} + try { + msgFolder.summaryChanged(); + } + catch(e) {} + if (forceCompact) + msgFolder.compact(null,msgWindow); +} + +// scandir flag is to know if the function must scan a directory or just import mbox file(s) +function importmbox(scandir,keepstructure,openProfDir, recursiveMode,msgFolder) { + // initialize variables + gMsgFolderImported = []; + gNeedCompact = false; + var buildMSF = IETprefs.getBoolPref("extensions.importexporttools.import.build_mbox_index"); + var nsIFilePicker = Components.interfaces.nsIFilePicker; + var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker); + if (! scandir) { + // open the filepicker + fp.init(window, mboximportbundle.GetStringFromName("filePickerImport"), nsIFilePicker.modeOpenMultiple); + fp.appendFilters(nsIFilePicker.filterAll); + + if (openProfDir) { + var profDir = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("ProfD", Components.interfaces.nsIFile); + fp.displayDirectory = profDir.parent; + } + + if (fp.show) + var res = fp.show(); + else + var res = IETopenFPsync(fp); + if (res==nsIFilePicker.returnOK){ + // thefiles is the nsiSimpleEnumerator with the files selected from the filepicker + var thefiles=fp.files; + while(thefiles.hasMoreElements()) { + var onefile= thefiles.getNext(); + onefile = onefile.QueryInterface(Components.interfaces.nsIFile); + var mboxname=onefile.leafName; + trytocopy(onefile,mboxname,msgFolder,keepstructure); + } + if (buildMSF || gNeedCompact) { + var timout = keepstructure ? 2000 : 1000; + setTimeout(buildMSGfile, timout, false); + } + } + else + return; + } + else { + // Open the filepicker to choose the directory + fp.init(window, mboximportbundle.GetStringFromName("searchdir"), nsIFilePicker.modeGetFolder); + + if (openProfDir) { + var profDir = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("ProfD", Components.interfaces.nsIFile); + fp.displayDirectory = profDir.parent; + } + + if (fp.show) + var res = fp.show(); + else + var res = IETopenFPsync(fp); + if (res==nsIFilePicker.returnOK){ + if (! recursiveMode) { + // allfiles is the nsiSimpleEnumerator with the files in the directory selected from the filepicker + var allfiles = fp.file.directoryEntries; + var filesArray = new Array; + while(allfiles.hasMoreElements()) { + var singlefile= allfiles.getNext(); + singlefile = singlefile.QueryInterface(Components.interfaces.nsIFile); + filesArray.push(singlefile); + } + } + else + var filesArray = MBOXIMPORTscandir.find(fp.file); + + // scanning the directory to search files that could be mbox files + for (i=0; i 0 && folders[i].server.type != lastType) || (folders.length > 1 && isVirtualFolder) ) { + alert(mboximportbundle.GetStringFromName("noFolderExport")); + return; + } + var lastType = folders[i].server.type; + } + if (locale && (lastType == "imap" || lastType == "nntp")) { + var go = IETremoteWarning(); + if (! go) + return; + } + var destdirNSIFILE = getPredefinedFolder(0); + if (! destdirNSIFILE) { + destdirNSIFILE = IETgetPickerModeFolder(); + if (! destdirNSIFILE) + return; + } + + if (zip) { + if (! String.trim) + alert(mboximportbundle.GetStringFromName("needTB3")); + else + IETexportZip(destdirNSIFILE, folders); + return; + } + + if (locale) { + for (var i=0;i -1) { + var mime2DecodedService = Components.classes["@mozilla.org/network/mime-hdrparam;1"] + .getService(Components.interfaces.nsIMIMEHeaderParam); + var dateOrig = header.match(/Date: \=\?.+\?\=\r\n/).toString(); + var dateDecoded = "Date: "+mime2DecodedService.getParameter(dateOrig.substring(6), null, "", false, {value: null})+"\r\n"; + header = header.replace(dateOrig, dateDecoded); + } + var data = header+text.substring(index); + var data = text; + } + catch(e) { + var data = text; + } + + if (! this.imap) + writeDataToFolder(data,this.msgFolder,this.file,this.removeFile); + importEMLlistener.next(); + }, + + next : function() { + if (this.allEML && gEMLimported < gFileEMLarray.length) { + var nextFile = gFileEMLarray[gEMLimported].file; + trytoimportEML(nextFile,gFileEMLarray[gEMLimported].msgFolder,this.removeFile, this.fileArray,this.allEML); + } + else if (this.fileArray && gEMLimported < this.fileArray.length) { + var nextFile = this.fileArray[gEMLimported]; + trytoimportEML(nextFile,this.msgFolder,this.removeFile, this.fileArray, false); + } + else { + // At the end we update the fodler view and summary + this.msgFolder.updateFolder(msgWindow); + this.msgFolder.updateSummaryTotals(true); + } + }, + + QueryInterface : function(aIID) { + if (aIID.equals(Components.interfaces.nsISupports) || + aIID.equals(Components.interfaces.nsIInterfaceRequestor) || + aIID.equals(Components.interfaces.nsIChannelEventSink) || + aIID.equals(Components.interfaces.nsIProgressEventSink) || + aIID.equals(Components.interfaces.nsIHttpEventSink) || + aIID.equals(Components.interfaces.nsIStreamListener)) + return this; + + throw Components.results.NS_NOINTERFACE; + } +}; + + + +function trytoimportEML(file,msgFolder,removeFile,fileArray,allEML) { + if (file.path.indexOf(".emlx") > -1) { + file = IETemlx2eml(file); + } + + var listener = importEMLlistener; + importEMLlistener.msgFolder = msgFolder; + importEMLlistener.removeFile = removeFile; + importEMLlistener.file = file; + importEMLlistener.fileArray = fileArray; + importEMLlistener.allEML = allEML; + if (String.trim && msgFolder.server.type == "imap" ) { + importEMLlistener.imap = true; + var cs = Components.classes["@mozilla.org/messenger/messagecopyservice;1"] + .getService(Components.interfaces.nsIMsgCopyService); + cs.CopyFileMessage(file, msgFolder, null, false, 1, "", importEMLlistener, msgWindow); + if (! removeFile) { + gEMLimported = gEMLimported + 1; + IETwritestatus(mboximportbundle.GetStringFromName("numEML")+gEMLimported+"/"+gEMLtotal); + } + } + else { + importEMLlistener.imap = false; + var ios = Components.classes["@mozilla.org/network/io-service;1"] + .getService(Components.interfaces.nsIIOService); + var fileURI = ios.newFileURI(file); + var channel = ios.newChannelFromURI(fileURI); + channel.asyncOpen(listener, null); + } +} + +function writeDataToFolder(data,msgFolder,file,removeFile) { + var msgLocalFolder = msgFolder.QueryInterface(Components.interfaces.nsIMsgLocalMailFolder); + // strip off the null characters, that break totally import and display + data = data.replace(/\x00/g, ""); + var now = new Date; + try { + var nowString = now.toString().match(/.+:\d\d/); + nowString = nowString.toString().replace(/\d{4} /, ""); + nowString = nowString + " " + now.getFullYear(); + } + catch(e) { + var nowString = now.toString().replace(/GMT.+/, ""); + } + + var top = data.substring(0,2000); + + // Fix for crazy format returned by Hotmail view-source + if (top.match(/X-Message-Delivery:.+\r?\n\r?\n/) || top.match(/X-Message-Info:.+\r?\n\r?\n/) ) + data = data.replace(/(\r?\n\r?\n)/g, "\n"); + + // Fix for some not-compliant date headers + if (top.match(/Posted-Date\:/)) + data = data.replace("Posted-Date:", "Date:"); + if (top.match(/X-OriginalArrivalTime:.+\r?\n\r?\n/)) + data = data.replace("X-OriginalArrivalTime:", "Date:"); + + // Some eml files begin with "From " + // This causes that Thunderbird will not handle properly the message + // so in this case the first line is deleted + data = data.replace(/^From\s+.+\r?\n/, ""); + + // Prologue needed to add the message to the folder + var prologue = "From - " + nowString + "\n"; // The first line must begin with "From -", the following is not important + // If the message has no X-Mozilla-Status, we add them to it + if (data.indexOf("X-Mozilla-Status") < 0) + var prologue = prologue + "X-Mozilla-Status: 0000\nX-Mozilla-Status2: 00000000\n"; + else if (IETprefs.getBoolPref("extensions.importexporttools.reset_mozilla_status")) { + // Reset the X-Mozilla status + data = data.replace(/X-Mozilla-Status: \d{4}/, "X-Mozilla-Status: 0000"); + data = data.replace(/X-Mozilla-Status2: \d{8}/, "X-Mozilla-Status2: 00000000"); + } + // If the message has no X-Account-Key, we add it to it, taking it from the account selected + if (data.indexOf("X-Account-Key") < 0) { + var myAccountManager = Components.classes["@mozilla.org/messenger/account-manager;1"] + .getService(Components.interfaces.nsIMsgAccountManager); + var myAccount = myAccountManager.FindAccountForServer(msgFolder.server); + prologue = prologue + "X-Account-Key: " + myAccount.key + "\n"; + } + data = IETescapeBeginningFrom(data); + // Add the prologue to the EML text + data = prologue + data + "\n"; + // Add the email to the folder + msgLocalFolder.addMessage(data); + gEMLimported = gEMLimported + 1; + IETwritestatus(mboximportbundle.GetStringFromName("numEML")+gEMLimported+"/"+gEMLtotal); + if (removeFile) + file.remove(false); +} + +function importEmlToFolder() { + // To import an eml attachment in folder, as a real message, it's necessary to save it + // in a temporary file in temp directory + var restoreDownloadWindowPref = false; + var msgFolder = GetSelectedMsgFolders()[0]; + // 0x0020 is MSG_FOLDER_FLAG_VIRTUAL + var isVirtFol = msgFolder ? msgFolder.flags & 0x0020 : false; + if (! String.trim && ( (msgFolder.server.type != "pop3" && msgFolder.server.type != "none") || isVirtFol) ) { + alert(mboximportbundle.GetStringFromName("badfolder2")); + return; + } + var item = document.getElementById("attachmentList").selectedItem; + var attachment = item.attachment; + var tempdir = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("TmpD", Components.interfaces.nsIFile); + + // Hide download window, if necessary (TB2 only) + try { + var downloadWindowPref = IETprefs.getBoolPref("browser.download.manager.useWindow"); + if (downloadWindowPref) { + IETprefs.setBoolPref("browser.download.manager.useWindow", false); + restoreDownloadWindowPref = true; + } + } + catch(e) {} + IETtempfilesize = -1; + IETcount = 0; + try { + var uri = attachment.uri ? attachment.uri : attachment.messageUri; + var tempfile = messenger.saveAttachmentToFolder(attachment.contentType , attachment.url , encodeURIComponent(attachment.displayName), uri , tempdir); + window.setTimeout(checkToImportEMLattach,1000,tempfile,msgFolder); + } + catch(e) { + alert(mboximportbundle.GetStringFromName("temp_error")); + } + if (restoreDownloadWindowPref) + window.setTimeout(function() {IETprefs.setBoolPref("browser.download.manager.useWindow", true);}, 500); +} + +function checkToImportEMLattach(file,msgFolder) { + // To see if the download is finished, the extension checks the filesize + // every second, for 20 times (20 sec. are enough for every attachment) + if (! file.exists()) + return; + if (file.fileSize != IETtempfilesize) { + IETtempfilesize = file.fileSize; + if (IETcount < 20) { + IETcount++; + window.setTimeout(checkToImportEMLattach,1000,file,msgFolder); + } + else + file.remove(false); + return; + } + // The download is finished, call the eml import function, with the "delete-file" option + trytoimportEML(file,msgFolder,true, null,false); +} + +function openIEToptions() { + window.openDialog("chrome://mboximport/content/mboximportOptions.xul","", "chrome,modal,centerscreen"); +} + +function IETcopyFolderPath() { + var msgFolder = GetSelectedMsgFolders()[0]; + var file = msgFolder2LocalFile(msgFolder); + IETcopyStrToClip(file.path) +} + +function IETopenFolderPath() { + var msgFolder = GetSelectedMsgFolders()[0]; + var file = msgFolder2LocalFile(msgFolder); + try { + // Show the directory containing the file and select the file + file.reveal(); + } catch (e) { + // If reveal fails for some reason (e.g., it's not implemented on unix or + // the file doesn't exist), try using the parent if we have it. + if (msgFolder.isServer) + var parent = file; + else + var parent = file.parent.QueryInterface(Components.interfaces.nsIFile); + if (! parent) + return; + try { + // "Double click" the parent directory to show where the file should be + parent.launch(); + } catch (e) { + // If launch also fails (probably because it's not implemented), let the + // OS handler try to open the parent + var uri = Components.classes["@mozilla.org/network/io-service;1"]. + getService(Components.interfaces.nsIIOService).newFileURI(file); + var protocolSvc = Components.classes["@mozilla.org/uriloader/external-protocol-service;1"]. + getService(Components.interfaces.nsIExternalProtocolService); + protocolSvc.loadUrl(uri); + } + } +} + +function IETimportSMS() { + var msgFolder = GetSelectedMsgFolders()[0]; + if ( (msgFolder.server.type == "imap" ) || (msgFolder.server.type == "nntp" ) ) { + alert(mboximportbundle.GetStringFromName("badfolder")); + return; + } + var nsIFilePicker = Components.interfaces.nsIFilePicker; + var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker); + fp.init(window, mboximportbundle.GetStringFromName("importSMSandroid"), nsIFilePicker.modeOpen); + fp.appendFilter("*.xml", "*.xml"); + if (fp.show) + var res = fp.show(); + else + var res = IETopenFPsync(fp); + if (res==nsIFilePicker.returnOK) { + var msgLocalFolder = msgFolder.QueryInterface(Components.interfaces.nsIMsgLocalMailFolder); + var xml =fp.file; + var xmlhttp = new XMLHttpRequest(); + xmlhttp.open("GET", "file:///"+xml.path,true); + xmlhttp.onreadystatechange=function() { + if (xmlhttp.readyState==4) { + var dom = xmlhttp.responseXML; + var smss = dom.getElementsByTagName("sms"); + var uConv = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"] + .createInstance(Components.interfaces.nsIScriptableUnicodeConverter); + uConv.charset = "UTF-8"; + var abManager = Components.classes["@mozilla.org/abmanager;1"] + .getService(Components.interfaces.nsIAbManager); + var myAccountManager = Components.classes["@mozilla.org/messenger/account-manager;1"] + .getService(Components.interfaces.nsIMsgAccountManager); + if (msgFolder) { + var incServer = msgFolder.server; + var identity = myAccountManager.getFirstIdentityForServer(incServer); + } + else + var identity = null; + if (identity) + var myname = identity.fullName; + else + var myname = myAccountManager.defaultAccount.defaultIdentity.fullName; + var subOn = IETprefs.getBoolPref("extensions.importexporttools.sms.add_subject"); + + for (var i=0;i"; + else + var address = smss[i].getAttribute("address"); + if (smss[i].getAttribute("type") == "1") { + var from = address; + var to = myname; + } + else { + var to = address; + var from = myname; + } + + var when = smss[i].getAttribute("date"); + var d = new Date(parseInt(when)); + var body = smss[i].getAttribute("body"); + if (subOn) + var subject = body.substring(0,20); + else + var subject = ""; + var now = new Date; + try { + var nowString = now.toString().match(/.+:\d\d/); + nowString = nowString.toString().replace(/\d{4} /, ""); + nowString = nowString + " " + now.getFullYear(); + } + catch(e) { + var nowString = now.toString().replace(/GMT.+/, ""); + } + var email = "From - "+nowString+"\n"+ "X-Mozilla-Status: 0001\nX-Mozilla-Status2: 00000000\n"+"Date: "+d+"\n"+"From: "+from+"\n"+"To: "+to+"\n"+"Subject: "+subject+"\nX-SMS: true\nMIME-Version: 1.0\nContent-Type: text/plain; charset=UTF-8\n\n"+body+"\n\n"; + email = uConv.ConvertFromUnicode(email); + msgLocalFolder.addMessage(email); + } + } + } + xmlhttp.send(null); + } +} diff --git a/src/chrome/content/mboximport/mboximport.xul b/src/chrome/content/mboximport/mboximport.xul new file mode 100644 index 00000000..b2580652 --- /dev/null +++ b/src/chrome/content/mboximport/mboximport.xul @@ -0,0 +1,324 @@ + + + + + + diff --git a/src/chrome/content/mboximport/printEngineWindowOverlay.js b/src/chrome/content/mboximport/printEngineWindowOverlay.js new file mode 100644 index 00000000..1bcfa7db --- /dev/null +++ b/src/chrome/content/mboximport/printEngineWindowOverlay.js @@ -0,0 +1,47 @@ +var IETprintPDFengine = { + prefs : Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch), + + exit : function() { + if (opener.IETprintPDFmain.total > 0 && ! IETprintPDFengine.error) + opener.IETprintPDFmain.printDelayed(); + else + IETprintPDFengine.restore(); + }, + + restore : function() { + IETprintPDFengine.prefs.setBoolPref("extensions.importexporttools.printPDF.start", false); + if (IETprintPDFengine.prefs.getBoolPref("extensions.importexporttools.printPDF.restore_print_silent")) + IETprintPDFengine.prefs.setBoolPref("print.always_print_silent", false); + opener.document.getElementById("IETabortIcon").collapsed = true; + }, + + onLoad : function() { + try { + PrintEngineCreateGlobals(); + InitPrintEngineWindow(); + var PSSVC = Components.classes["@mozilla.org/gfx/printsettings-service;1"] + .getService(Components.interfaces.nsIPrintSettingsService); + var myPrintSettings = PSSVC.newPrintSettings; + myPrintSettings.printSilent = true; + myPrintSettings.toFileName = opener.IETprintPDFmain.filePath; + myPrintSettings.printToFile = true; + var fileFormat = IETprintPDFengine.prefs.getIntPref("extensions.importexporttools.printPDF.fileFormat"); + if (fileFormat < 3) + myPrintSettings.outputFormat = fileFormat; + printEngine.startPrintOperation(myPrintSettings); + } + catch(e) { + IETprintPDFengine.error = true; + setTimeout(function() {window.close();}, 500); + } + } +}; + + +if (IETprintPDFengine.prefs.getBoolPref("extensions.importexporttools.printPDF.start")) { + OnLoadPrintEngine = IETprintPDFengine.onLoad; + IETprintPDFengine.prefs.setBoolPref("extensions.importexporttools.printPDF.start", false); +} + +window.addEventListener("unload", IETprintPDFengine.exit, false); + diff --git a/src/chrome/content/mboximport/printEngineWindowOverlay.xul b/src/chrome/content/mboximport/printEngineWindowOverlay.xul new file mode 100644 index 00000000..6d975e4b --- /dev/null +++ b/src/chrome/content/mboximport/printEngineWindowOverlay.xul @@ -0,0 +1,8 @@ + + + +