├── test ├── .gitignore ├── A memo.msg ├── Simple.msg ├── sent.msg ├── sent2.msg ├── test1.msg ├── test2.msg ├── voteNo.msg ├── Subject.msg ├── htmlLink.msg ├── msgInMsg.msg ├── unicode1.msg ├── voteYes.msg ├── A daily 1.msg ├── A monthly 1.msg ├── A schedule.msg ├── A weekly 1.msg ├── A yearly 1.msg ├── Hello +CJK.msg ├── Outer mail.msg ├── contactAnsi.msg ├── htmlLink2.msg ├── longerDifat.msg ├── longerFat.msg ├── voteItems.msg ├── Friday Lunch.msg ├── msgInMsgInMsg.msg ├── 200 recipients.msg ├── 7 days everyday.msg ├── attachAndInline.msg ├── attachmentFiles.msg ├── attachmentsOrder.msg ├── contactUnicode.msg ├── nonUnicodeCP932.msg ├── nonUnicodeMail.msg ├── A black friday (w tz).msg ├── A black friday (wo tz).msg ├── Appointment sample EST.msg ├── Attach sample eml sent.msg ├── Attach sample eml received.msg ├── newOutlook Microsoft Outlook test.msg ├── Lanch time every friday in 2023 org.msg ├── Lanch time every friday in 2023 chgs1.msg ├── Lanch time every friday in 2023 chgs2.msg ├── test1.rtf ├── A memo.rtf ├── A memo.json ├── nonUnicodeCP932.json ├── nonUnicodeMail.json ├── sent.json ├── test1.json ├── test2.json ├── longerFat-attachments0.json ├── longerDifat-attachments0.json ├── Subject.json ├── sent2.json ├── attachAndInline.json ├── msgInMsg-attachments0.json ├── msgInMsgInMsg-attachments0-attachments0.json ├── attachmentsOrder.json ├── voteItems.json ├── voteNo.json ├── voteYes.json ├── attachmentFiles.json ├── A black friday (w tz).json ├── A black friday (wo tz).json ├── contactUnicode.json ├── contactAnsi.json ├── A schedule.json ├── msgInMsg.json ├── msgInMsgInMsg-attachments0.json ├── Appointment sample EST.json ├── sent.rtf ├── voteItems.rtf ├── msgInMsgInMsg.json ├── A monthly 1.json ├── A yearly 1.json ├── A daily 1.json ├── A weekly 1.json ├── Lanch time every friday in 2023 org.json ├── 7 days everyday.json ├── Lanch time every friday in 2023 chgs1.json ├── Lanch time every friday in 2023 chgs2.json ├── Friday Lunch.json ├── A schedule.rtf └── sent2.rtf ├── data └── test.msg ├── test2 ├── Small.bin ├── Normal.bin └── test.js ├── tools └── ValidateCompoundFile.exe ├── others ├── PidLidAppointmentRecur │ ├── chgs1 │ ├── chgs2 │ └── org ├── StringStream.bt ├── EntryStream.bt ├── PidLidVerbStream.bt ├── msg.bt └── AppointmentRecurrencePattern.bt ├── src ├── index.ts ├── EntryStreamParser.ts ├── VerbStreamParser.ts ├── TZREGParser.ts ├── utils.ts ├── TZDEFINITIONParser.ts └── const.ts ├── .gitmodules ├── MEMO.md ├── typedoc.json ├── .editorconfig ├── tsconfig.json ├── docup.js ├── uml └── broken-msg.plantuml ├── .github └── workflows │ ├── close-inactive-issues.yml │ └── codeql.yml ├── azure-pipelines.yml ├── package.json ├── .gitignore ├── README.md ├── cli.js └── LICENSE /test/.gitignore: -------------------------------------------------------------------------------- 1 | /vote3 2 | /Attach and inline 3 | -------------------------------------------------------------------------------- /data/test.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/data/test.msg -------------------------------------------------------------------------------- /test/A memo.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/A memo.msg -------------------------------------------------------------------------------- /test/Simple.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/Simple.msg -------------------------------------------------------------------------------- /test/sent.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/sent.msg -------------------------------------------------------------------------------- /test/sent2.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/sent2.msg -------------------------------------------------------------------------------- /test/test1.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/test1.msg -------------------------------------------------------------------------------- /test/test2.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/test2.msg -------------------------------------------------------------------------------- /test/voteNo.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/voteNo.msg -------------------------------------------------------------------------------- /test2/Small.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test2/Small.bin -------------------------------------------------------------------------------- /test/Subject.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/Subject.msg -------------------------------------------------------------------------------- /test/htmlLink.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/htmlLink.msg -------------------------------------------------------------------------------- /test/msgInMsg.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/msgInMsg.msg -------------------------------------------------------------------------------- /test/unicode1.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/unicode1.msg -------------------------------------------------------------------------------- /test/voteYes.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/voteYes.msg -------------------------------------------------------------------------------- /test2/Normal.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test2/Normal.bin -------------------------------------------------------------------------------- /test/A daily 1.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/A daily 1.msg -------------------------------------------------------------------------------- /test/A monthly 1.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/A monthly 1.msg -------------------------------------------------------------------------------- /test/A schedule.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/A schedule.msg -------------------------------------------------------------------------------- /test/A weekly 1.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/A weekly 1.msg -------------------------------------------------------------------------------- /test/A yearly 1.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/A yearly 1.msg -------------------------------------------------------------------------------- /test/Hello +CJK.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/Hello +CJK.msg -------------------------------------------------------------------------------- /test/Outer mail.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/Outer mail.msg -------------------------------------------------------------------------------- /test/contactAnsi.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/contactAnsi.msg -------------------------------------------------------------------------------- /test/htmlLink2.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/htmlLink2.msg -------------------------------------------------------------------------------- /test/longerDifat.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/longerDifat.msg -------------------------------------------------------------------------------- /test/longerFat.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/longerFat.msg -------------------------------------------------------------------------------- /test/voteItems.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/voteItems.msg -------------------------------------------------------------------------------- /test/Friday Lunch.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/Friday Lunch.msg -------------------------------------------------------------------------------- /test/msgInMsgInMsg.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/msgInMsgInMsg.msg -------------------------------------------------------------------------------- /test/200 recipients.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/200 recipients.msg -------------------------------------------------------------------------------- /test/7 days everyday.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/7 days everyday.msg -------------------------------------------------------------------------------- /test/attachAndInline.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/attachAndInline.msg -------------------------------------------------------------------------------- /test/attachmentFiles.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/attachmentFiles.msg -------------------------------------------------------------------------------- /test/attachmentsOrder.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/attachmentsOrder.msg -------------------------------------------------------------------------------- /test/contactUnicode.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/contactUnicode.msg -------------------------------------------------------------------------------- /test/nonUnicodeCP932.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/nonUnicodeCP932.msg -------------------------------------------------------------------------------- /test/nonUnicodeMail.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/nonUnicodeMail.msg -------------------------------------------------------------------------------- /test/A black friday (w tz).msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/A black friday (w tz).msg -------------------------------------------------------------------------------- /tools/ValidateCompoundFile.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/tools/ValidateCompoundFile.exe -------------------------------------------------------------------------------- /test/A black friday (wo tz).msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/A black friday (wo tz).msg -------------------------------------------------------------------------------- /test/Appointment sample EST.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/Appointment sample EST.msg -------------------------------------------------------------------------------- /test/Attach sample eml sent.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/Attach sample eml sent.msg -------------------------------------------------------------------------------- /others/PidLidAppointmentRecur/chgs1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/others/PidLidAppointmentRecur/chgs1 -------------------------------------------------------------------------------- /others/PidLidAppointmentRecur/chgs2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/others/PidLidAppointmentRecur/chgs2 -------------------------------------------------------------------------------- /others/PidLidAppointmentRecur/org: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/others/PidLidAppointmentRecur/org -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import MsgReader from "./MsgReader"; 2 | 3 | export * from "./MsgReader"; 4 | 5 | export default MsgReader; 6 | -------------------------------------------------------------------------------- /test/Attach sample eml received.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/Attach sample eml received.msg -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "docs"] 2 | path = docs 3 | url = https://github.com/HiraokaHyperTools/msgreader.git 4 | branch = gh-pages 5 | -------------------------------------------------------------------------------- /test/newOutlook Microsoft Outlook test.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/newOutlook Microsoft Outlook test.msg -------------------------------------------------------------------------------- /test/Lanch time every friday in 2023 org.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/Lanch time every friday in 2023 org.msg -------------------------------------------------------------------------------- /test/Lanch time every friday in 2023 chgs1.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/Lanch time every friday in 2023 chgs1.msg -------------------------------------------------------------------------------- /test/Lanch time every friday in 2023 chgs2.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiraokaHyperTools/msgreader/HEAD/test/Lanch time every friday in 2023 chgs2.msg -------------------------------------------------------------------------------- /MEMO.md: -------------------------------------------------------------------------------- 1 | # my MEMO 2 | 3 | ## To restore npm packages 4 | 5 | ```bat 6 | yarn 7 | ``` 8 | 9 | ## To build 10 | 11 | ```bat 12 | npm run build 13 | ``` 14 | 15 | ## To publish 16 | 17 | ```bat 18 | npm adduser 19 | npm publish 20 | ``` 21 | -------------------------------------------------------------------------------- /test/test1.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\fromtext \fbidis \deff0{\fonttbl 2 | {\f0\fswiss Arial;} 3 | {\f1\fmodern Courier New;} 4 | {\f2\fnil\fcharset2 Symbol;} 5 | {\f3\fmodern\fcharset0 Courier New;}} 6 | {\colortbl\red0\green0\blue0;\red0\green0\blue255;} 7 | \uc1\pard\plain\deftab360 \f0\fs20 body\par 8 | } -------------------------------------------------------------------------------- /test/A memo.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg932\deff0\nouicompat\deflang1033\deflangfe1041{\fonttbl{\f0\fswiss\fcharset0 Calibri;}{\f1\fmodern\fcharset128 Meiryo UI;}} 2 | {\colortbl ;\red0\green0\blue0;} 3 | {\*\generator Riched20 15.0.5233}{\*\mmathPr\mwrapIndent1440 }\viewkind4\uc1 4 | \pard\cf1\f0\fs22 A memo.\f1\lang1041\par 5 | } 6 | -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": [ 3 | "./src/Burner.ts", 4 | "./src/DataStream.ts", 5 | "./src/MsgReader.ts", 6 | "./src/Reader.ts", 7 | "./src/Defs.ts" 8 | ], 9 | "out": "docs/typedoc", 10 | "excludePrivate": true, 11 | "excludeInternal": true, 12 | "readme": "none", 13 | "includeVersion": true 14 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | end_of_line = crlf 8 | 9 | [*.js] 10 | indent_style = space 11 | indent_size = 2 12 | 13 | [*.ts] 14 | indent_style = space 15 | indent_size = 2 16 | 17 | [Burner.ts] 18 | indent_style = space 19 | indent_size = 4 20 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES5", 4 | "module": "CommonJS", 5 | "declaration": true, 6 | "outDir": "lib", 7 | "allowSyntheticDefaultImports": true, 8 | "esModuleInterop": true, 9 | "moduleResolution": "Node", 10 | "typeRoots": [ 11 | "node_modules/@types" 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /docup.js: -------------------------------------------------------------------------------- 1 | // for package maintenance: document auto updater. 2 | // usage: node docup.js 3 | 4 | const util = require('util'); 5 | const exec = util.promisify(require('child_process').exec); 6 | 7 | async function run() { 8 | await exec("yarn typedoc", {}); 9 | await exec("git add -A", { cwd: "docs" }); 10 | await exec("git commit -a -m \"Update doc\"", { cwd: "docs" }); 11 | await exec("git push", { cwd: "docs" }); 12 | await exec("git commit docs -m \"- Update doc\"", {}); 13 | await exec("git push", {}); 14 | console.info("OK!"); 15 | } 16 | 17 | run(); 18 | -------------------------------------------------------------------------------- /test/A memo.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [], 4 | "recipients": [], 5 | "messageClass": "IPM.StickyNote", 6 | "subject": "A memo.", 7 | "conversationTopic": "A memo.", 8 | "normalizedSubject": "A memo.", 9 | "body": "A memo.\r\n", 10 | "lastModifierName": "ku@digitaldolphins.onmicrosoft.com", 11 | "inetAcctName": "ku@digitaldolphins.onmicrosoft.com", 12 | "creationTime": "Wed, 13 Oct 2021 09:16:00 GMT", 13 | "lastModificationTime": "Wed, 13 Oct 2021 09:16:00 GMT", 14 | "clientSubmitTime": "Wed, 13 Oct 2021 09:15:57 GMT", 15 | "messageDeliveryTime": "Wed, 13 Oct 2021 09:15:57 GMT", 16 | "messageFlags": 1, 17 | "messageLocaleId": 1041 18 | } -------------------------------------------------------------------------------- /others/StringStream.bt: -------------------------------------------------------------------------------- 1 | //------------------------------------------------ 2 | //--- 010 Editor v11.0.1 Binary Template 3 | // 4 | // File: String Stream 5 | // Authors: 6 | // Version: 7 | // Purpose: https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxmsg/14dd4e27-58e1-4d6c-b4c2-7a1fd19d819c 8 | // Category: 9 | // File Mask: __substg1.0_00040102 10 | // ID Bytes: 11 | // History: 12 | //------------------------------------------------ 13 | 14 | LittleEndian(); 15 | 16 | struct Entry { 17 | uint32 NameLength ; 18 | wchar_t Name[NameLength / 2] ; 19 | }; 20 | 21 | while (!FEof()) { 22 | Entry entry; 23 | FSeek((FTell() + 3) & (~3)); 24 | } 25 | -------------------------------------------------------------------------------- /test/nonUnicodeCP932.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [], 4 | "recipients": [ 5 | { 6 | "dataType": "recipient", 7 | "name": "xmailuser2@xmailserver.test", 8 | "addressType": "SMTP", 9 | "email": "xmailuser2@xmailserver.test", 10 | "recipType": "to" 11 | } 12 | ], 13 | "messageClass": "IPM.Note", 14 | "subject": "日本語 Non Unicode タイトル", 15 | "conversationTopic": "日本語 Non Unicode タイトル", 16 | "normalizedSubject": "日本語 Non Unicode タイトル", 17 | "body": "日本語 Non Unicode 本文\r\n", 18 | "creationTime": "Fri, 05 Nov 2021 10:25:21 GMT", 19 | "lastModificationTime": "Fri, 05 Nov 2021 10:25:21 GMT", 20 | "messageDeliveryTime": "Fri, 05 Nov 2021 10:25:17 GMT", 21 | "messageFlags": 9, 22 | "internetCodepage": 50220, 23 | "messageLocaleId": 1041 24 | } -------------------------------------------------------------------------------- /test/nonUnicodeMail.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [], 4 | "recipients": [ 5 | { 6 | "dataType": "recipient", 7 | "name": "xmailuser2@xmailserver.test", 8 | "addressType": "SMTP", 9 | "email": "xmailuser2@xmailserver.test", 10 | "recipType": "to" 11 | } 12 | ], 13 | "messageClass": "IPM.Note", 14 | "subject": "Non Unicode mail subject", 15 | "conversationTopic": "Non Unicode mail subject", 16 | "normalizedSubject": "Non Unicode mail subject", 17 | "body": "Non Unicode mail body!\r\n", 18 | "creationTime": "Fri, 05 Nov 2021 10:23:10 GMT", 19 | "lastModificationTime": "Fri, 05 Nov 2021 10:23:10 GMT", 20 | "messageDeliveryTime": "Fri, 05 Nov 2021 10:22:54 GMT", 21 | "messageFlags": 8, 22 | "internetCodepage": 20127, 23 | "messageLocaleId": 1041 24 | } -------------------------------------------------------------------------------- /uml/broken-msg.plantuml: -------------------------------------------------------------------------------- 1 | @startuml how-to 2 | start 3 | 4 | :Open ""xxx.msg"" with Outlook; 5 | 6 | if (Success?) then (Yes) 7 | :Case 0. 8 | It seems no problem.; 9 | stop 10 | else (No: We can't open xxx.msg) 11 | :Open ""xxx.msg"" with 7-Zip File Manager; 12 | if (Success?) then (No: Can not open file xxx.msg as archive) 13 | :Case 1. 14 | File is broken 15 | (Not valid Compound File); 16 | :Maybe bug in **Compound File handling**. 17 | Check: 18 | - ""Reader"" class 19 | - ""Burner"" class 20 | ; 21 | stop 22 | else (Yes) 23 | :Case 2. 24 | File is not broken 25 | (Valid Compound File); 26 | :Maybe bug in **structure handling**. 27 | Check: 28 | - ""MsgReader"" class 29 | ; 30 | stop 31 | endif 32 | endif 33 | 34 | @enduml -------------------------------------------------------------------------------- /.github/workflows/close-inactive-issues.yml: -------------------------------------------------------------------------------- 1 | name: Close inactive issues 2 | on: 3 | schedule: 4 | - cron: "30 1 * * *" 5 | 6 | jobs: 7 | close-issues: 8 | runs-on: ubuntu-latest 9 | permissions: 10 | issues: write 11 | pull-requests: write 12 | steps: 13 | - uses: actions/stale@v5 14 | with: 15 | days-before-issue-stale: 30 16 | days-before-issue-close: 14 17 | stale-issue-label: "stale" 18 | stale-issue-message: "This issue is stale because it has been open for 30 days with no activity." 19 | close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale." 20 | days-before-pr-stale: -1 21 | days-before-pr-close: -1 22 | repo-token: ${{ secrets.GITHUB_TOKEN }} 23 | -------------------------------------------------------------------------------- /src/EntryStreamParser.ts: -------------------------------------------------------------------------------- 1 | import DataStream from "./DataStream"; 2 | 3 | export interface Entry { 4 | key: number; 5 | isStringProperty: boolean; 6 | guidIndex: number; 7 | propertyIndex: number; 8 | } 9 | 10 | /** 11 | * @internal 12 | */ 13 | export function parse(array: Uint8Array): Entry[] { 14 | const ds = new DataStream(array, 0, DataStream.LITTLE_ENDIAN); 15 | const ret: Entry[] = []; 16 | while (!ds.isEof()) { 17 | const key = ds.readUint32(); 18 | const low = ds.readUint16(); 19 | const hi = ds.readUint16(); 20 | ret.push( 21 | { 22 | key, 23 | isStringProperty: (low & 1) != 0, 24 | guidIndex: (low >> 1) & 32767, 25 | propertyIndex: hi, 26 | } 27 | ) 28 | } 29 | return ret; 30 | } 31 | -------------------------------------------------------------------------------- /test/sent.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [], 4 | "recipients": [ 5 | { 6 | "dataType": "recipient", 7 | "name": "'xmailuser@xmailserver.test'", 8 | "addressType": "SMTP", 9 | "email": "xmailuser@xmailserver.test", 10 | "recipType": "to" 11 | } 12 | ], 13 | "messageClass": "IPM.Note", 14 | "subject": "Sent time", 15 | "conversationTopic": "Sent time", 16 | "senderName": "xmailuser", 17 | "senderAddressType": "SMTP", 18 | "senderEmail": "xmailuser@xmailserver.test", 19 | "normalizedSubject": "Sent time", 20 | "body": "Test mail\r\n\r\n", 21 | "creationTime": "Mon, 15 Feb 2021 08:19:21 GMT", 22 | "lastModificationTime": "Mon, 15 Feb 2021 08:19:21 GMT", 23 | "clientSubmitTime": "Mon, 15 Feb 2021 08:19:04 GMT", 24 | "messageDeliveryTime": "Mon, 15 Feb 2021 08:19:00 GMT", 25 | "messageFlags": 1, 26 | "internetCodepage": 20127, 27 | "messageLocaleId": 1041 28 | } -------------------------------------------------------------------------------- /test/test1.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [], 4 | "recipients": [ 5 | { 6 | "dataType": "recipient", 7 | "name": "to@example.com", 8 | "addressType": "SMTP", 9 | "email": "to@example.com", 10 | "recipType": "to" 11 | }, 12 | { 13 | "dataType": "recipient", 14 | "name": "cc@example.com", 15 | "addressType": "SMTP", 16 | "email": "cc@example.com", 17 | "recipType": "cc" 18 | } 19 | ], 20 | "messageClass": "IPM.Note", 21 | "subject": "title", 22 | "conversationTopic": "title", 23 | "normalizedSubject": "title", 24 | "body": "body\r\n", 25 | "inetAcctName": "ku@digitaldolphins.jp", 26 | "creationTime": "Tue, 05 Mar 2019 07:22:33 GMT", 27 | "lastModificationTime": "Tue, 05 Mar 2019 07:22:33 GMT", 28 | "messageDeliveryTime": "Tue, 05 Mar 2019 07:22:17 GMT", 29 | "messageFlags": 8, 30 | "internetCodepage": 20127, 31 | "messageLocaleId": 1041 32 | } -------------------------------------------------------------------------------- /test/test2.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [ 4 | { 5 | "dataType": "attachment", 6 | "name": "A.txt", 7 | "dataId": 40, 8 | "contentLength": 11, 9 | "extension": ".txt", 10 | "fileNameShort": "A.txt", 11 | "fileName": "A.txt", 12 | "creationTime": "Tue, 05 Mar 2019 07:41:02 GMT", 13 | "lastModificationTime": "Tue, 05 Mar 2019 07:40:48 GMT", 14 | "attachmentHidden": false 15 | } 16 | ], 17 | "recipients": [], 18 | "messageClass": "IPM.Note", 19 | "subject": "", 20 | "conversationTopic": "", 21 | "normalizedSubject": "", 22 | "body": " \r\n", 23 | "inetAcctName": "ku@digitaldolphins.jp", 24 | "creationTime": "Tue, 05 Mar 2019 07:41:09 GMT", 25 | "lastModificationTime": "Tue, 05 Mar 2019 07:41:09 GMT", 26 | "messageDeliveryTime": "Tue, 05 Mar 2019 07:41:04 GMT", 27 | "messageFlags": 24, 28 | "internetCodepage": 20127, 29 | "messageLocaleId": 1041 30 | } -------------------------------------------------------------------------------- /test/longerFat-attachments0.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [ 4 | { 5 | "dataType": "attachment", 6 | "name": "64KB.bin", 7 | "dataId": 33, 8 | "contentLength": 65536, 9 | "extension": ".bin", 10 | "fileNameShort": "64KB.bin", 11 | "fileName": "64KB.bin", 12 | "creationTime": "Thu, 15 Jul 2021 13:29:43 GMT", 13 | "lastModificationTime": "Thu, 15 Jul 2021 13:29:43 GMT", 14 | "attachmentHidden": false 15 | } 16 | ], 17 | "recipients": [], 18 | "messageClass": "IPM.Note", 19 | "subject": "Has 64KB.bin", 20 | "conversationTopic": "Has 64KB.bin", 21 | "normalizedSubject": "Has 64KB.bin", 22 | "body": " \r\n", 23 | "creationTime": "Thu, 15 Jul 2021 13:31:27 GMT", 24 | "lastModificationTime": "Thu, 15 Jul 2021 13:31:27 GMT", 25 | "messageDeliveryTime": "Thu, 15 Jul 2021 13:30:21 GMT", 26 | "messageFlags": 25, 27 | "internetCodepage": 20127, 28 | "messageLocaleId": 1041 29 | } -------------------------------------------------------------------------------- /test2/test.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const assert = require('assert'); 3 | 4 | describe('Reader', function () { 5 | const Reader = require('../lib/Reader').Reader; 6 | 7 | function testIt(file, expectedWords) { 8 | const array = fs.readFileSync(`test2/${file}`); 9 | const reader = new Reader(array); 10 | reader.parse(); 11 | 12 | const readData = reader.rootFolder().readFile("File"); 13 | assert.notStrictEqual(readData, null); 14 | assert.notStrictEqual(readData, undefined); 15 | 16 | const actual = Buffer.from(readData).toString("ascii").replace(/\0+/g, " ").trim(); 17 | assert.strictEqual(actual, expectedWords); 18 | }; 19 | 20 | it('Small.bin', function () { 21 | testIt('Small.bin', "First Second Third"); 22 | }); 23 | it('Normal.bin', function () { 24 | testIt('Normal.bin', "First Second Third Fourth Fifth Sixth Seventh Eighth"); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/longerDifat-attachments0.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [ 4 | { 5 | "dataType": "attachment", 6 | "name": "8MiB.bin", 7 | "dataId": 35, 8 | "contentLength": 8388608, 9 | "extension": ".bin", 10 | "fileNameShort": "8MiB.bin", 11 | "fileName": "8MiB.bin", 12 | "creationTime": "Thu, 15 Jul 2021 13:41:10 GMT", 13 | "lastModificationTime": "Thu, 15 Jul 2021 13:41:10 GMT", 14 | "attachmentHidden": false 15 | } 16 | ], 17 | "recipients": [], 18 | "messageClass": "IPM.Note", 19 | "subject": "Has 8MiB.bin", 20 | "conversationTopic": "Has 8MiB.bin", 21 | "normalizedSubject": "Has 8MiB.bin", 22 | "body": " \r\n", 23 | "inetAcctName": "xmailuser@xmailserver.test", 24 | "creationTime": "Thu, 15 Jul 2021 13:42:25 GMT", 25 | "lastModificationTime": "Thu, 15 Jul 2021 13:42:25 GMT", 26 | "messageDeliveryTime": "Thu, 15 Jul 2021 13:41:55 GMT", 27 | "messageFlags": 25, 28 | "internetCodepage": 20127, 29 | "messageLocaleId": 1041 30 | } -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # Node.js 2 | # Build a general Node.js project with npm. 3 | # Add steps that analyze code, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/javascript 5 | 6 | trigger: 7 | - master 8 | 9 | schedules: 10 | - cron: "0 9 1 * *" 11 | displayName: Monthly build 12 | branches: 13 | include: 14 | - master 15 | always: true 16 | 17 | strategy: 18 | matrix: 19 | "Ubuntu": 20 | image: "ubuntu-latest" 21 | "Windows Server": 22 | image: "windows-latest" 23 | 24 | pool: 25 | vmImage: "$(image)" 26 | 27 | steps: 28 | - task: UseNode@1 29 | inputs: 30 | version: "20.x" 31 | displayName: "Install Node.js" 32 | 33 | - script: "npm i -g yarn" 34 | displayName: "Install yarn" 35 | 36 | - script: "yarn" 37 | displayName: "Invoke yarn" 38 | 39 | - script: "node cli -h" 40 | displayName: "Invoke cli.js" 41 | -------------------------------------------------------------------------------- /others/EntryStream.bt: -------------------------------------------------------------------------------- 1 | //------------------------------------------------ 2 | //--- 010 Editor v11.0.1 Binary Template 3 | // 4 | // File: 2.2.3.1.2 Entry Stream 5 | // Authors: kenjiuno 6 | // Version: 7 | // Purpose: https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxmsg/81159dd0-649e-4491-b216-877008b23f65 8 | // Category: 9 | // File Mask: __substg1.0_00030102 10 | // ID Bytes: 11 | // History: 12 | //------------------------------------------------ 13 | 14 | LittleEndian(); 15 | 16 | struct Entry { 17 | uint NameIdentifier_StringOffset ; 18 | uint16 StringNamedProperty:1; 19 | uint16 GUIDIndex:15; 20 | uint16 PropertyIndex; 21 | }; 22 | 23 | string FormatComment(Entry &e) { 24 | string s = ""; 25 | SPrintf(s, "0x%04x, %d, %d, %d" 26 | , e.NameIdentifier_StringOffset 27 | , e.StringNamedProperty 28 | , e.PropertyIndex 29 | , e.GUIDIndex 30 | ); 31 | return s; 32 | } 33 | 34 | while (!FEof()) { 35 | Entry entry ; 36 | } 37 | -------------------------------------------------------------------------------- /test/Subject.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [], 4 | "recipients": [ 5 | { 6 | "dataType": "recipient", 7 | "name": "ToUser", 8 | "addressType": "SMTP", 9 | "email": "to@example.com", 10 | "recipType": "to" 11 | }, 12 | { 13 | "dataType": "recipient", 14 | "name": "ToCc", 15 | "addressType": "SMTP", 16 | "email": "cc@example.com", 17 | "recipType": "cc" 18 | }, 19 | { 20 | "dataType": "recipient", 21 | "name": "ToBcc", 22 | "addressType": "SMTP", 23 | "email": "bcc@example.com", 24 | "recipType": "bcc" 25 | } 26 | ], 27 | "messageClass": "IPM.Note", 28 | "subject": "Subject", 29 | "conversationTopic": "Subject", 30 | "normalizedSubject": "Subject", 31 | "body": "Message\r\n", 32 | "creationTime": "Mon, 28 Sep 2020 11:28:52 GMT", 33 | "lastModificationTime": "Mon, 28 Sep 2020 11:28:52 GMT", 34 | "messageDeliveryTime": "Mon, 28 Sep 2020 11:28:39 GMT", 35 | "messageFlags": 8, 36 | "internetCodepage": 20127, 37 | "messageLocaleId": 1041 38 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@kenjiuno/msgreader", 3 | "version": "1.27.1-alpha.1", 4 | "description": "Outlook Item File (.msg) reader in JavaScript Npm Module", 5 | "main": "lib/index.js", 6 | "types": "lib/index.d.ts", 7 | "private": false, 8 | "scripts": { 9 | "build": "tsc", 10 | "clean": "rimraf lib", 11 | "test": "npm run mocha test/ test2/", 12 | "prepare": "npm run clean && npm run build && npm run test", 13 | "mocha": "set NODE_ENV=test && mocha", 14 | "dr": "npm publish --dry-run" 15 | }, 16 | "author": "kenjiuno", 17 | "repository": "github:HiraokaHyperTools/msgreader", 18 | "license": "Apache-2.0", 19 | "devDependencies": { 20 | "@types/node": "^22.10.3", 21 | "commander": "^13.0.0", 22 | "mocha": "^11.0.1", 23 | "rimraf": "^6.0.1", 24 | "temp": "^0.9.4", 25 | "typedoc": "^0.27.6", 26 | "typedoc-plugin-rename-defaults": "^0.7.2", 27 | "typescript": "^5.7.2" 28 | }, 29 | "dependencies": { 30 | "@kenjiuno/decompressrtf": "^0.1.3", 31 | "iconv-lite": "^0.6.3" 32 | }, 33 | "engines": { 34 | "node": ">= 10" 35 | }, 36 | "files": [ 37 | "lib" 38 | ] 39 | } -------------------------------------------------------------------------------- /test/sent2.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [], 4 | "recipients": [ 5 | { 6 | "dataType": "recipient", 7 | "name": "ku@digitaldolphins.jp", 8 | "addressType": "SMTP", 9 | "email": "ku@digitaldolphins.jp", 10 | "smtpAddress": "ku@digitaldolphins.jp", 11 | "recipType": "to" 12 | } 13 | ], 14 | "messageClass": "IPM.Note", 15 | "subject": "Sending test", 16 | "conversationTopic": "Sending test", 17 | "senderName": "UnoKenji", 18 | "senderAddressType": "EX", 19 | "senderEmail": "/O=EXCHANGELABS/OU=EXCHANGE ADMINISTRATIVE GROUP (FYDIBOHF23SPDLT)/CN=RECIPIENTS/CN=A17940F5293148CCB36A0455DEAF4F84-KU", 20 | "normalizedSubject": "Sending test", 21 | "body": "Hello.\r\n", 22 | "messageId": "", 23 | "preview": "Hello.", 24 | "lastModifierName": "UnoKenji", 25 | "creatorSMTPAddress": "ku@digitaldolphins.onmicrosoft.com", 26 | "lastModifierSMTPAddress": "ku@digitaldolphins.onmicrosoft.com", 27 | "inetAcctName": "ku@digitaldolphins.onmicrosoft.com", 28 | "creationTime": "Tue, 31 Aug 2021 01:56:30 GMT", 29 | "lastModificationTime": "Tue, 31 Aug 2021 01:56:30 GMT", 30 | "clientSubmitTime": "Tue, 31 Aug 2021 01:56:16 GMT", 31 | "messageDeliveryTime": "Tue, 31 Aug 2021 01:56:00 GMT", 32 | "messageFlags": 1, 33 | "internetCodepage": 20127, 34 | "messageLocaleId": 1041, 35 | "messageCodepage": 1252 36 | } -------------------------------------------------------------------------------- /others/PidLidVerbStream.bt: -------------------------------------------------------------------------------- 1 | //------------------------------------------------ 2 | //--- 010 Editor v11.0.1 Binary Template 3 | // 4 | // File: PidLidVerbStream Property 5 | // Authors: kenjiuno 6 | // Version: 0.1 7 | // Purpose: https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxomsg/89a70cdb-28ca-4d63-9deb-6d8c15c2cb47 8 | // Category: 9 | // File Mask: 10 | // ID Bytes: 02 01 11 | // History: 12 | //------------------------------------------------ 13 | 14 | struct VoteOption { 15 | uint VerbType; 16 | byte DisplayNameCount; 17 | char DisplayName[DisplayNameCount]; 18 | byte MsgClsNameCount; 19 | char MsgClsName[MsgClsNameCount]; 20 | byte Internal1StringCount; 21 | char Internal1String[Internal1StringCount]; 22 | byte DisplayNameCountRepeat; 23 | char DisplayNameRepeat[DisplayNameCountRepeat]; 24 | uint Internal2; 25 | byte Internal3; 26 | uint fUseUSHeaders; 27 | uint Internal4; 28 | uint SendBehavior; 29 | uint Internal5; 30 | uint ID; 31 | int Internal6; 32 | }; 33 | 34 | struct VoteOptionExtra{ 35 | byte DisplayNameCount; 36 | wchar_t DisplayName[DisplayNameCount]; 37 | byte DisplayNameCountRepeat; 38 | wchar_t DisplayNameRepeat[DisplayNameCountRepeat]; 39 | }; 40 | 41 | struct Header { 42 | uint16 Version; 43 | uint16 Count; 44 | uint16 Dummy1; 45 | VoteOption VoteOptions[Count] ; 46 | uint16 Version2; 47 | VoteOptionExtra VoteOptionExtras[Count] ; 48 | }; 49 | 50 | Header header; 51 | -------------------------------------------------------------------------------- /test/attachAndInline.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [ 4 | { 5 | "dataType": "attachment", 6 | "name": "attach.png", 7 | "dataId": 47, 8 | "contentLength": 1558, 9 | "extension": ".png", 10 | "fileNameShort": "attach.png", 11 | "fileName": "attach.png", 12 | "creationTime": "Tue, 27 Jul 2021 22:19:41 GMT", 13 | "lastModificationTime": "Tue, 27 Jul 2021 22:19:41 GMT", 14 | "attachmentHidden": false 15 | }, 16 | { 17 | "dataType": "attachment", 18 | "name": "image001.png", 19 | "dataId": 57, 20 | "contentLength": 809, 21 | "extension": ".png", 22 | "fileNameShort": "image001.png", 23 | "fileName": "image001.png", 24 | "attachMimeTag": "image/png", 25 | "pidContentId": "image001.png@01D78380.EF6DC500", 26 | "attachmentHidden": true 27 | } 28 | ], 29 | "recipients": [ 30 | { 31 | "dataType": "recipient", 32 | "name": "xmailuser@xmailserver.test", 33 | "addressType": "SMTP", 34 | "email": "xmailuser@xmailserver.test", 35 | "recipType": "to" 36 | } 37 | ], 38 | "messageClass": "IPM.Note", 39 | "subject": "Attach and inline", 40 | "conversationTopic": "Attach and inline", 41 | "normalizedSubject": "Attach and inline", 42 | "body": "\r\n \r\n", 43 | "inetAcctName": "xmailuser@xmailserver.test", 44 | "creationTime": "Tue, 27 Jul 2021 22:20:15 GMT", 45 | "lastModificationTime": "Tue, 27 Jul 2021 22:20:15 GMT", 46 | "messageDeliveryTime": "Tue, 27 Jul 2021 22:19:50 GMT", 47 | "messageFlags": 24, 48 | "internetCodepage": 20127, 49 | "messageLocaleId": 1041 50 | } -------------------------------------------------------------------------------- /others/msg.bt: -------------------------------------------------------------------------------- 1 | //------------------------------------------------ 2 | //--- 010 Editor v11.0.1 Binary Template 3 | // 4 | // File: CFB 5 | // Authors: kenjiuno 6 | // Version: 0.1 7 | // Purpose: [MS-CFB]: Compound File Binary File 8 | // Category: Archive 9 | // File Mask: *.msg 10 | // ID Bytes: D0 CF 11 E0 A1 B1 1A E1 11 | // History: 12 | //------------------------------------------------ 13 | 14 | LittleEndian(); 15 | 16 | // See: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-cfb/05060311-bfce-4b12-874d-71fd4ce63aea 17 | 18 | struct Header { 19 | uint64 HeaderSignature; 20 | GUID HeaderCLSID; 21 | ushort MinorVersion; 22 | ushort MajorVersion; 23 | ushort ByteOrder; 24 | ushort SectorShift; 25 | ushort MiniSectorShift; 26 | byte Reserved[6]; 27 | int32 NumberOfDirectorySectors; 28 | int32 NumberOfFATSectors; 29 | int32 FirstDirectorySectorLocation; 30 | int32 TransactionSignatureNumber; 31 | int32 MiniStreamCutoffSize; 32 | int32 FirstMiniFATSectorLocation; 33 | int32 NumberOfMiniFATSectors; 34 | int32 FirstDIFATSectorLocation; 35 | int32 NumberOfDIFATSectors; 36 | int32 DIFAT[109]; 37 | }; 38 | 39 | Header header; 40 | 41 | struct Dir { 42 | wchar_t DirectoryEntryName[32]; 43 | ushort DirectoryEntryNameLength; 44 | byte ObjectType; 45 | byte IsBlack; 46 | int32 LeftSiblingID; 47 | int32 RightSiblingID; 48 | int32 ChildID; 49 | GUID CLSID; 50 | uint32 StateBits; 51 | int64 CreationTime; 52 | int64 ModifiedTime; 53 | int32 StartingSectorLocation; 54 | int64 StreamSize; 55 | }; 56 | 57 | FSeek(512 * (1+header.FirstDirectorySectorLocation)); 58 | Dir dir[4]; 59 | -------------------------------------------------------------------------------- /test/msgInMsg-attachments0.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [], 4 | "recipients": [ 5 | { 6 | "dataType": "recipient", 7 | "name": "xmailuser", 8 | "addressType": "SMTP", 9 | "email": "xmailuser@xmailserver.test", 10 | "recipType": "to" 11 | } 12 | ], 13 | "messageClass": "IPM.Note", 14 | "subject": "Microsoft Outlook テスト メッセージ", 15 | "conversationTopic": "Microsoft Outlook テスト メッセージ", 16 | "headers": "Return-Path: \r\nDelivered-To: xmailuser@xmailserver.test\r\nX-AuthUser: xmailuser@xmailserver.test\r\nReceived: from H270 ([127.0.0.1]:56695)\r\n\tby xmailserver.test with [XMail 1.27 ESMTP Server]\r\n\tid for from ;\r\n\tTue, 12 May 2020 14:45:17 +0900\r\nFrom: Microsoft Outlook \r\nTo: =?utf-8?B?eG1haWx1c2Vy?= \r\nSubject: =?utf-8?B?TWljcm9zb2Z0IE91dGxvb2sg44OG44K544OIIOODoeODg+OCu+ODvOOCuA==?=\r\nMIME-Version: 1.0\r\nContent-Type: text/html;\r\n charset=\"utf-8\"\r\nContent-Transfer-Encoding: 8bit\r\n\r\n", 17 | "senderName": "Microsoft Outlook", 18 | "senderAddressType": "SMTP", 19 | "senderEmail": "xmailuser@xmailserver.test", 20 | "normalizedSubject": "Microsoft Outlook テスト メッセージ", 21 | "body": "この電子メール メッセージは、アカウントの設定のテスト中に、Microsoft Outlook から自動送信されたものです。 \r\n", 22 | "inetAcctName": "xmailuser@xmailserver.test", 23 | "creationTime": "Thu, 10 Sep 2020 10:18:11 GMT", 24 | "lastModificationTime": "Thu, 10 Sep 2020 10:18:11 GMT", 25 | "messageDeliveryTime": "Tue, 12 May 2020 05:45:17 GMT", 26 | "messageFlags": 1, 27 | "internetCodepage": 65001 28 | } -------------------------------------------------------------------------------- /test/msgInMsgInMsg-attachments0-attachments0.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [], 4 | "recipients": [ 5 | { 6 | "dataType": "recipient", 7 | "name": "xmailuser", 8 | "addressType": "SMTP", 9 | "email": "xmailuser@xmailserver.test", 10 | "recipType": "to" 11 | } 12 | ], 13 | "messageClass": "IPM.Note", 14 | "subject": "Microsoft Outlook テスト メッセージ", 15 | "conversationTopic": "Microsoft Outlook テスト メッセージ", 16 | "headers": "Return-Path: \r\nDelivered-To: xmailuser@xmailserver.test\r\nX-AuthUser: xmailuser@xmailserver.test\r\nReceived: from H270 ([127.0.0.1]:56695)\r\n\tby xmailserver.test with [XMail 1.27 ESMTP Server]\r\n\tid for from ;\r\n\tTue, 12 May 2020 14:45:17 +0900\r\nFrom: Microsoft Outlook \r\nTo: =?utf-8?B?eG1haWx1c2Vy?= \r\nSubject: =?utf-8?B?TWljcm9zb2Z0IE91dGxvb2sg44OG44K544OIIOODoeODg+OCu+ODvOOCuA==?=\r\nMIME-Version: 1.0\r\nContent-Type: text/html;\r\n charset=\"utf-8\"\r\nContent-Transfer-Encoding: 8bit\r\n\r\n", 17 | "senderName": "Microsoft Outlook", 18 | "senderAddressType": "SMTP", 19 | "senderEmail": "xmailuser@xmailserver.test", 20 | "normalizedSubject": "Microsoft Outlook テスト メッセージ", 21 | "body": "この電子メール メッセージは、アカウントの設定のテスト中に、Microsoft Outlook から自動送信されたものです。 \r\n", 22 | "inetAcctName": "xmailuser@xmailserver.test", 23 | "creationTime": "Thu, 10 Sep 2020 10:21:42 GMT", 24 | "lastModificationTime": "Thu, 10 Sep 2020 10:21:42 GMT", 25 | "messageDeliveryTime": "Tue, 12 May 2020 05:45:17 GMT", 26 | "messageFlags": 1, 27 | "internetCodepage": 65001 28 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/node 2 | # Edit at https://www.gitignore.io/?templates=node 3 | 4 | ### Node ### 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | 24 | # nyc test coverage 25 | .nyc_output 26 | 27 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 28 | .grunt 29 | 30 | # Bower dependency directory (https://bower.io/) 31 | bower_components 32 | 33 | # node-waf configuration 34 | .lock-wscript 35 | 36 | # Compiled binary addons (https://nodejs.org/api/addons.html) 37 | build/Release 38 | 39 | # Dependency directories 40 | node_modules/ 41 | jspm_packages/ 42 | 43 | # TypeScript v1 declaration files 44 | typings/ 45 | 46 | # Optional npm cache directory 47 | .npm 48 | 49 | # Optional eslint cache 50 | .eslintcache 51 | 52 | # Optional REPL history 53 | .node_repl_history 54 | 55 | # Output of 'npm pack' 56 | *.tgz 57 | 58 | # Yarn Integrity file 59 | .yarn-integrity 60 | 61 | # dotenv environment variables file 62 | .env 63 | .env.test 64 | 65 | # parcel-bundler cache (https://parceljs.org/) 66 | .cache 67 | 68 | # next.js build output 69 | .next 70 | 71 | # nuxt.js build output 72 | .nuxt 73 | 74 | # vuepress build output 75 | .vuepress/dist 76 | 77 | # Serverless directories 78 | .serverless/ 79 | 80 | # FuseBox cache 81 | .fusebox/ 82 | 83 | # DynamoDB Local files 84 | .dynamodb/ 85 | 86 | # End of https://www.gitignore.io/api/node 87 | 88 | /lib 89 | /out 90 | -------------------------------------------------------------------------------- /test/attachmentsOrder.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [ 4 | { 5 | "dataType": "attachment", 6 | "name": "A.docx", 7 | "dataId": 33, 8 | "contentLength": 11627, 9 | "extension": ".docx", 10 | "fileNameShort": "A774F~1.DOC", 11 | "fileName": "A.docx", 12 | "creationTime": "Wed, 06 Aug 2025 11:07:27 GMT", 13 | "lastModificationTime": "Wed, 06 Aug 2025 11:07:28 GMT", 14 | "attachmentHidden": false 15 | }, 16 | { 17 | "dataType": "attachment", 18 | "name": "B.docx", 19 | "dataId": 43, 20 | "contentLength": 11631, 21 | "extension": ".docx", 22 | "fileNameShort": "B72F6~1.DOC", 23 | "fileName": "B.docx", 24 | "creationTime": "Wed, 06 Aug 2025 11:08:00 GMT", 25 | "lastModificationTime": "Wed, 06 Aug 2025 11:08:01 GMT", 26 | "attachmentHidden": false 27 | }, 28 | { 29 | "dataType": "attachment", 30 | "name": "C.docx", 31 | "dataId": 53, 32 | "contentLength": 11753, 33 | "extension": ".docx", 34 | "fileNameShort": "CB128~1.DOC", 35 | "fileName": "C.docx", 36 | "creationTime": "Wed, 06 Aug 2025 11:08:07 GMT", 37 | "lastModificationTime": "Wed, 06 Aug 2025 11:08:08 GMT", 38 | "attachmentHidden": false 39 | }, 40 | { 41 | "dataType": "attachment", 42 | "name": "D.docx", 43 | "dataId": 63, 44 | "contentLength": 11762, 45 | "extension": ".docx", 46 | "fileNameShort": "D598F~1.DOC", 47 | "fileName": "D.docx", 48 | "creationTime": "Wed, 06 Aug 2025 11:08:18 GMT", 49 | "lastModificationTime": "Wed, 06 Aug 2025 11:08:19 GMT", 50 | "attachmentHidden": false 51 | } 52 | ], 53 | "recipients": [], 54 | "messageClass": "IPM.Note", 55 | "subject": "Attachments order", 56 | "conversationTopic": "Attachments order", 57 | "normalizedSubject": "Attachments order", 58 | "body": "Attachments order\r\n", 59 | "creationTime": "Wed, 06 Aug 2025 11:08:37 GMT", 60 | "lastModificationTime": "Wed, 06 Aug 2025 11:08:37 GMT", 61 | "messageDeliveryTime": "Wed, 06 Aug 2025 11:08:27 GMT", 62 | "messageFlags": 24, 63 | "internetCodepage": 20127, 64 | "messageLocaleId": 1041 65 | } -------------------------------------------------------------------------------- /test/voteItems.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [], 4 | "recipients": [ 5 | { 6 | "dataType": "recipient", 7 | "name": "xmailuser@xmailserver.test", 8 | "addressType": "SMTP", 9 | "email": "xmailuser@xmailserver.test", 10 | "recipType": "to" 11 | } 12 | ], 13 | "messageClass": "IPM.Note", 14 | "subject": "3 vote items: はい;いいえ;たぶん", 15 | "conversationTopic": "3 vote items: はい;いいえ;たぶん", 16 | "headers": "Return-Path: \r\nDelivered-To: xmailuser@xmailserver.test\r\nX-AuthUser: xmailuser@xmailserver.test\r\nReceived: from H270 ([127.0.0.1]:51819)\r\n\tby xmailserver.test with [XMail 1.27 ESMTP Server]\r\n\tid for from ;\r\n\tThu, 29 Jul 2021 19:57:30 +0900\r\nFrom: \"xmailuser\" \r\nTo: \r\nSubject: =?iso-2022-jp?B?MyB2b3RlIGl0ZW1zOiAbJEIkTyQkGyhCOxskQiQkJCQkKBsoQjs=?=\r\n\t=?iso-2022-jp?B?GyRCJD8kViRzGyhC?=\r\nDate: Thu, 29 Jul 2021 19:57:30 +0900\r\nMessage-ID: \r\nMIME-Version: 1.0\r\nContent-Type: multipart/mixed;\r\n\tboundary=\"----=_NextPart_000_0004_01D784B3.F712B610\"\r\nX-Mailer: Microsoft Outlook 15.0\r\nContent-Language: ja\r\nThread-Index: AdeEaIQ+lL4xXQlpRZG40M0tYNz0ww==\r\nX-MS-TNEF-Correlator: 00000000C86F779786E2ED45AD1235DD950AA43D84072000\r\n\r\n", 17 | "senderName": "xmailuser", 18 | "senderAddressType": "SMTP", 19 | "senderEmail": "xmailuser@xmailserver.test", 20 | "normalizedSubject": "3 vote items: はい;いいえ;たぶん", 21 | "body": " \r\n\r\n", 22 | "messageId": "", 23 | "inetAcctName": "xmailuser@xmailserver.test", 24 | "votingOptions": "はい;いいえ;たぶん", 25 | "creationTime": "Thu, 29 Jul 2021 10:58:01 GMT", 26 | "lastModificationTime": "Thu, 29 Jul 2021 10:58:01 GMT", 27 | "clientSubmitTime": "Thu, 29 Jul 2021 10:57:30 GMT", 28 | "messageDeliveryTime": "Thu, 29 Jul 2021 10:57:30 GMT", 29 | "messageFlags": 65537, 30 | "internetCodepage": 50220, 31 | "messageLocaleId": 1041 32 | } -------------------------------------------------------------------------------- /test/voteNo.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [], 4 | "recipients": [ 5 | { 6 | "dataType": "recipient", 7 | "name": "'xmailuser'", 8 | "addressType": "SMTP", 9 | "email": "xmailuser@xmailserver.test", 10 | "recipType": "to" 11 | } 12 | ], 13 | "messageClass": "IPM.Note", 14 | "subject": "いいえ: 3 vote items: はい;いいえ;たぶん", 15 | "conversationTopic": "3 vote items: はい;いいえ;たぶん", 16 | "headers": "Return-Path: \r\nDelivered-To: xmailuser@xmailserver.test\r\nX-AuthUser: xmailuser@xmailserver.test\r\nReceived: from H270 ([127.0.0.1]:54153)\r\n\tby xmailserver.test with [XMail 1.27 ESMTP Server]\r\n\tid for from ;\r\n\tThu, 29 Jul 2021 19:57:50 +0900\r\nFrom: \"xmailuser\" \r\nTo: \"'xmailuser'\" \r\nSubject: =?iso-2022-jp?B?GyRCJCQkJCQoGyhCOiAzIHZvdGUgaXRlbXM6IBskQiRPJCQbKEI=?=\r\n\t=?iso-2022-jp?B?OxskQiQkJCQkKBsoQjsbJEIkPyRWJHMbKEI=?=\r\nDate: Thu, 29 Jul 2021 19:57:50 +0900\r\nMessage-ID: \r\nMIME-Version: 1.0\r\nContent-Type: application/ms-tnef;\r\n\tname=\"winmail.dat\"\r\nContent-Transfer-Encoding: base64\r\nContent-Disposition: attachment;\r\n\tfilename=\"winmail.dat\"\r\nX-Mailer: Microsoft Outlook 15.0\r\nContent-Language: ja\r\nThread-Index: AdeEaIQ+lL4xXQlpRZG40M0tYNz0wwAAA5CQ\r\nX-MS-TNEF-Correlator: 00000000C86F779786E2ED45AD1235DD950AA43DE4072000\r\n\r\n", 17 | "senderName": "xmailuser", 18 | "senderAddressType": "SMTP", 19 | "senderEmail": "xmailuser@xmailserver.test", 20 | "normalizedSubject": "3 vote items: はい;いいえ;たぶん", 21 | "messageId": "", 22 | "inetAcctName": "xmailuser@xmailserver.test", 23 | "votingResponse": "いいえ", 24 | "creationTime": "Thu, 29 Jul 2021 10:58:27 GMT", 25 | "lastModificationTime": "Thu, 29 Jul 2021 10:58:27 GMT", 26 | "clientSubmitTime": "Thu, 29 Jul 2021 10:57:50 GMT", 27 | "messageDeliveryTime": "Thu, 29 Jul 2021 10:57:50 GMT", 28 | "messageFlags": 65537, 29 | "internetCodepage": 50220, 30 | "messageLocaleId": 1041 31 | } -------------------------------------------------------------------------------- /test/voteYes.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [], 4 | "recipients": [ 5 | { 6 | "dataType": "recipient", 7 | "name": "'xmailuser'", 8 | "addressType": "SMTP", 9 | "email": "xmailuser@xmailserver.test", 10 | "recipType": "to" 11 | } 12 | ], 13 | "messageClass": "IPM.Note", 14 | "subject": "はい: 3 vote items: はい;いいえ;たぶん", 15 | "conversationTopic": "3 vote items: はい;いいえ;たぶん", 16 | "headers": "Return-Path: \r\nDelivered-To: xmailuser@xmailserver.test\r\nX-AuthUser: xmailuser@xmailserver.test\r\nReceived: from H270 ([127.0.0.1]:54153)\r\n\tby xmailserver.test with [XMail 1.27 ESMTP Server]\r\n\tid for from ;\r\n\tThu, 29 Jul 2021 19:57:47 +0900\r\nFrom: \"xmailuser\" \r\nTo: \"'xmailuser'\" \r\nSubject: =?iso-2022-jp?B?GyRCJE8kJBsoQjogMyB2b3RlIGl0ZW1zOiAbJEIkTyQkGyhCOw==?=\r\n\t=?iso-2022-jp?B?GyRCJCQkJCQoGyhCOxskQiQ/JFYkcxsoQg==?=\r\nDate: Thu, 29 Jul 2021 19:57:47 +0900\r\nMessage-ID: \r\nMIME-Version: 1.0\r\nContent-Type: application/ms-tnef;\r\n\tname=\"winmail.dat\"\r\nContent-Transfer-Encoding: base64\r\nContent-Disposition: attachment;\r\n\tfilename=\"winmail.dat\"\r\nX-Mailer: Microsoft Outlook 15.0\r\nContent-Language: ja\r\nThread-Index: AdeEaIQ+lL4xXQlpRZG40M0tYNz0wwAAAy8A\r\nX-MS-TNEF-Correlator: 00000000C86F779786E2ED45AD1235DD950AA43DC4072000\r\n\r\n", 17 | "senderName": "xmailuser", 18 | "senderAddressType": "SMTP", 19 | "senderEmail": "xmailuser@xmailserver.test", 20 | "normalizedSubject": "3 vote items: はい;いいえ;たぶん", 21 | "messageId": "", 22 | "inetAcctName": "xmailuser@xmailserver.test", 23 | "votingResponse": "はい", 24 | "creationTime": "Thu, 29 Jul 2021 10:58:13 GMT", 25 | "lastModificationTime": "Thu, 29 Jul 2021 10:58:13 GMT", 26 | "clientSubmitTime": "Thu, 29 Jul 2021 10:57:47 GMT", 27 | "messageDeliveryTime": "Thu, 29 Jul 2021 10:57:47 GMT", 28 | "messageFlags": 65537, 29 | "internetCodepage": 50220, 30 | "messageLocaleId": 1041 31 | } -------------------------------------------------------------------------------- /test/attachmentFiles.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [ 4 | { 5 | "dataType": "attachment", 6 | "dataId": 58, 7 | "contentLength": 726, 8 | "fileNameShort": "jpg.jpg", 9 | "fileName": "jpg.jpg", 10 | "attachMimeTag": "image/jpeg" 11 | }, 12 | { 13 | "dataType": "attachment", 14 | "dataId": 66, 15 | "contentLength": 134, 16 | "fileNameShort": "png.png", 17 | "fileName": "png.png", 18 | "attachMimeTag": "image/png" 19 | }, 20 | { 21 | "dataType": "attachment", 22 | "dataId": 74, 23 | "contentLength": 664, 24 | "fileNameShort": "tif.tif", 25 | "fileName": "tif.tif", 26 | "attachMimeTag": "image/tiff" 27 | } 28 | ], 29 | "recipients": [ 30 | { 31 | "dataType": "recipient", 32 | "name": "hmailuser@hmailserver.test", 33 | "addressType": "SMTP", 34 | "email": "hmailuser@hmailserver.test", 35 | "recipType": "to" 36 | } 37 | ], 38 | "messageClass": "IPM.Note", 39 | "subject": "attachmentFiles", 40 | "conversationTopic": "attachmentFiles", 41 | "headers": "Return-Path: hmailuser@hmailserver.test\r\nReceived: from H270 (kubernetes.docker.internal [127.0.0.1])\r\n\tby H270 with ESMTPA\r\n\t; Wed, 1 Nov 2023 09:48:31 +0900\r\nFrom: \"hmailuser\" \r\nTo: \r\nSubject: attachmentFiles\r\nDate: Wed, 1 Nov 2023 09:48:31 +0900\r\nMessage-ID: <000001da0c5d$22ab1460$68013d20$@hmailserver.test>\r\nMIME-Version: 1.0\r\nContent-Type: multipart/mixed;\r\n\tboundary=\"----=_NextPart_000_0001_01DA0CA8.92933190\"\r\nX-Mailer: Microsoft Outlook 15.0\r\nThread-Index: AdoMXRSNJVJU6zGuSsKW/eBdqjoYLA==\r\nContent-Language: ja\r\n\r\n", 42 | "senderName": "hmailuser", 43 | "senderAddressType": "SMTP", 44 | "senderEmail": "hmailuser@hmailserver.test", 45 | "normalizedSubject": "attachmentFiles", 46 | "body": "attachmentFiles\r\n\r\n", 47 | "messageId": "<000001da0c5d$22ab1460$68013d20$@hmailserver.test>", 48 | "inetAcctName": "hmailuser@hmailserver.test", 49 | "creationTime": "Wed, 01 Nov 2023 00:52:39 GMT", 50 | "lastModificationTime": "Wed, 01 Nov 2023 00:52:39 GMT", 51 | "clientSubmitTime": "Wed, 01 Nov 2023 00:48:31 GMT", 52 | "messageDeliveryTime": "Wed, 01 Nov 2023 00:48:31 GMT", 53 | "messageFlags": 65554, 54 | "internetCodepage": 20127, 55 | "messageLocaleId": 1041 56 | } -------------------------------------------------------------------------------- /test/A black friday (w tz).json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [], 4 | "recipients": [], 5 | "messageClass": "IPM.Appointment", 6 | "subject": "A black friday", 7 | "conversationTopic": "A black friday", 8 | "senderName": "Unknown", 9 | "senderAddressType": "UNKNOWN", 10 | "senderEmail": "Unknown", 11 | "normalizedSubject": "A black friday", 12 | "body": "Dec 02, 2012\r\n", 13 | "apptLocation": "", 14 | "apptTZDefStartDisplay": { 15 | "rules": [ 16 | { 17 | "flags": 2, 18 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 19 | "bias": -540, 20 | "standardBias": 0, 21 | "daylightBias": -60, 22 | "standardDate": { 23 | "year": 0, 24 | "month": 0, 25 | "dayOfWeek": 0, 26 | "day": 0, 27 | "hour": 0, 28 | "minute": 0 29 | }, 30 | "daylightDate": { 31 | "year": 0, 32 | "month": 0, 33 | "dayOfWeek": 0, 34 | "day": 0, 35 | "hour": 0, 36 | "minute": 0 37 | } 38 | } 39 | ], 40 | "keyName": "Tokyo Standard Time" 41 | }, 42 | "timeZoneDesc": "(UTC+09:00) 大阪、札幌、東京", 43 | "apptTZDefEndDisplay": { 44 | "rules": [ 45 | { 46 | "flags": 2, 47 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 48 | "bias": -540, 49 | "standardBias": 0, 50 | "daylightBias": -60, 51 | "standardDate": { 52 | "year": 0, 53 | "month": 0, 54 | "dayOfWeek": 0, 55 | "day": 0, 56 | "hour": 0, 57 | "minute": 0 58 | }, 59 | "daylightDate": { 60 | "year": 0, 61 | "month": 0, 62 | "dayOfWeek": 0, 63 | "day": 0, 64 | "hour": 0, 65 | "minute": 0 66 | } 67 | } 68 | ], 69 | "keyName": "Tokyo Standard Time" 70 | }, 71 | "globalAppointmentID": "040000008200E00074C5B7101A82E0080000000090A4F9F86B05D90100000000000000001000000057BD115DD6EE3A48AB219BD1FD41A570", 72 | "creationTime": "Thu, 01 Dec 2022 01:04:00 GMT", 73 | "lastModificationTime": "Thu, 01 Dec 2022 01:04:00 GMT", 74 | "clientSubmitTime": "Thu, 01 Dec 2022 01:02:43 GMT", 75 | "messageDeliveryTime": "Thu, 01 Dec 2022 01:02:43 GMT", 76 | "messageFlags": 1, 77 | "internetCodepage": 50220, 78 | "messageLocaleId": 1041, 79 | "apptStartWhole": "Thu, 01 Dec 2022 15:00:00 GMT", 80 | "apptEndWhole": "Fri, 02 Dec 2022 15:00:00 GMT", 81 | "clipStart": "Thu, 01 Dec 2022 15:00:00 GMT", 82 | "clipEnd": "Fri, 02 Dec 2022 15:00:00 GMT" 83 | } -------------------------------------------------------------------------------- /test/A black friday (wo tz).json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [], 4 | "recipients": [], 5 | "messageClass": "IPM.Appointment", 6 | "subject": "A black friday", 7 | "conversationTopic": "A black friday", 8 | "senderName": "Unknown", 9 | "senderAddressType": "UNKNOWN", 10 | "senderEmail": "Unknown", 11 | "normalizedSubject": "A black friday", 12 | "body": "Dec 02, 2012\r\n", 13 | "apptLocation": "", 14 | "apptTZDefStartDisplay": { 15 | "rules": [ 16 | { 17 | "flags": 2, 18 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 19 | "bias": -540, 20 | "standardBias": 0, 21 | "daylightBias": -60, 22 | "standardDate": { 23 | "year": 0, 24 | "month": 0, 25 | "dayOfWeek": 0, 26 | "day": 0, 27 | "hour": 0, 28 | "minute": 0 29 | }, 30 | "daylightDate": { 31 | "year": 0, 32 | "month": 0, 33 | "dayOfWeek": 0, 34 | "day": 0, 35 | "hour": 0, 36 | "minute": 0 37 | } 38 | } 39 | ], 40 | "keyName": "Tokyo Standard Time" 41 | }, 42 | "timeZoneDesc": "(UTC+09:00) 大阪、札幌、東京", 43 | "apptTZDefEndDisplay": { 44 | "rules": [ 45 | { 46 | "flags": 2, 47 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 48 | "bias": -540, 49 | "standardBias": 0, 50 | "daylightBias": -60, 51 | "standardDate": { 52 | "year": 0, 53 | "month": 0, 54 | "dayOfWeek": 0, 55 | "day": 0, 56 | "hour": 0, 57 | "minute": 0 58 | }, 59 | "daylightDate": { 60 | "year": 0, 61 | "month": 0, 62 | "dayOfWeek": 0, 63 | "day": 0, 64 | "hour": 0, 65 | "minute": 0 66 | } 67 | } 68 | ], 69 | "keyName": "Tokyo Standard Time" 70 | }, 71 | "globalAppointmentID": "040000008200E00074C5B7101A82E0080000000090A4F9F86B05D90100000000000000001000000057BD115DD6EE3A48AB219BD1FD41A570", 72 | "creationTime": "Thu, 01 Dec 2022 01:03:06 GMT", 73 | "lastModificationTime": "Thu, 01 Dec 2022 01:03:06 GMT", 74 | "clientSubmitTime": "Thu, 01 Dec 2022 01:02:43 GMT", 75 | "messageDeliveryTime": "Thu, 01 Dec 2022 01:02:43 GMT", 76 | "messageFlags": 1, 77 | "internetCodepage": 50220, 78 | "messageLocaleId": 1041, 79 | "apptStartWhole": "Thu, 01 Dec 2022 15:00:00 GMT", 80 | "apptEndWhole": "Fri, 02 Dec 2022 15:00:00 GMT", 81 | "clipStart": "Thu, 01 Dec 2022 15:00:00 GMT", 82 | "clipEnd": "Fri, 02 Dec 2022 15:00:00 GMT" 83 | } -------------------------------------------------------------------------------- /test/contactUnicode.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [], 4 | "recipients": [], 5 | "messageClass": "IPM.Contact", 6 | "subject": "コム ドット イグザンプル 殿", 7 | "conversationTopic": "コム ドット イグザンプル 殿", 8 | "senderName": "xmailuser", 9 | "senderAddressType": "SMTP", 10 | "senderEmail": "xmailuser@xmailserver.test", 11 | "normalizedSubject": "コム ドット イグザンプル 殿", 12 | "body": "\r\n", 13 | "name": "Mr. イグザンプル ドット コム 殿", 14 | "generation": "殿", 15 | "givenName": "コム", 16 | "businessTelephoneNumber": "06-0001-0002", 17 | "homeTelephoneNumber": "06-0001-0001", 18 | "location": "", 19 | "surname": "イグザンプル", 20 | "postalAddress": "544-0001\r\nOsaka\r\nOsaka\r\nSomewhere", 21 | "companyName": "My work company", 22 | "title": "My position", 23 | "departmentName": "My division", 24 | "mobileTelephoneNumber": "080-0001-0001", 25 | "businessFaxNumber": "06-0001-0003", 26 | "country": "日本", 27 | "addressCity": "Osaka", 28 | "stateOrProvince": "Osaka", 29 | "streetAddress": "Somewhere", 30 | "postalCode": "544-0001", 31 | "middleName": "ドット", 32 | "displayNamePrefix": "Mr.", 33 | "businessHomePage": "https://example.com", 34 | "email1DisplayName": "コム ドット イグザンプル 殿 (test@example.com)", 35 | "email1EmailAddress": "test@example.com", 36 | "email1OriginalDisplayName": "test@example.com", 37 | "instMsg": "", 38 | "fileUnder": "イグザンプル, コム ドット", 39 | "yomiFirstName": "komu", 40 | "yomiLastName": "eguzanpuru", 41 | "yomiCompanyName": "My work placecompany", 42 | "workAddress": "544-0001\r\nOsaka\r\nOsaka\r\nSomewhere", 43 | "department": "", 44 | "addressCountryCode": "JP", 45 | "fax1AddrType": "FAX", 46 | "fax1EmailAddress": "", 47 | "fax1OriginalDisplayName": "", 48 | "fax2AddrType": "FAX", 49 | "fax2EmailAddress": "コム ドット イグザンプル 殿@+81 06-0001-0003", 50 | "fax2OriginalDisplayName": "コム ドット イグザンプル 殿", 51 | "fax3AddrType": "FAX", 52 | "fax3EmailAddress": "", 53 | "fax3OriginalDisplayName": "", 54 | "workAddressStreet": "Somewhere", 55 | "workAddressCity": "Osaka", 56 | "workAddressState": "Osaka", 57 | "workAddressPostalCode": "544-0001", 58 | "workAddressCountry": "日本", 59 | "workAddressCountryCode": "JP", 60 | "contactHtml": "https://example.com", 61 | "creationTime": "Wed, 20 Jul 2022 23:58:09 GMT", 62 | "lastModificationTime": "Wed, 20 Jul 2022 23:58:09 GMT", 63 | "clientSubmitTime": "Wed, 20 Jul 2022 17:36:38 GMT", 64 | "messageDeliveryTime": "Wed, 20 Jul 2022 17:36:38 GMT", 65 | "messageFlags": 1, 66 | "internetCodepage": 50220, 67 | "messageLocaleId": 1041 68 | } -------------------------------------------------------------------------------- /src/VerbStreamParser.ts: -------------------------------------------------------------------------------- 1 | import DataStream from "./DataStream"; 2 | 3 | /** 4 | * @internal 5 | */ 6 | export function parse(ds: DataStream): string { 7 | // 2.2.1.74.1 VoteOption Structure 8 | // https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxomsg/87488eff-3eec-4502-bc94-2368c04e3109 9 | 10 | const items: { VerbType: number, DisplayName: string }[] = []; 11 | let count = 0; 12 | while (!ds.isEof()) { 13 | const version = ds.readUint16(); 14 | if (version === 258) { 15 | count = ds.readUint16(); 16 | const dummy1 = ds.readUint16(); 17 | for (let index = 0; index < count; index += 1) { 18 | const VerbType = ds.readInt32(); 19 | const DisplayNameCount = ds.readUint8(); 20 | const DisplayName = ds.readString(DisplayNameCount); 21 | const MsgClsNameCount = ds.readUint8(); 22 | const MsgClsName = ds.readString(MsgClsNameCount); 23 | const Internal1StringCount = ds.readUint8(); 24 | const Internal1String = ds.readString(Internal1StringCount); 25 | const DisplayNameCountRepeat = ds.readUint8(); 26 | const DisplayNameRepeat = ds.readString(DisplayNameCountRepeat); 27 | const Internal2 = ds.readInt32(); 28 | const Internal3 = ds.readUint8(); 29 | const fUseUSHeaders = ds.readInt32(); 30 | const Internal4 = ds.readInt32(); 31 | const SendBehavior = ds.readInt32(); 32 | const Internal5 = ds.readInt32(); 33 | const ID = ds.readInt32(); 34 | const Internal6 = ds.readInt32(); 35 | 36 | items.push({ VerbType, DisplayName }); 37 | } 38 | } 39 | else if (version === 260) { 40 | for (let index = 0; index < count; index += 1) { 41 | const DisplayNameCount = ds.readUint8(); 42 | const DisplayName = ds.readUCS2String(DisplayNameCount); 43 | const DisplayNameCountRepeat = ds.readUint8(); 44 | const DisplayNameRepeat = ds.readUCS2String(DisplayNameCountRepeat); 45 | 46 | items[index].DisplayName = DisplayName; 47 | } 48 | } 49 | } 50 | 51 | return items 52 | .filter(it => it.VerbType === 4) 53 | .map(it => it.DisplayName) 54 | .join(";"); 55 | } 56 | -------------------------------------------------------------------------------- /test/contactAnsi.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [], 4 | "recipients": [], 5 | "messageClass": "IPM.Contact", 6 | "subject": "ƒRƒ€ ƒhƒbƒg ƒCƒOƒUƒ“ƒvƒ‹ “a", 7 | "conversationTopic": "ƒRƒ€ ƒhƒbƒg ƒCƒOƒUƒ“ƒvƒ‹ “a", 8 | "senderName": "xmailuser", 9 | "senderAddressType": "SMTP", 10 | "senderEmail": "xmailuser@xmailserver.test", 11 | "normalizedSubject": "ƒRƒ€ ƒhƒbƒg ƒCƒOƒUƒ“ƒvƒ‹ “a", 12 | "body": "\r\n", 13 | "name": "Mr. ƒCƒOƒUƒ“ƒvƒ‹ ƒhƒbƒg ƒRƒ€ “a", 14 | "generation": "“a", 15 | "givenName": "ƒRƒ€", 16 | "businessTelephoneNumber": "06-0001-0002", 17 | "homeTelephoneNumber": "06-0001-0001", 18 | "location": "", 19 | "surname": "ƒCƒOƒUƒ“ƒvƒ‹", 20 | "postalAddress": "544-0001\r\nOsaka\r\nOsaka\r\nSomewhere", 21 | "companyName": "My work company", 22 | "title": "My position", 23 | "departmentName": "My division", 24 | "mobileTelephoneNumber": "080-0001-0001", 25 | "businessFaxNumber": "06-0001-0003", 26 | "country": "“ú–{", 27 | "addressCity": "Osaka", 28 | "stateOrProvince": "Osaka", 29 | "streetAddress": "Somewhere", 30 | "postalCode": "544-0001", 31 | "middleName": "ƒhƒbƒg", 32 | "displayNamePrefix": "Mr.", 33 | "businessHomePage": "https://example.com", 34 | "email1DisplayName": "ƒRƒ€ ƒhƒbƒg ƒCƒOƒUƒ“ƒvƒ‹ “a (test@example.com)", 35 | "email1EmailAddress": "test@example.com", 36 | "email1OriginalDisplayName": "test@example.com", 37 | "instMsg": "", 38 | "fileUnder": "ƒCƒOƒUƒ“ƒvƒ‹, ƒRƒ€ ƒhƒbƒg", 39 | "yomiFirstName": "komu", 40 | "yomiLastName": "eguzanpuru", 41 | "yomiCompanyName": "My work placecompany", 42 | "workAddress": "544-0001\r\nOsaka\r\nOsaka\r\nSomewhere", 43 | "department": "", 44 | "addressCountryCode": "JP", 45 | "fax1AddrType": "FAX", 46 | "fax1EmailAddress": "", 47 | "fax1OriginalDisplayName": "", 48 | "fax2AddrType": "FAX", 49 | "fax2EmailAddress": "ƒRƒ€ ƒhƒbƒg ƒCƒOƒUƒ“ƒvƒ‹ “a@+81 06-0001-0003", 50 | "fax2OriginalDisplayName": "ƒRƒ€ ƒhƒbƒg ƒCƒOƒUƒ“ƒvƒ‹ “a", 51 | "fax3AddrType": "FAX", 52 | "fax3EmailAddress": "", 53 | "fax3OriginalDisplayName": "", 54 | "workAddressStreet": "Somewhere", 55 | "workAddressCity": "Osaka", 56 | "workAddressState": "Osaka", 57 | "workAddressPostalCode": "544-0001", 58 | "workAddressCountry": "“ú–{", 59 | "workAddressCountryCode": "JP", 60 | "contactHtml": "https://example.com", 61 | "creationTime": "Wed, 20 Jul 2022 23:58:23 GMT", 62 | "lastModificationTime": "Wed, 20 Jul 2022 23:58:23 GMT", 63 | "clientSubmitTime": "Wed, 20 Jul 2022 17:36:38 GMT", 64 | "messageDeliveryTime": "Wed, 20 Jul 2022 17:36:38 GMT", 65 | "messageFlags": 1, 66 | "internetCodepage": 50220, 67 | "messageLocaleId": 1041 68 | } -------------------------------------------------------------------------------- /test/A schedule.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [], 4 | "recipients": [ 5 | { 6 | "dataType": "recipient", 7 | "name": "UnoKenji", 8 | "addressType": "EX", 9 | "email": "/o=ExchangeLabs/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=a17940f5293148ccb36a0455deaf4f84-ku", 10 | "smtpAddress": "ku@digitaldolphins.onmicrosoft.com", 11 | "recipType": "to" 12 | } 13 | ], 14 | "messageClass": "IPM.Appointment", 15 | "subject": "A schedule", 16 | "conversationTopic": "A schedule", 17 | "senderName": "UnoKenji", 18 | "senderAddressType": "EX", 19 | "senderEmail": "/o=ExchangeLabs/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=a17940f5293148ccb36a0455deaf4f84-ku", 20 | "normalizedSubject": "A schedule", 21 | "body": "A message body.\r\n", 22 | "lastModifierName": "ku@digitaldolphins.onmicrosoft.com", 23 | "apptLocation": "A place", 24 | "globalAppointmentID": "040000008200E00074C5B7101A82E0080000000080E86D1D5EC0D70100000000000000001000000042A83B3C2CADD148B1039F5CCBD1D5B8", 25 | "timeZoneDesc": "(UTC+09:00) 大阪、札幌、東京", 26 | "apptTZDefStartDisplay": { 27 | "rules": [ 28 | { 29 | "flags": 2, 30 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 31 | "bias": -540, 32 | "standardBias": 0, 33 | "daylightBias": -60, 34 | "standardDate": { 35 | "year": 0, 36 | "month": 0, 37 | "dayOfWeek": 0, 38 | "day": 0, 39 | "hour": 0, 40 | "minute": 0 41 | }, 42 | "daylightDate": { 43 | "year": 0, 44 | "month": 0, 45 | "dayOfWeek": 0, 46 | "day": 0, 47 | "hour": 0, 48 | "minute": 0 49 | } 50 | } 51 | ], 52 | "keyName": "Tokyo Standard Time" 53 | }, 54 | "apptTZDefEndDisplay": { 55 | "rules": [ 56 | { 57 | "flags": 2, 58 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 59 | "bias": -540, 60 | "standardBias": 0, 61 | "daylightBias": -60, 62 | "standardDate": { 63 | "year": 0, 64 | "month": 0, 65 | "dayOfWeek": 0, 66 | "day": 0, 67 | "hour": 0, 68 | "minute": 0 69 | }, 70 | "daylightDate": { 71 | "year": 0, 72 | "month": 0, 73 | "dayOfWeek": 0, 74 | "day": 0, 75 | "hour": 0, 76 | "minute": 0 77 | } 78 | } 79 | ], 80 | "keyName": "Tokyo Standard Time" 81 | }, 82 | "inetAcctName": "ku@digitaldolphins.onmicrosoft.com", 83 | "creationTime": "Wed, 13 Oct 2021 09:14:52 GMT", 84 | "lastModificationTime": "Wed, 13 Oct 2021 09:14:52 GMT", 85 | "clientSubmitTime": "Wed, 13 Oct 2021 09:14:08 GMT", 86 | "messageDeliveryTime": "Wed, 13 Oct 2021 09:14:08 GMT", 87 | "messageFlags": 1, 88 | "internetCodepage": 50220, 89 | "messageLocaleId": 1041, 90 | "apptStartWhole": "Wed, 13 Oct 2021 09:30:00 GMT", 91 | "apptEndWhole": "Wed, 13 Oct 2021 10:00:00 GMT", 92 | "clipStart": "Wed, 13 Oct 2021 09:30:00 GMT", 93 | "clipEnd": "Wed, 13 Oct 2021 09:30:00 GMT" 94 | } -------------------------------------------------------------------------------- /src/TZREGParser.ts: -------------------------------------------------------------------------------- 1 | import DataStream from "./DataStream"; 2 | import { TransitionSystemTime } from "./TZDEFINITIONParser"; 3 | import { readSystemTime, readTransitionSystemTime } from "./utils"; 4 | 5 | 6 | /** 7 | * Contains a stream that maps to the persisted format of a TZREG structure, 8 | * which describes the time zone to be used for the start and end time of a 9 | * recurring appointment or meeting request. 10 | * 11 | * @see [PidLidTimeZoneStruct Canonical Property | Microsoft Learn](https://learn.microsoft.com/en-us/office/client-developer/outlook/mapi/pidlidtimezonestruct-canonical-property) 12 | */ 13 | export interface TzReg { 14 | /** 15 | * offset from GMT 16 | * 17 | * Unit: in minutes 18 | * 19 | * e.g. `-540` for JST (TZ=JST-9) 20 | */ 21 | bias: number, 22 | 23 | /** 24 | * offset from bias during standard time 25 | */ 26 | standardBias: number, 27 | 28 | /** 29 | * offset from bias during daylight time 30 | * 31 | * Unit: in minutes 32 | * 33 | * e.g. `-60` for Japanese. In Japan there is no summer time. This value may be generic value over worldwide. 34 | */ 35 | daylightBias: number, 36 | 37 | /** 38 | * matches the stStandardDate's wYear member 39 | */ 40 | standardYear: number; 41 | 42 | /** 43 | * time to switch to standard time 44 | */ 45 | standardDate: TransitionSystemTime, 46 | 47 | /** 48 | * matches the stDaylightDate's wYear field 49 | */ 50 | daylightYear: number; 51 | 52 | /** 53 | * time to switch to daylight time 54 | */ 55 | daylightDate: TransitionSystemTime, 56 | } 57 | 58 | /** 59 | * @internal 60 | */ 61 | export function parse(ds: DataStream): TzReg | null { 62 | // PidLidTimeZoneStruct Canonical Property 63 | // https://learn.microsoft.com/en-us/office/client-developer/outlook/mapi/pidlidtimezonestruct-canonical-property 64 | 65 | if (!ds.isEof()) { 66 | const lBias = ds.readInt32(); 67 | const lStandardBias = ds.readInt32(); 68 | const lDaylightBias = ds.readInt32(); 69 | const wStandardYear = ds.readUint16(); 70 | const stStandardDate = readTransitionSystemTime(ds); 71 | const wDaylightYear = ds.readUint16(); 72 | const stDaylightDate = readTransitionSystemTime(ds); 73 | 74 | return Object.assign( 75 | {}, 76 | { 77 | bias: lBias, 78 | standardBias: lStandardBias, 79 | daylightBias: lDaylightBias, 80 | standardYear: wStandardYear, 81 | standardDate: stStandardDate, 82 | daylightYear: wDaylightYear, 83 | daylightDate: stDaylightDate, 84 | } 85 | ); 86 | } 87 | return null; 88 | } 89 | -------------------------------------------------------------------------------- /test/msgInMsg.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [ 4 | { 5 | "dataType": "attachment", 6 | "innerMsgContentFields": { 7 | "dataType": "msg", 8 | "attachments": [], 9 | "recipients": [ 10 | { 11 | "dataType": "recipient", 12 | "name": "xmailuser", 13 | "addressType": "SMTP", 14 | "email": "xmailuser@xmailserver.test", 15 | "recipType": "to" 16 | } 17 | ], 18 | "messageClass": "IPM.Note", 19 | "subject": "Microsoft Outlook テスト メッセージ", 20 | "conversationTopic": "Microsoft Outlook テスト メッセージ", 21 | "headers": "Return-Path: \r\nDelivered-To: xmailuser@xmailserver.test\r\nX-AuthUser: xmailuser@xmailserver.test\r\nReceived: from H270 ([127.0.0.1]:56695)\r\n\tby xmailserver.test with [XMail 1.27 ESMTP Server]\r\n\tid for from ;\r\n\tTue, 12 May 2020 14:45:17 +0900\r\nFrom: Microsoft Outlook \r\nTo: =?utf-8?B?eG1haWx1c2Vy?= \r\nSubject: =?utf-8?B?TWljcm9zb2Z0IE91dGxvb2sg44OG44K544OIIOODoeODg+OCu+ODvOOCuA==?=\r\nMIME-Version: 1.0\r\nContent-Type: text/html;\r\n charset=\"utf-8\"\r\nContent-Transfer-Encoding: 8bit\r\n\r\n", 22 | "senderName": "Microsoft Outlook", 23 | "senderAddressType": "SMTP", 24 | "senderEmail": "xmailuser@xmailserver.test", 25 | "normalizedSubject": "Microsoft Outlook テスト メッセージ", 26 | "body": "この電子メール メッセージは、アカウントの設定のテスト中に、Microsoft Outlook から自動送信されたものです。 \r\n", 27 | "inetAcctName": "xmailuser@xmailserver.test", 28 | "creationTime": "Thu, 10 Sep 2020 10:18:11 GMT", 29 | "lastModificationTime": "Thu, 10 Sep 2020 10:18:11 GMT", 30 | "messageDeliveryTime": "Tue, 12 May 2020 05:45:17 GMT", 31 | "messageFlags": 1, 32 | "internetCodepage": 65001 33 | }, 34 | "innerMsgContent": true, 35 | "folderId": 38, 36 | "name": "Microsoft Outlook テスト メッセージ", 37 | "attachmentHidden": false 38 | }, 39 | { 40 | "dataType": "attachment", 41 | "name": "green.png", 42 | "dataId": 90, 43 | "contentLength": 134, 44 | "extension": ".png", 45 | "fileNameShort": "green.png", 46 | "fileName": "green.png", 47 | "creationTime": "Thu, 10 Sep 2020 10:17:47 GMT", 48 | "lastModificationTime": "Thu, 10 Sep 2020 10:17:47 GMT", 49 | "attachmentHidden": false 50 | } 51 | ], 52 | "recipients": [], 53 | "messageClass": "IPM.Note", 54 | "subject": "I have attachments!", 55 | "conversationTopic": "I have attachments!", 56 | "normalizedSubject": "I have attachments!", 57 | "body": "I have attachments!\r\n", 58 | "inetAcctName": "xmailuser@xmailserver.test", 59 | "creationTime": "Thu, 10 Sep 2020 10:18:11 GMT", 60 | "lastModificationTime": "Thu, 10 Sep 2020 10:18:11 GMT", 61 | "messageDeliveryTime": "Thu, 10 Sep 2020 10:18:08 GMT", 62 | "messageFlags": 25, 63 | "internetCodepage": 50220, 64 | "messageLocaleId": 1041 65 | } -------------------------------------------------------------------------------- /test/msgInMsgInMsg-attachments0.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [ 4 | { 5 | "dataType": "attachment", 6 | "innerMsgContentFields": { 7 | "dataType": "msg", 8 | "attachments": [], 9 | "recipients": [ 10 | { 11 | "dataType": "recipient", 12 | "name": "xmailuser", 13 | "addressType": "SMTP", 14 | "email": "xmailuser@xmailserver.test", 15 | "recipType": "to" 16 | } 17 | ], 18 | "messageClass": "IPM.Note", 19 | "subject": "Microsoft Outlook テスト メッセージ", 20 | "conversationTopic": "Microsoft Outlook テスト メッセージ", 21 | "headers": "Return-Path: \r\nDelivered-To: xmailuser@xmailserver.test\r\nX-AuthUser: xmailuser@xmailserver.test\r\nReceived: from H270 ([127.0.0.1]:56695)\r\n\tby xmailserver.test with [XMail 1.27 ESMTP Server]\r\n\tid for from ;\r\n\tTue, 12 May 2020 14:45:17 +0900\r\nFrom: Microsoft Outlook \r\nTo: =?utf-8?B?eG1haWx1c2Vy?= \r\nSubject: =?utf-8?B?TWljcm9zb2Z0IE91dGxvb2sg44OG44K544OIIOODoeODg+OCu+ODvOOCuA==?=\r\nMIME-Version: 1.0\r\nContent-Type: text/html;\r\n charset=\"utf-8\"\r\nContent-Transfer-Encoding: 8bit\r\n\r\n", 22 | "senderName": "Microsoft Outlook", 23 | "senderAddressType": "SMTP", 24 | "senderEmail": "xmailuser@xmailserver.test", 25 | "normalizedSubject": "Microsoft Outlook テスト メッセージ", 26 | "body": "この電子メール メッセージは、アカウントの設定のテスト中に、Microsoft Outlook から自動送信されたものです。 \r\n", 27 | "inetAcctName": "xmailuser@xmailserver.test", 28 | "creationTime": "Thu, 10 Sep 2020 10:21:42 GMT", 29 | "lastModificationTime": "Thu, 10 Sep 2020 10:21:42 GMT", 30 | "messageDeliveryTime": "Tue, 12 May 2020 05:45:17 GMT", 31 | "messageFlags": 1, 32 | "internetCodepage": 65001 33 | }, 34 | "innerMsgContent": true, 35 | "folderId": 41, 36 | "name": "Microsoft Outlook テスト メッセージ", 37 | "attachmentHidden": false 38 | }, 39 | { 40 | "dataType": "attachment", 41 | "name": "green.png", 42 | "dataId": 90, 43 | "contentLength": 134, 44 | "extension": ".png", 45 | "fileNameShort": "green.png", 46 | "fileName": "green.png", 47 | "creationTime": "Thu, 10 Sep 2020 10:17:47 GMT", 48 | "lastModificationTime": "Thu, 10 Sep 2020 10:17:47 GMT", 49 | "attachmentHidden": false 50 | } 51 | ], 52 | "recipients": [], 53 | "messageClass": "IPM.Note", 54 | "subject": "I have attachments!", 55 | "conversationTopic": "I have attachments!", 56 | "normalizedSubject": "I have attachments!", 57 | "body": "I have attachments!\r\n", 58 | "inetAcctName": "xmailuser@xmailserver.test", 59 | "creationTime": "Thu, 10 Sep 2020 10:21:42 GMT", 60 | "lastModificationTime": "Thu, 10 Sep 2020 10:21:42 GMT", 61 | "messageDeliveryTime": "Thu, 10 Sep 2020 10:18:08 GMT", 62 | "messageFlags": 25, 63 | "internetCodepage": 50220, 64 | "messageLocaleId": 1041 65 | } -------------------------------------------------------------------------------- /test/Appointment sample EST.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [], 4 | "recipients": [], 5 | "messageClass": "IPM.Appointment", 6 | "subject": "Appointment sample EST", 7 | "conversationTopic": "Appointment sample EST", 8 | "senderName": "Unknown", 9 | "senderAddressType": "UNKNOWN", 10 | "senderEmail": "Unknown", 11 | "normalizedSubject": "Appointment sample EST", 12 | "body": "\r\n", 13 | "apptLocation": "", 14 | "apptTZDefStartDisplay": { 15 | "rules": [ 16 | { 17 | "flags": 2, 18 | "start": "Mon, 01 Jan 2007 00:00:00 GMT", 19 | "bias": 300, 20 | "standardBias": 0, 21 | "daylightBias": -60, 22 | "standardDate": { 23 | "year": 0, 24 | "month": 11, 25 | "dayOfWeek": 0, 26 | "day": 1, 27 | "hour": 2, 28 | "minute": 0 29 | }, 30 | "daylightDate": { 31 | "year": 0, 32 | "month": 3, 33 | "dayOfWeek": 0, 34 | "day": 2, 35 | "hour": 2, 36 | "minute": 0 37 | } 38 | } 39 | ], 40 | "keyName": "Eastern Standard Time" 41 | }, 42 | "timeZoneDesc": "(UTC+09:00) 大阪、札幌、東京", 43 | "apptTZDefEndDisplay": { 44 | "rules": [ 45 | { 46 | "flags": 0, 47 | "start": "Sun, 01 Jan 2006 00:00:00 GMT", 48 | "bias": 300, 49 | "standardBias": 0, 50 | "daylightBias": -60, 51 | "standardDate": { 52 | "year": 0, 53 | "month": 10, 54 | "dayOfWeek": 0, 55 | "day": 5, 56 | "hour": 2, 57 | "minute": 0 58 | }, 59 | "daylightDate": { 60 | "year": 0, 61 | "month": 4, 62 | "dayOfWeek": 0, 63 | "day": 1, 64 | "hour": 2, 65 | "minute": 0 66 | } 67 | }, 68 | { 69 | "flags": 2, 70 | "start": "Mon, 01 Jan 2007 00:00:00 GMT", 71 | "bias": 300, 72 | "standardBias": 0, 73 | "daylightBias": -60, 74 | "standardDate": { 75 | "year": 0, 76 | "month": 11, 77 | "dayOfWeek": 0, 78 | "day": 1, 79 | "hour": 2, 80 | "minute": 0 81 | }, 82 | "daylightDate": { 83 | "year": 0, 84 | "month": 3, 85 | "dayOfWeek": 0, 86 | "day": 2, 87 | "hour": 2, 88 | "minute": 0 89 | } 90 | } 91 | ], 92 | "keyName": "Eastern Standard Time" 93 | }, 94 | "globalAppointmentID": "040000008200E00074C5B7101A82E00800000000900FCFA32907D901000000000000000010000000B33703C253FC254D8AC55471CA0D9ECC", 95 | "creationTime": "Sat, 03 Dec 2022 06:13:05 GMT", 96 | "lastModificationTime": "Sat, 03 Dec 2022 06:13:05 GMT", 97 | "clientSubmitTime": "Sat, 03 Dec 2022 06:12:57 GMT", 98 | "messageDeliveryTime": "Sat, 03 Dec 2022 06:12:57 GMT", 99 | "messageFlags": 1, 100 | "internetCodepage": 50220, 101 | "messageLocaleId": 1041, 102 | "apptStartWhole": "Sun, 04 Dec 2022 13:00:00 GMT", 103 | "apptEndWhole": "Sun, 04 Dec 2022 13:30:00 GMT", 104 | "clipStart": "Sun, 04 Dec 2022 13:00:00 GMT", 105 | "clipEnd": "Sun, 04 Dec 2022 13:30:00 GMT" 106 | } -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "master" ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ "master" ] 20 | 21 | jobs: 22 | analyze: 23 | name: Analyze 24 | runs-on: ubuntu-latest 25 | permissions: 26 | actions: read 27 | contents: read 28 | security-events: write 29 | 30 | strategy: 31 | fail-fast: false 32 | matrix: 33 | language: [ 'javascript' ] 34 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 35 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 36 | 37 | steps: 38 | - name: Checkout repository 39 | uses: actions/checkout@v3 40 | 41 | # Initializes the CodeQL tools for scanning. 42 | - name: Initialize CodeQL 43 | uses: github/codeql-action/init@v2 44 | with: 45 | languages: ${{ matrix.language }} 46 | # If you wish to specify custom queries, you can do so here or in a config file. 47 | # By default, queries listed here will override any specified in a config file. 48 | # Prefix the list here with "+" to use these queries and those in the config file. 49 | 50 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 51 | # queries: security-extended,security-and-quality 52 | 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v2 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 61 | 62 | # If the Autobuild fails above, remove it and uncomment the following three lines. 63 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 64 | 65 | # - run: | 66 | # echo "Run, Build Application using script" 67 | # ./location_of_script_within_repo/buildscript.sh 68 | 69 | - name: Perform CodeQL Analysis 70 | uses: github/codeql-action/analyze@v2 71 | with: 72 | category: "/language:${{matrix.language}}" 73 | -------------------------------------------------------------------------------- /test/sent.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg932\fromhtml1 \fbidis \deff0{\fonttbl 2 | {\f0\fswiss MS PGothic;} 3 | {\f1\fmodern MS Gothic;} 4 | {\f2\fnil\fcharset2 Symbol;} 5 | {\f3\fmodern\fcharset0 Courier New;}} 6 | {\colortbl\red0\green0\blue0;\red5\green99\blue193;} 7 | \uc1\pard\plain\deftab360 \f0\fs24 8 | {\*\htmltag19 } 9 | {\*\htmltag34 } 10 | {\*\htmltag161 } 11 | {\*\htmltag241 } 14 | {\*\htmltag241 } 15 | {\*\htmltag241 } 16 | {\*\htmltag41 } 17 | {\*\htmltag50 }\htmlrtf \lang1041 \htmlrtf0 18 | {\*\htmltag96
}\htmlrtf {\htmlrtf0 19 | {\*\htmltag64

}\htmlrtf {\htmlrtf0 20 | {\*\htmltag148 }\htmlrtf {\lang1033 \htmlrtf0 Test mail 21 | {\*\htmltag244 } 22 | {\*\htmltag252 } 23 | {\*\htmltag156 }\htmlrtf }\htmlrtf0 \htmlrtf\par}\htmlrtf0 24 | \htmlrtf \par 25 | \htmlrtf0 26 | {\*\htmltag72

} 27 | {\*\htmltag104
}\htmlrtf }\htmlrtf0 28 | {\*\htmltag58 } 29 | {\*\htmltag27 }} -------------------------------------------------------------------------------- /test/voteItems.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg932\fromhtml1 \fbidis \deff0{\fonttbl 2 | {\f0\fswiss\fcharset128 MS PGothic;} 3 | {\f1\fmodern MS Gothic;} 4 | {\f2\fnil\fcharset2 Symbol;} 5 | {\f3\fmodern\fcharset0 Courier New;}} 6 | {\colortbl\red0\green0\blue0;\red5\green99\blue193;} 7 | \uc1\pard\plain\deftab360 \f0\fs24 8 | {\*\htmltag19 } 9 | {\*\htmltag34 } 10 | {\*\htmltag161 } 11 | {\*\htmltag241 } 14 | {\*\htmltag241 } 15 | {\*\htmltag241 } 16 | {\*\htmltag41 } 17 | {\*\htmltag50 }\htmlrtf \lang1041 \htmlrtf0 18 | {\*\htmltag96
}\htmlrtf {\htmlrtf0 19 | {\*\htmltag64

}\htmlrtf {\htmlrtf0 20 | {\*\htmltag148 }\htmlrtf {\lang1033 \htmlrtf0 21 | {\*\htmltag244 } 22 | {\*\htmltag84  }\htmlrtf {\f3\'a0}\htmlrtf0 23 | {\*\htmltag252 } 24 | {\*\htmltag156 }\htmlrtf }\htmlrtf0 \htmlrtf\par}\htmlrtf0 25 | \htmlrtf \par 26 | \htmlrtf0 27 | {\*\htmltag72

} 28 | {\*\htmltag104
}\htmlrtf }\htmlrtf0 29 | {\*\htmltag58 } 30 | {\*\htmltag27 }} -------------------------------------------------------------------------------- /test/msgInMsgInMsg.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [ 4 | { 5 | "dataType": "attachment", 6 | "innerMsgContentFields": { 7 | "dataType": "msg", 8 | "attachments": [ 9 | { 10 | "dataType": "attachment", 11 | "innerMsgContentFields": { 12 | "dataType": "msg", 13 | "attachments": [], 14 | "recipients": [ 15 | { 16 | "dataType": "recipient", 17 | "name": "xmailuser", 18 | "addressType": "SMTP", 19 | "email": "xmailuser@xmailserver.test", 20 | "recipType": "to" 21 | } 22 | ], 23 | "messageClass": "IPM.Note", 24 | "subject": "Microsoft Outlook テスト メッセージ", 25 | "conversationTopic": "Microsoft Outlook テスト メッセージ", 26 | "headers": "Return-Path: \r\nDelivered-To: xmailuser@xmailserver.test\r\nX-AuthUser: xmailuser@xmailserver.test\r\nReceived: from H270 ([127.0.0.1]:56695)\r\n\tby xmailserver.test with [XMail 1.27 ESMTP Server]\r\n\tid for from ;\r\n\tTue, 12 May 2020 14:45:17 +0900\r\nFrom: Microsoft Outlook \r\nTo: =?utf-8?B?eG1haWx1c2Vy?= \r\nSubject: =?utf-8?B?TWljcm9zb2Z0IE91dGxvb2sg44OG44K544OIIOODoeODg+OCu+ODvOOCuA==?=\r\nMIME-Version: 1.0\r\nContent-Type: text/html;\r\n charset=\"utf-8\"\r\nContent-Transfer-Encoding: 8bit\r\n\r\n", 27 | "senderName": "Microsoft Outlook", 28 | "senderAddressType": "SMTP", 29 | "senderEmail": "xmailuser@xmailserver.test", 30 | "normalizedSubject": "Microsoft Outlook テスト メッセージ", 31 | "body": "この電子メール メッセージは、アカウントの設定のテスト中に、Microsoft Outlook から自動送信されたものです。 \r\n", 32 | "inetAcctName": "xmailuser@xmailserver.test", 33 | "creationTime": "Thu, 10 Sep 2020 10:21:42 GMT", 34 | "lastModificationTime": "Thu, 10 Sep 2020 10:21:42 GMT", 35 | "messageDeliveryTime": "Tue, 12 May 2020 05:45:17 GMT", 36 | "messageFlags": 1, 37 | "internetCodepage": 65001 38 | }, 39 | "innerMsgContent": true, 40 | "folderId": 60, 41 | "name": "Microsoft Outlook テスト メッセージ", 42 | "attachmentHidden": false 43 | }, 44 | { 45 | "dataType": "attachment", 46 | "name": "green.png", 47 | "dataId": 112, 48 | "contentLength": 134, 49 | "extension": ".png", 50 | "fileNameShort": "green.png", 51 | "fileName": "green.png", 52 | "creationTime": "Thu, 10 Sep 2020 10:17:47 GMT", 53 | "lastModificationTime": "Thu, 10 Sep 2020 10:17:47 GMT", 54 | "attachmentHidden": false 55 | } 56 | ], 57 | "recipients": [], 58 | "messageClass": "IPM.Note", 59 | "subject": "I have attachments!", 60 | "conversationTopic": "I have attachments!", 61 | "normalizedSubject": "I have attachments!", 62 | "body": "I have attachments!\r\n", 63 | "inetAcctName": "xmailuser@xmailserver.test" 64 | }, 65 | "innerMsgContent": true, 66 | "folderId": 38, 67 | "name": "I have attachments!", 68 | "attachmentHidden": false 69 | }, 70 | { 71 | "dataType": "attachment", 72 | "name": "blue.png", 73 | "dataId": 125, 74 | "contentLength": 134, 75 | "extension": ".png", 76 | "fileNameShort": "blue.png", 77 | "fileName": "blue.png", 78 | "creationTime": "Thu, 10 Sep 2020 10:21:37 GMT", 79 | "lastModificationTime": "Thu, 10 Sep 2020 10:21:37 GMT", 80 | "attachmentHidden": false 81 | } 82 | ], 83 | "recipients": [], 84 | "messageClass": "IPM.Note", 85 | "subject": "I have sub attachments!", 86 | "conversationTopic": "I have sub attachments!", 87 | "normalizedSubject": "I have sub attachments!", 88 | "body": "I have sub attachments!\r\n", 89 | "inetAcctName": "xmailuser@xmailserver.test", 90 | "creationTime": "Thu, 10 Sep 2020 10:21:42 GMT", 91 | "lastModificationTime": "Thu, 10 Sep 2020 10:21:42 GMT", 92 | "messageDeliveryTime": "Thu, 10 Sep 2020 10:21:41 GMT", 93 | "messageFlags": 24, 94 | "internetCodepage": 20127, 95 | "messageLocaleId": 1041 96 | } -------------------------------------------------------------------------------- /test/A monthly 1.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [], 4 | "recipients": [], 5 | "messageClass": "IPM.Appointment", 6 | "subject": "A monthly 1", 7 | "conversationTopic": "Monthly", 8 | "senderName": "Unknown", 9 | "senderAddressType": "UNKNOWN", 10 | "senderEmail": "Unknown", 11 | "normalizedSubject": "A monthly 1", 12 | "body": "\r\n", 13 | "apptRecur": { 14 | "recurrencePattern": { 15 | "recurFrequency": 8204, 16 | "patternType": 2, 17 | "calendarType": 0, 18 | "firstDateTime": 0, 19 | "period": 1, 20 | "slidingFlag": 0, 21 | "endType": 8226, 22 | "occurrenceCount": 1, 23 | "firstDOW": 0, 24 | "deletedInstanceDates": [], 25 | "modifiedInstanceDates": [], 26 | "startDate": 221921280, 27 | "endDate": 221921280, 28 | "patternTypeMonth": { 29 | "day": 12 30 | } 31 | }, 32 | "startTimeOffset": 0, 33 | "endTimeOffset": 1440, 34 | "exceptionInfo": [] 35 | }, 36 | "timeZoneStruct": { 37 | "bias": -540, 38 | "standardBias": 0, 39 | "daylightBias": 0, 40 | "standardYear": 0, 41 | "standardDate": { 42 | "year": 0, 43 | "month": 0, 44 | "dayOfWeek": 0, 45 | "day": 0, 46 | "hour": 0, 47 | "minute": 0 48 | }, 49 | "daylightYear": 0, 50 | "daylightDate": { 51 | "year": 0, 52 | "month": 0, 53 | "dayOfWeek": 0, 54 | "day": 0, 55 | "hour": 0, 56 | "minute": 0 57 | } 58 | }, 59 | "apptLocation": "", 60 | "apptTZDefStartDisplay": { 61 | "rules": [ 62 | { 63 | "flags": 2, 64 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 65 | "bias": -540, 66 | "standardBias": 0, 67 | "daylightBias": 0, 68 | "standardDate": { 69 | "year": 0, 70 | "month": 0, 71 | "dayOfWeek": 0, 72 | "day": 0, 73 | "hour": 0, 74 | "minute": 0 75 | }, 76 | "daylightDate": { 77 | "year": 0, 78 | "month": 0, 79 | "dayOfWeek": 0, 80 | "day": 0, 81 | "hour": 0, 82 | "minute": 0 83 | } 84 | } 85 | ], 86 | "keyName": "Tokyo Standard Time" 87 | }, 88 | "timeZoneDesc": "(UTC+09:00) 大阪、札幌、東京", 89 | "apptTZDefEndDisplay": { 90 | "rules": [ 91 | { 92 | "flags": 2, 93 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 94 | "bias": -540, 95 | "standardBias": 0, 96 | "daylightBias": 0, 97 | "standardDate": { 98 | "year": 0, 99 | "month": 0, 100 | "dayOfWeek": 0, 101 | "day": 0, 102 | "hour": 0, 103 | "minute": 0 104 | }, 105 | "daylightDate": { 106 | "year": 0, 107 | "month": 0, 108 | "dayOfWeek": 0, 109 | "day": 0, 110 | "hour": 0, 111 | "minute": 0 112 | } 113 | } 114 | ], 115 | "keyName": "Tokyo Standard Time" 116 | }, 117 | "apptTZDefRecur": { 118 | "rules": [ 119 | { 120 | "flags": 3, 121 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 122 | "bias": -540, 123 | "standardBias": 0, 124 | "daylightBias": 0, 125 | "standardDate": { 126 | "year": 0, 127 | "month": 0, 128 | "dayOfWeek": 0, 129 | "day": 0, 130 | "hour": 0, 131 | "minute": 0 132 | }, 133 | "daylightDate": { 134 | "year": 0, 135 | "month": 0, 136 | "dayOfWeek": 0, 137 | "day": 0, 138 | "hour": 0, 139 | "minute": 0 140 | } 141 | } 142 | ], 143 | "keyName": "Tokyo Standard Time" 144 | }, 145 | "globalAppointmentID": "040000008200E00074C5B7101A82E0080000000000E7B41F420ED9010000000000000000100000000271014EF0A9E24880087EC5980818EA", 146 | "creationTime": "Mon, 12 Dec 2022 06:56:26 GMT", 147 | "lastModificationTime": "Mon, 12 Dec 2022 06:56:26 GMT", 148 | "clientSubmitTime": "Mon, 12 Dec 2022 06:55:22 GMT", 149 | "messageDeliveryTime": "Mon, 12 Dec 2022 06:55:22 GMT", 150 | "messageFlags": 1, 151 | "internetCodepage": 50220, 152 | "messageLocaleId": 1041, 153 | "apptStartWhole": "Sun, 11 Dec 2022 15:00:00 GMT", 154 | "apptEndWhole": "Mon, 12 Dec 2022 15:00:00 GMT", 155 | "clipStart": "Sun, 11 Dec 2022 15:00:00 GMT", 156 | "clipEnd": "Sun, 11 Dec 2022 15:00:00 GMT" 157 | } -------------------------------------------------------------------------------- /test/A yearly 1.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [], 4 | "recipients": [], 5 | "messageClass": "IPM.Appointment", 6 | "subject": "A yearly 1", 7 | "conversationTopic": "Yearly 1", 8 | "senderName": "Unknown", 9 | "senderAddressType": "UNKNOWN", 10 | "senderEmail": "Unknown", 11 | "normalizedSubject": "A yearly 1", 12 | "body": "\r\n", 13 | "apptRecur": { 14 | "recurrencePattern": { 15 | "recurFrequency": 8205, 16 | "patternType": 2, 17 | "calendarType": 0, 18 | "firstDateTime": 480960, 19 | "period": 12, 20 | "slidingFlag": 0, 21 | "endType": 8226, 22 | "occurrenceCount": 1, 23 | "firstDOW": 0, 24 | "deletedInstanceDates": [], 25 | "modifiedInstanceDates": [], 26 | "startDate": 221921280, 27 | "endDate": 221921280, 28 | "patternTypeMonth": { 29 | "day": 12 30 | } 31 | }, 32 | "startTimeOffset": 0, 33 | "endTimeOffset": 1440, 34 | "exceptionInfo": [] 35 | }, 36 | "timeZoneStruct": { 37 | "bias": -540, 38 | "standardBias": 0, 39 | "daylightBias": 0, 40 | "standardYear": 0, 41 | "standardDate": { 42 | "year": 0, 43 | "month": 0, 44 | "dayOfWeek": 0, 45 | "day": 0, 46 | "hour": 0, 47 | "minute": 0 48 | }, 49 | "daylightYear": 0, 50 | "daylightDate": { 51 | "year": 0, 52 | "month": 0, 53 | "dayOfWeek": 0, 54 | "day": 0, 55 | "hour": 0, 56 | "minute": 0 57 | } 58 | }, 59 | "apptLocation": "", 60 | "apptTZDefStartDisplay": { 61 | "rules": [ 62 | { 63 | "flags": 2, 64 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 65 | "bias": -540, 66 | "standardBias": 0, 67 | "daylightBias": 0, 68 | "standardDate": { 69 | "year": 0, 70 | "month": 0, 71 | "dayOfWeek": 0, 72 | "day": 0, 73 | "hour": 0, 74 | "minute": 0 75 | }, 76 | "daylightDate": { 77 | "year": 0, 78 | "month": 0, 79 | "dayOfWeek": 0, 80 | "day": 0, 81 | "hour": 0, 82 | "minute": 0 83 | } 84 | } 85 | ], 86 | "keyName": "Tokyo Standard Time" 87 | }, 88 | "timeZoneDesc": "(UTC+09:00) 大阪、札幌、東京", 89 | "apptTZDefEndDisplay": { 90 | "rules": [ 91 | { 92 | "flags": 2, 93 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 94 | "bias": -540, 95 | "standardBias": 0, 96 | "daylightBias": 0, 97 | "standardDate": { 98 | "year": 0, 99 | "month": 0, 100 | "dayOfWeek": 0, 101 | "day": 0, 102 | "hour": 0, 103 | "minute": 0 104 | }, 105 | "daylightDate": { 106 | "year": 0, 107 | "month": 0, 108 | "dayOfWeek": 0, 109 | "day": 0, 110 | "hour": 0, 111 | "minute": 0 112 | } 113 | } 114 | ], 115 | "keyName": "Tokyo Standard Time" 116 | }, 117 | "apptTZDefRecur": { 118 | "rules": [ 119 | { 120 | "flags": 3, 121 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 122 | "bias": -540, 123 | "standardBias": 0, 124 | "daylightBias": 0, 125 | "standardDate": { 126 | "year": 0, 127 | "month": 0, 128 | "dayOfWeek": 0, 129 | "day": 0, 130 | "hour": 0, 131 | "minute": 0 132 | }, 133 | "daylightDate": { 134 | "year": 0, 135 | "month": 0, 136 | "dayOfWeek": 0, 137 | "day": 0, 138 | "hour": 0, 139 | "minute": 0 140 | } 141 | } 142 | ], 143 | "keyName": "Tokyo Standard Time" 144 | }, 145 | "globalAppointmentID": "040000008200E00074C5B7101A82E00800000000F00CCA08420ED9010000000000000000100000000A38926A5274684B98AD0F9A47DF3B9F", 146 | "creationTime": "Mon, 12 Dec 2022 06:56:26 GMT", 147 | "lastModificationTime": "Mon, 12 Dec 2022 06:56:26 GMT", 148 | "clientSubmitTime": "Mon, 12 Dec 2022 06:55:09 GMT", 149 | "messageDeliveryTime": "Mon, 12 Dec 2022 06:55:09 GMT", 150 | "messageFlags": 1, 151 | "internetCodepage": 50220, 152 | "messageLocaleId": 1041, 153 | "apptStartWhole": "Sun, 11 Dec 2022 15:00:00 GMT", 154 | "apptEndWhole": "Mon, 12 Dec 2022 15:00:00 GMT", 155 | "clipStart": "Sun, 11 Dec 2022 15:00:00 GMT", 156 | "clipEnd": "Sun, 11 Dec 2022 15:00:00 GMT" 157 | } -------------------------------------------------------------------------------- /test/A daily 1.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [], 4 | "recipients": [], 5 | "messageClass": "IPM.Appointment", 6 | "subject": "A daily 1", 7 | "conversationTopic": "Daily 1", 8 | "senderName": "Unknown", 9 | "senderAddressType": "UNKNOWN", 10 | "senderEmail": "Unknown", 11 | "normalizedSubject": "A daily 1", 12 | "body": "\r\n", 13 | "apptRecur": { 14 | "recurrencePattern": { 15 | "recurFrequency": 8202, 16 | "patternType": 1, 17 | "calendarType": 0, 18 | "firstDateTime": 8640, 19 | "period": 1, 20 | "slidingFlag": 0, 21 | "endType": 8226, 22 | "occurrenceCount": 1, 23 | "firstDOW": 0, 24 | "deletedInstanceDates": [], 25 | "modifiedInstanceDates": [], 26 | "startDate": 221921280, 27 | "endDate": 221921280, 28 | "patternTypeWeek": { 29 | "dayOfWeekBits": 62 30 | } 31 | }, 32 | "startTimeOffset": 0, 33 | "endTimeOffset": 1440, 34 | "exceptionInfo": [] 35 | }, 36 | "timeZoneStruct": { 37 | "bias": -540, 38 | "standardBias": 0, 39 | "daylightBias": 0, 40 | "standardYear": 0, 41 | "standardDate": { 42 | "year": 0, 43 | "month": 0, 44 | "dayOfWeek": 0, 45 | "day": 0, 46 | "hour": 0, 47 | "minute": 0 48 | }, 49 | "daylightYear": 0, 50 | "daylightDate": { 51 | "year": 0, 52 | "month": 0, 53 | "dayOfWeek": 0, 54 | "day": 0, 55 | "hour": 0, 56 | "minute": 0 57 | } 58 | }, 59 | "apptLocation": "", 60 | "apptTZDefStartDisplay": { 61 | "rules": [ 62 | { 63 | "flags": 2, 64 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 65 | "bias": -540, 66 | "standardBias": 0, 67 | "daylightBias": 0, 68 | "standardDate": { 69 | "year": 0, 70 | "month": 0, 71 | "dayOfWeek": 0, 72 | "day": 0, 73 | "hour": 0, 74 | "minute": 0 75 | }, 76 | "daylightDate": { 77 | "year": 0, 78 | "month": 0, 79 | "dayOfWeek": 0, 80 | "day": 0, 81 | "hour": 0, 82 | "minute": 0 83 | } 84 | } 85 | ], 86 | "keyName": "Tokyo Standard Time" 87 | }, 88 | "timeZoneDesc": "(UTC+09:00) 大阪、札幌、東京", 89 | "apptTZDefEndDisplay": { 90 | "rules": [ 91 | { 92 | "flags": 2, 93 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 94 | "bias": -540, 95 | "standardBias": 0, 96 | "daylightBias": 0, 97 | "standardDate": { 98 | "year": 0, 99 | "month": 0, 100 | "dayOfWeek": 0, 101 | "day": 0, 102 | "hour": 0, 103 | "minute": 0 104 | }, 105 | "daylightDate": { 106 | "year": 0, 107 | "month": 0, 108 | "dayOfWeek": 0, 109 | "day": 0, 110 | "hour": 0, 111 | "minute": 0 112 | } 113 | } 114 | ], 115 | "keyName": "Tokyo Standard Time" 116 | }, 117 | "apptTZDefRecur": { 118 | "rules": [ 119 | { 120 | "flags": 3, 121 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 122 | "bias": -540, 123 | "standardBias": 0, 124 | "daylightBias": 0, 125 | "standardDate": { 126 | "year": 0, 127 | "month": 0, 128 | "dayOfWeek": 0, 129 | "day": 0, 130 | "hour": 0, 131 | "minute": 0 132 | }, 133 | "daylightDate": { 134 | "year": 0, 135 | "month": 0, 136 | "dayOfWeek": 0, 137 | "day": 0, 138 | "hour": 0, 139 | "minute": 0 140 | } 141 | } 142 | ], 143 | "keyName": "Tokyo Standard Time" 144 | }, 145 | "globalAppointmentID": "040000008200E00074C5B7101A82E0080000000050582732420ED901000000000000000010000000378180154BD7D04A9C8B235F1E706782", 146 | "creationTime": "Mon, 12 Dec 2022 06:56:25 GMT", 147 | "lastModificationTime": "Mon, 12 Dec 2022 06:56:25 GMT", 148 | "clientSubmitTime": "Mon, 12 Dec 2022 06:55:57 GMT", 149 | "messageDeliveryTime": "Mon, 12 Dec 2022 06:55:57 GMT", 150 | "messageFlags": 1, 151 | "internetCodepage": 50220, 152 | "messageLocaleId": 1041, 153 | "apptStartWhole": "Sun, 11 Dec 2022 15:00:00 GMT", 154 | "apptEndWhole": "Mon, 12 Dec 2022 15:00:00 GMT", 155 | "clipStart": "Sun, 11 Dec 2022 15:00:00 GMT", 156 | "clipEnd": "Sun, 11 Dec 2022 15:00:00 GMT" 157 | } -------------------------------------------------------------------------------- /test/A weekly 1.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [], 4 | "recipients": [], 5 | "messageClass": "IPM.Appointment", 6 | "subject": "A weekly 1", 7 | "conversationTopic": "Weekly 1", 8 | "senderName": "Unknown", 9 | "senderAddressType": "UNKNOWN", 10 | "senderEmail": "Unknown", 11 | "normalizedSubject": "A weekly 1", 12 | "body": "\r\n", 13 | "apptRecur": { 14 | "recurrencePattern": { 15 | "recurFrequency": 8203, 16 | "patternType": 1, 17 | "calendarType": 0, 18 | "firstDateTime": 8640, 19 | "period": 1, 20 | "slidingFlag": 0, 21 | "endType": 8226, 22 | "occurrenceCount": 1, 23 | "firstDOW": 0, 24 | "deletedInstanceDates": [], 25 | "modifiedInstanceDates": [], 26 | "startDate": 221921280, 27 | "endDate": 221921280, 28 | "patternTypeWeek": { 29 | "dayOfWeekBits": 2 30 | } 31 | }, 32 | "startTimeOffset": 960, 33 | "endTimeOffset": 990, 34 | "exceptionInfo": [] 35 | }, 36 | "timeZoneStruct": { 37 | "bias": -540, 38 | "standardBias": 0, 39 | "daylightBias": 0, 40 | "standardYear": 0, 41 | "standardDate": { 42 | "year": 0, 43 | "month": 0, 44 | "dayOfWeek": 0, 45 | "day": 0, 46 | "hour": 0, 47 | "minute": 0 48 | }, 49 | "daylightYear": 0, 50 | "daylightDate": { 51 | "year": 0, 52 | "month": 0, 53 | "dayOfWeek": 0, 54 | "day": 0, 55 | "hour": 0, 56 | "minute": 0 57 | } 58 | }, 59 | "apptLocation": "", 60 | "apptTZDefStartDisplay": { 61 | "rules": [ 62 | { 63 | "flags": 2, 64 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 65 | "bias": -540, 66 | "standardBias": 0, 67 | "daylightBias": 0, 68 | "standardDate": { 69 | "year": 0, 70 | "month": 0, 71 | "dayOfWeek": 0, 72 | "day": 0, 73 | "hour": 0, 74 | "minute": 0 75 | }, 76 | "daylightDate": { 77 | "year": 0, 78 | "month": 0, 79 | "dayOfWeek": 0, 80 | "day": 0, 81 | "hour": 0, 82 | "minute": 0 83 | } 84 | } 85 | ], 86 | "keyName": "Tokyo Standard Time" 87 | }, 88 | "timeZoneDesc": "(UTC+09:00) 大阪、札幌、東京", 89 | "apptTZDefEndDisplay": { 90 | "rules": [ 91 | { 92 | "flags": 2, 93 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 94 | "bias": -540, 95 | "standardBias": 0, 96 | "daylightBias": 0, 97 | "standardDate": { 98 | "year": 0, 99 | "month": 0, 100 | "dayOfWeek": 0, 101 | "day": 0, 102 | "hour": 0, 103 | "minute": 0 104 | }, 105 | "daylightDate": { 106 | "year": 0, 107 | "month": 0, 108 | "dayOfWeek": 0, 109 | "day": 0, 110 | "hour": 0, 111 | "minute": 0 112 | } 113 | } 114 | ], 115 | "keyName": "Tokyo Standard Time" 116 | }, 117 | "apptTZDefRecur": { 118 | "rules": [ 119 | { 120 | "flags": 3, 121 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 122 | "bias": -540, 123 | "standardBias": 0, 124 | "daylightBias": 0, 125 | "standardDate": { 126 | "year": 0, 127 | "month": 0, 128 | "dayOfWeek": 0, 129 | "day": 0, 130 | "hour": 0, 131 | "minute": 0 132 | }, 133 | "daylightDate": { 134 | "year": 0, 135 | "month": 0, 136 | "dayOfWeek": 0, 137 | "day": 0, 138 | "hour": 0, 139 | "minute": 0 140 | } 141 | } 142 | ], 143 | "keyName": "Tokyo Standard Time" 144 | }, 145 | "globalAppointmentID": "040000008200E00074C5B7101A82E0080000000050843228420ED901000000000000000010000000EA7EC9E1FDB9AA43B565589FC2E4646E", 146 | "creationTime": "Mon, 12 Dec 2022 06:56:26 GMT", 147 | "lastModificationTime": "Mon, 12 Dec 2022 06:56:26 GMT", 148 | "clientSubmitTime": "Mon, 12 Dec 2022 06:55:38 GMT", 149 | "messageDeliveryTime": "Mon, 12 Dec 2022 06:55:38 GMT", 150 | "messageFlags": 1, 151 | "internetCodepage": 50220, 152 | "messageLocaleId": 1041, 153 | "apptStartWhole": "Mon, 12 Dec 2022 07:00:00 GMT", 154 | "apptEndWhole": "Mon, 12 Dec 2022 07:30:00 GMT", 155 | "clipStart": "Sun, 11 Dec 2022 15:00:00 GMT", 156 | "clipEnd": "Sun, 11 Dec 2022 15:00:00 GMT" 157 | } -------------------------------------------------------------------------------- /test/Lanch time every friday in 2023 org.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [], 4 | "recipients": [], 5 | "messageClass": "IPM.Appointment", 6 | "subject": "Lanch time, every friday, in 2023", 7 | "conversationTopic": "Lanch time, every friday, in 2023", 8 | "senderName": "Unknown", 9 | "senderAddressType": "UNKNOWN", 10 | "senderEmail": "Unknown", 11 | "normalizedSubject": "Lanch time, every friday, in 2023", 12 | "body": "\r\n", 13 | "apptRecur": { 14 | "recurrencePattern": { 15 | "recurFrequency": 8203, 16 | "patternType": 1, 17 | "calendarType": 0, 18 | "firstDateTime": 8640, 19 | "period": 1, 20 | "slidingFlag": 0, 21 | "endType": 8225, 22 | "occurrenceCount": 52, 23 | "firstDOW": 0, 24 | "deletedInstanceDates": [], 25 | "modifiedInstanceDates": [], 26 | "startDate": 221957280, 27 | "endDate": 222474240, 28 | "patternTypeWeek": { 29 | "dayOfWeekBits": 32 30 | } 31 | }, 32 | "startTimeOffset": 720, 33 | "endTimeOffset": 780, 34 | "exceptionInfo": [] 35 | }, 36 | "timeZoneStruct": { 37 | "bias": -540, 38 | "standardBias": 0, 39 | "daylightBias": -60, 40 | "standardYear": 0, 41 | "standardDate": { 42 | "year": 0, 43 | "month": 0, 44 | "dayOfWeek": 0, 45 | "day": 0, 46 | "hour": 0, 47 | "minute": 0 48 | }, 49 | "daylightYear": 0, 50 | "daylightDate": { 51 | "year": 0, 52 | "month": 0, 53 | "dayOfWeek": 0, 54 | "day": 0, 55 | "hour": 0, 56 | "minute": 0 57 | } 58 | }, 59 | "apptLocation": "", 60 | "apptTZDefStartDisplay": { 61 | "rules": [ 62 | { 63 | "flags": 2, 64 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 65 | "bias": -540, 66 | "standardBias": 0, 67 | "daylightBias": 0, 68 | "standardDate": { 69 | "year": 0, 70 | "month": 0, 71 | "dayOfWeek": 0, 72 | "day": 0, 73 | "hour": 0, 74 | "minute": 0 75 | }, 76 | "daylightDate": { 77 | "year": 0, 78 | "month": 0, 79 | "dayOfWeek": 0, 80 | "day": 0, 81 | "hour": 0, 82 | "minute": 0 83 | } 84 | } 85 | ], 86 | "keyName": "Tokyo Standard Time" 87 | }, 88 | "timeZoneDesc": "(UTC+09:00) 大阪、札幌、東京", 89 | "apptTZDefEndDisplay": { 90 | "rules": [ 91 | { 92 | "flags": 2, 93 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 94 | "bias": -540, 95 | "standardBias": 0, 96 | "daylightBias": 0, 97 | "standardDate": { 98 | "year": 0, 99 | "month": 0, 100 | "dayOfWeek": 0, 101 | "day": 0, 102 | "hour": 0, 103 | "minute": 0 104 | }, 105 | "daylightDate": { 106 | "year": 0, 107 | "month": 0, 108 | "dayOfWeek": 0, 109 | "day": 0, 110 | "hour": 0, 111 | "minute": 0 112 | } 113 | } 114 | ], 115 | "keyName": "Tokyo Standard Time" 116 | }, 117 | "apptTZDefRecur": { 118 | "rules": [ 119 | { 120 | "flags": 2, 121 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 122 | "bias": -540, 123 | "standardBias": 0, 124 | "daylightBias": -60, 125 | "standardDate": { 126 | "year": 0, 127 | "month": 0, 128 | "dayOfWeek": 0, 129 | "day": 0, 130 | "hour": 0, 131 | "minute": 0 132 | }, 133 | "daylightDate": { 134 | "year": 0, 135 | "month": 0, 136 | "dayOfWeek": 0, 137 | "day": 0, 138 | "hour": 0, 139 | "minute": 0 140 | } 141 | } 142 | ], 143 | "keyName": "Tokyo Standard Time" 144 | }, 145 | "globalAppointmentID": "040000008200E00074C5B7101A82E008000000002000F9F62D0AD901000000000000000010000000F00F89203A1BCA479377447BDED6505A", 146 | "creationTime": "Wed, 07 Dec 2022 02:48:58 GMT", 147 | "lastModificationTime": "Wed, 07 Dec 2022 02:48:58 GMT", 148 | "clientSubmitTime": "Wed, 07 Dec 2022 02:23:01 GMT", 149 | "messageDeliveryTime": "Wed, 07 Dec 2022 02:23:01 GMT", 150 | "messageFlags": 1, 151 | "internetCodepage": 50220, 152 | "messageLocaleId": 1041, 153 | "apptStartWhole": "Fri, 06 Jan 2023 03:00:00 GMT", 154 | "apptEndWhole": "Fri, 06 Jan 2023 04:00:00 GMT", 155 | "clipStart": "Thu, 05 Jan 2023 15:00:00 GMT", 156 | "clipEnd": "Sat, 30 Dec 2023 15:00:00 GMT" 157 | } -------------------------------------------------------------------------------- /test/7 days everyday.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [], 4 | "recipients": [ 5 | { 6 | "dataType": "recipient", 7 | "name": "Unknown", 8 | "addressType": "UNKNOWN", 9 | "email": "Unknown", 10 | "recipType": "to" 11 | } 12 | ], 13 | "messageClass": "IPM.Appointment", 14 | "subject": "7 days, everyday", 15 | "conversationTopic": "7 days, everyday", 16 | "senderName": "Unknown", 17 | "senderAddressType": "UNKNOWN", 18 | "senderEmail": "Unknown", 19 | "normalizedSubject": "7 days, everyday", 20 | "body": "\r\n", 21 | "apptRecur": { 22 | "recurrencePattern": { 23 | "recurFrequency": 8202, 24 | "patternType": 0, 25 | "calendarType": 0, 26 | "firstDateTime": 0, 27 | "period": 1440, 28 | "slidingFlag": 0, 29 | "endType": 8225, 30 | "occurrenceCount": 7, 31 | "firstDOW": 0, 32 | "deletedInstanceDates": [], 33 | "modifiedInstanceDates": [], 34 | "startDate": 221905440, 35 | "endDate": 221914080 36 | }, 37 | "startTimeOffset": 0, 38 | "endTimeOffset": 1440, 39 | "exceptionInfo": [] 40 | }, 41 | "timeZoneStruct": { 42 | "bias": -540, 43 | "standardBias": 0, 44 | "daylightBias": -60, 45 | "standardYear": 0, 46 | "standardDate": { 47 | "year": 0, 48 | "month": 0, 49 | "dayOfWeek": 0, 50 | "day": 0, 51 | "hour": 0, 52 | "minute": 0 53 | }, 54 | "daylightYear": 0, 55 | "daylightDate": { 56 | "year": 0, 57 | "month": 0, 58 | "dayOfWeek": 0, 59 | "day": 0, 60 | "hour": 0, 61 | "minute": 0 62 | } 63 | }, 64 | "apptLocation": "", 65 | "apptTZDefStartDisplay": { 66 | "rules": [ 67 | { 68 | "flags": 2, 69 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 70 | "bias": -540, 71 | "standardBias": 0, 72 | "daylightBias": -60, 73 | "standardDate": { 74 | "year": 0, 75 | "month": 0, 76 | "dayOfWeek": 0, 77 | "day": 0, 78 | "hour": 0, 79 | "minute": 0 80 | }, 81 | "daylightDate": { 82 | "year": 0, 83 | "month": 0, 84 | "dayOfWeek": 0, 85 | "day": 0, 86 | "hour": 0, 87 | "minute": 0 88 | } 89 | } 90 | ], 91 | "keyName": "Tokyo Standard Time" 92 | }, 93 | "timeZoneDesc": "(UTC+09:00) 大阪、札幌、東京", 94 | "apptTZDefEndDisplay": { 95 | "rules": [ 96 | { 97 | "flags": 2, 98 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 99 | "bias": -540, 100 | "standardBias": 0, 101 | "daylightBias": -60, 102 | "standardDate": { 103 | "year": 0, 104 | "month": 0, 105 | "dayOfWeek": 0, 106 | "day": 0, 107 | "hour": 0, 108 | "minute": 0 109 | }, 110 | "daylightDate": { 111 | "year": 0, 112 | "month": 0, 113 | "dayOfWeek": 0, 114 | "day": 0, 115 | "hour": 0, 116 | "minute": 0 117 | } 118 | } 119 | ], 120 | "keyName": "Tokyo Standard Time" 121 | }, 122 | "apptTZDefRecur": { 123 | "rules": [ 124 | { 125 | "flags": 2, 126 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 127 | "bias": -540, 128 | "standardBias": 0, 129 | "daylightBias": -60, 130 | "standardDate": { 131 | "year": 0, 132 | "month": 0, 133 | "dayOfWeek": 0, 134 | "day": 0, 135 | "hour": 0, 136 | "minute": 0 137 | }, 138 | "daylightDate": { 139 | "year": 0, 140 | "month": 0, 141 | "dayOfWeek": 0, 142 | "day": 0, 143 | "hour": 0, 144 | "minute": 0 145 | } 146 | } 147 | ], 148 | "keyName": "Tokyo Standard Time" 149 | }, 150 | "globalAppointmentID": "040000008200E00074C5B7101A82E00800000000A048DAF17405D9010000000000000000100000003C10A5564C9D36459E7780C78BAFCB77", 151 | "creationTime": "Thu, 01 Dec 2022 02:43:40 GMT", 152 | "lastModificationTime": "Thu, 01 Dec 2022 02:43:40 GMT", 153 | "clientSubmitTime": "Thu, 01 Dec 2022 02:07:53 GMT", 154 | "messageDeliveryTime": "Thu, 01 Dec 2022 02:07:53 GMT", 155 | "messageFlags": 1, 156 | "internetCodepage": 50220, 157 | "messageLocaleId": 1041, 158 | "apptStartWhole": "Wed, 30 Nov 2022 15:00:00 GMT", 159 | "apptEndWhole": "Thu, 01 Dec 2022 15:00:00 GMT", 160 | "clipStart": "Wed, 30 Nov 2022 15:00:00 GMT", 161 | "clipEnd": "Tue, 06 Dec 2022 15:00:00 GMT" 162 | } -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import { stringify } from "querystring"; 2 | import DataStream from "./DataStream"; 3 | import { TransitionSystemTime } from "./TZDEFINITIONParser"; 4 | 5 | /** 6 | * @internal 7 | */ 8 | export function arraysEqual(a: ArrayLike, b: ArrayLike): boolean { 9 | if (a === b) return true; 10 | if (a == null || b == null) return false; 11 | if (a.length != b.length) return false; 12 | 13 | for (var i = 0; i < a.length; i++) { 14 | if (a[i] !== b[i]) return false; 15 | } 16 | return true; 17 | } 18 | 19 | /** 20 | * @internal 21 | */ 22 | export function uInt2int(data: number[]): number[] { 23 | var result = new Array(data.length); 24 | for (var i = 0; i < data.length; i++) { 25 | result[i] = data[i] << 24 >> 24; 26 | } 27 | return result; 28 | } 29 | 30 | /** 31 | * @internal 32 | */ 33 | export function toHexStr(value: number, padding: number): string { 34 | let text = ""; 35 | while (value != 0) { 36 | text = "0123456789abcdef"[value & 15] + text; 37 | value >>= 4; 38 | text = "0123456789abcdef"[value & 15] + text; 39 | value >>= 4; 40 | } 41 | while (text.length < padding) { 42 | text = "0" + text; 43 | } 44 | return text; 45 | } 46 | 47 | const hex = "0123456789abcdef"; 48 | 49 | /** 50 | * byte to lower case hex string 51 | * 52 | * @internal 53 | */ 54 | export function toHex1(value: number): string { 55 | return hex[(value >> 4) & 15] 56 | + hex[(value) & 15]; 57 | } 58 | 59 | /** 60 | * little uint16 to lower case hex string 61 | * 62 | * @internal 63 | */ 64 | export function toHex2(value: number): string { 65 | return hex[(value >> 12) & 15] 66 | + hex[(value >> 8) & 15] 67 | + hex[(value >> 4) & 15] 68 | + hex[(value) & 15]; 69 | } 70 | 71 | /** 72 | * little uint32 to lower case hex string 73 | * 74 | * @internal 75 | */ 76 | export function toHex4(value: number): string { 77 | return hex[(value >> 28) & 15] 78 | + hex[(value >> 24) & 15] 79 | + hex[(value >> 20) & 15] 80 | + hex[(value >> 16) & 15] 81 | + hex[(value >> 12) & 15] 82 | + hex[(value >> 8) & 15] 83 | + hex[(value >> 4) & 15] 84 | + hex[(value) & 15]; 85 | } 86 | 87 | /** 88 | * Variant 2 UUIDs, historically used in Microsoft's COM/OLE libraries, 89 | * use a mixed-endian format, whereby the first three components of the UUID are little-endian, 90 | * and the last two are big-endian. 91 | * For example, `00112233-4455-6677-8899-aabbccddeeff` is encoded as the bytes 92 | * `33 22 11 00 55 44 77 66 88 99 aa bb cc dd ee ff`. 93 | * 94 | * @see https://en.wikipedia.org/wiki/Universally_unique_identifier 95 | * @internal 96 | */ 97 | export function msftUuidStringify(array: ArrayLike, offset: number): string { 98 | return "" 99 | + toHex1(array[offset + 3]) 100 | + toHex1(array[offset + 2]) 101 | + toHex1(array[offset + 1]) 102 | + toHex1(array[offset + 0]) 103 | + "-" 104 | + toHex1(array[offset + 5]) 105 | + toHex1(array[offset + 4]) 106 | + "-" 107 | + toHex1(array[offset + 7]) 108 | + toHex1(array[offset + 6]) 109 | + "-" 110 | + toHex1(array[offset + 8]) 111 | + toHex1(array[offset + 9]) 112 | + "-" 113 | + toHex1(array[offset + 10]) 114 | + toHex1(array[offset + 11]) 115 | + toHex1(array[offset + 12]) 116 | + toHex1(array[offset + 13]) 117 | + toHex1(array[offset + 14]) 118 | + toHex1(array[offset + 15]) 119 | ; 120 | } 121 | 122 | /** 123 | * @internal 124 | */ 125 | export function emptyToNull(text: string): string { 126 | return (text === "") ? null : text; 127 | } 128 | 129 | 130 | /** 131 | * @internal 132 | */ 133 | function padNumber(value: number, maxLen: number): string { 134 | return ("" + value).padStart(maxLen, '0'); 135 | } 136 | 137 | /** 138 | * @internal 139 | */ 140 | export function readSystemTime(ds: DataStream): Date | null { 141 | // SYSTEMTIME structure (minwinbase.h) 142 | // https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-systemtime 143 | 144 | const wYear = ds.readUint16(); 145 | const wMonth = ds.readUint16(); 146 | const wDayOfWeek = ds.readUint16(); 147 | const wDay = ds.readUint16(); 148 | const wHour = ds.readUint16(); 149 | const wMinute = ds.readUint16(); 150 | const wSecond = ds.readUint16(); 151 | const wMilliseconds = ds.readUint16(); 152 | 153 | const text = `${padNumber(wYear, 4)}-${padNumber(wMonth, 2)}-${padNumber(wDay, 2)}T${padNumber(wHour, 2)}:${padNumber(wMinute, 2)}:${padNumber(wSecond, 2)}Z`; 154 | if (text === '0000-00-00T00:00:00Z') { 155 | return null; 156 | } 157 | else { 158 | return new Date(text); 159 | } 160 | } 161 | 162 | /** 163 | * @internal 164 | */ 165 | export function readTransitionSystemTime(ds: DataStream): TransitionSystemTime { 166 | // SYSTEMTIME structure (minwinbase.h) 167 | // https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-systemtime 168 | 169 | const wYear = ds.readUint16(); 170 | const wMonth = ds.readUint16(); 171 | const wDayOfWeek = ds.readUint16(); 172 | const wDay = ds.readUint16(); 173 | const wHour = ds.readUint16(); 174 | const wMinute = ds.readUint16(); 175 | const wSecond = ds.readUint16(); 176 | const wMilliseconds = ds.readUint16(); 177 | 178 | return { 179 | year: wYear, 180 | month: wMonth, 181 | dayOfWeek: wDayOfWeek, 182 | day: wDay, 183 | hour: wHour, 184 | minute: wMinute, 185 | }; 186 | } 187 | 188 | /** 189 | * @internal 190 | */ 191 | export function bin2HexUpper(ds: DataStream): string { 192 | var text = ""; 193 | while (!ds.isEof()) { 194 | text += toHex1(ds.readUint8()); 195 | } 196 | return text.toUpperCase(); 197 | } 198 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # msgreader 2 | 3 | [![npm](https://img.shields.io/npm/v/@kenjiuno/msgreader)](https://www.npmjs.com/package/@kenjiuno/msgreader) 4 | [![Build Status](https://dev.azure.com/HiraokaHyperTools/msgreader/_apis/build/status/HiraokaHyperTools.msgreader?branchName=master)](https://dev.azure.com/HiraokaHyperTools/msgreader/_build/latest?definitionId=7&branchName=master) 5 | 6 | Outlook Item File (.msg) reader in JavaScript npm Module 7 | 8 | Original projects: 9 | 10 | - https://github.com/FreiraumIO/msgreader 11 | - https://github.com/ykarpovich/msg.reader 12 | 13 | This repo contains the code of the modified project. 14 | And also it is published as a [npm package](https://www.npmjs.com/package/@kenjiuno/msgreader). 15 | 16 | Links: [_typedoc documentation_](https://hiraokahypertools.github.io/msgreader/typedoc/) | [_online demo_](https://hiraokahypertools.github.io/msgreader_demo/) | [_online demo 2_](https://hiraokahypertools.github.io/msgreader_demo2/) | [_online demo 3_](https://hiraokahypertools.github.io/msgreader_demo3/) 17 | 18 | ## How to use 19 | 20 | ```javascript 21 | import fs from 'fs' 22 | import MsgReader from '@kenjiuno/msgreader' 23 | 24 | const msgFileBuffer = fs.readFileSync('./data/test.msg') 25 | const testMsg = new MsgReader(msgFileBuffer) 26 | const testMsgInfo = testMsg.getFileData() 27 | /** 28 | testMsgInfo contains: 29 | { 30 | attachments:[ 31 | { 32 | dataId:62, 33 | contentLength:122784, 34 | fileName:'5AAoPFgV-nJ965R7o-98C38840-4454-4750-9AEF-F53DB3E37548.jpg', 35 | fileNameShort:'5AAOPF~1.JPG' 36 | } 37 | ], 38 | recipients:[ 39 | { 40 | name:'christoph@freiraum.xyz' 41 | } 42 | ], 43 | senderName:'christoph@freiraum.xyz', 44 | body:' \r\n\r\n', 45 | headers:'Return-Path: \r\nReceived: from DESKTOPGBT9Q6P (HSI-KBW-109-193-162-142.hsi7.kabel-badenwuerttemberg.de. [109.193.162.142])\r\n by smtp.gmail.com with ESMTPSA id q81sm10535131wmg.8.2018.03.23.09.06.30\r\n for \r\n (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);\r\n Fri, 23 Mar 2018 09:06:30 -0700 (PDT)\r\nFrom: \r\nTo: \r\nSubject: asdf\r\nDate: Fri, 23 Mar 2018 17:06:29 +0100\r\nMessage-ID: <000001d3c2c0$e7ca4aa0$b75edfe0$@freiraum.xyz>\r\nMIME-Version: 1.0\r\nContent-Type: multipart/mixed;\r\n\tboundary="----=_NextPart_000_0001_01D3C2C9.498F75F0"\r\nX-Mailer: Microsoft Outlook 16.0\r\nThread-Index: AdPCwN90aOYoV24DTGKfv8JaCuci0g==\r\nContent-Language: de\r\n\r\n', 46 | subject:'asdf' 47 | } 48 | **/ 49 | const testMsgAttachment0 = testMsg.getAttachment(testMsgInfo.attachments[0]) 50 | /** 51 | testMsgAttachment0 === testMsg.getAttachment[0] and both contain: 52 | { 53 | fileName: '5AAoPFgV-nJ965R7o-98C38840-4454-4750-9AEF-F53DB3E37548.jpg', 54 | content: //content removed 55 | } 56 | **/ 57 | ``` 58 | 59 | ### List attachment files 60 | 61 | ```javascript 62 | const msgFileBuffer = fs.readFileSync(msgFilePath) 63 | const testMsg = new MsgReader(msgFileBuffer) 64 | const testMsgInfo = testMsg.getFileData() 65 | 66 | for (const att of testMsgInfo.attachments) { 67 | const attachment = testMsg.getAttachment(att); 68 | console.log(attachment.fileName); 69 | 70 | // fs.writeFileSync("save-" + attachment.fileName, attachment.content); 71 | 72 | // Node.js ≧ v0.1.90 73 | // Buffer.from(attachment.content).toString('base64') 74 | 75 | // Node.js ≧ v25 76 | // attachment.content.toBase64() 77 | } 78 | ``` 79 | 80 | For a reference: 81 | 82 | - [Buffer | Node.js v25.2.1 Documentation](https://nodejs.org/api/buffer.html#buftostringencoding-start-end) 83 | - [Uint8Array.prototype.toBase64() - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array/toBase64) 84 | 85 | ## Build msgreader locally 86 | 87 | ```bat 88 | yarn 89 | ``` 90 | 91 | ## Optional command line tool 92 | 93 | This can be used for testing this tool. 94 | 95 | ```bat 96 | C:\Proj\msgreader>node cli -h 97 | Usage: cli [options] [command] 98 | 99 | Options: 100 | -h, --help output usage information 101 | 102 | Commands: 103 | parse [options] Parse msg file and print parsed structure 104 | rtf [saveToRtfFilePath] Parse msg file and print decompressed rtf 105 | list-att Parse msg file and list attachment file names 106 | save-att Parse msg file and write all attachment files 107 | ``` 108 | 109 | Obtain decompressed RTF file from `test/test1.msg`: 110 | 111 | ```bat 112 | C:\Proj\msgreader>node cli rtf test\test1.msg test1.rtf 113 | 114 | C:\Proj\msgreader>type test1.rtf 115 | {\rtf1\ansi\ansicpg1252\fromtext \fbidis \deff0{\fonttbl 116 | {\f0\fswiss Arial;} 117 | {\f1\fmodern Courier New;} 118 | {\f2\fnil\fcharset2 Symbol;} 119 | {\f3\fmodern\fcharset0 Courier New;}} 120 | {\colortbl\red0\green0\blue0;\red0\green0\blue255;} 121 | \uc1\pard\plain\deftab360 \f0\fs20 body\par 122 | } 123 | ``` 124 | 125 | List attachment files in `test/test2.msg`: 126 | 127 | ```bat 128 | C:\Proj\msgreader>node cli list-att test\test2.msg 129 | A.txt 130 | ``` 131 | 132 | Extract attacument files into folder `test2`: 133 | 134 | ```bat 135 | C:\Proj\msgreader>node cli save-att test\test2.msg test2 136 | ``` 137 | 138 | ```bat 139 | C:\Proj\msgreader>dir test2 140 | Volume in drive C has no label. 141 | Volume Serial Number is CA6D-4F59 142 | 143 | Directory of C:\Proj\msgreader\test2 144 | 145 | 2020/03/19 19:40 . 146 | 2020/03/19 19:40 .. 147 | 2020/03/19 19:40 11 A.txt 148 | 1 File(s) 11 bytes 149 | 2 Dir(s) 9,542,762,496 bytes free 150 | ``` 151 | 152 | ```bat 153 | C:\Proj\msgreader>type test2\A.txt 154 | attach test 155 | ``` 156 | 157 | Checking date times from `sent.msg`: 158 | 159 | ```bat 160 | node cli parse test\sent.msg 161 | 162 | { 163 | dataType: 'msg', 164 | attachments: [], 165 | recipients: [ 166 | { 167 | dataType: 'recipient', 168 | name: "'xmailuser@xmailserver.test'", 169 | email: 'xmailuser@xmailserver.test', 170 | recipType: 'to' 171 | } 172 | ], 173 | senderEmail: 'xmailuser@xmailserver.test', 174 | subject: 'Sent time', 175 | body: 'Test mail\r\n\r\n', 176 | senderName: 'xmailuser', 177 | compressedRtf: Uint8Array(1409) [ 178 | ... 179 | ], 180 | creationTime: 'Mon, 15 Feb 2021 08:19:21 GMT', 181 | lastModificationTime: 'Mon, 15 Feb 2021 08:19:21 GMT', 182 | clientSubmitTime: 'Mon, 15 Feb 2021 08:19:04 GMT', 183 | messageDeliveryTime: 'Mon, 15 Feb 2021 08:19:00 GMT' 184 | } 185 | ``` 186 | -------------------------------------------------------------------------------- /src/TZDEFINITIONParser.ts: -------------------------------------------------------------------------------- 1 | import DataStream from "./DataStream"; 2 | import { readSystemTime, readTransitionSystemTime } from "./utils"; 3 | 4 | const TZDEFINITION_FLAG_VALID_GUID = 1; 5 | const TZDEFINITION_FLAG_VALID_KEYNAME = 2; 6 | 7 | const TZRULE_FLAG_EFFECTIVE_TZREG = 2; 8 | const TZRULE_FLAG_RECUR_CURRENT_TZREG = 1; 9 | 10 | /** 11 | * StandardDate 12 | * 13 | * A SYSTEMTIME structure that contains a date and local time when the transition from daylight saving time to standard time occurs on this operating system. If the time zone does not support daylight saving time or if the caller needs to disable daylight saving time, the wMonth member in the SYSTEMTIME structure must be zero. If this date is specified, the DaylightDate member of this structure must also be specified. 14 | * 15 | * Otherwise, the system assumes the time zone data is invalid and no changes will be applied. 16 | * 17 | * To select the correct day in the month, set the wYear member to zero, the wHour and wMinute members to the transition time, the wDayOfWeek member to the appropriate weekday, and the wDay member to indicate the occurrence of the day of the week within the month (1 to 5, where 5 indicates the final occurrence during the month if that day of the week does not occur 5 times). 18 | * 19 | * Using this notation, specify 02:00 on the first Sunday in April as follows: wHour = 2, wMonth = 4, wDayOfWeek = 0, wDay = 1. Specify 02:00 on the last Thursday in October as follows: wHour = 2, wMonth = 10, wDayOfWeek = 4, wDay = 5. 20 | * 21 | * If the wYear member is not zero, the transition date is absolute; it will only occur one time. Otherwise, it is a relative date that occurs yearly. 22 | * 23 | * @see [TIME_ZONE_INFORMATION (timezoneapi.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/ns-timezoneapi-time_zone_information) 24 | */ 25 | export interface TransitionSystemTime { 26 | year: number; 27 | month: number; 28 | dayOfWeek: number; 29 | day: number; 30 | hour: number; 31 | minute: number; 32 | } 33 | 34 | /** 35 | * 36 | * @see [TZRULE | Microsoft Learn](https://learn.microsoft.com/en-us/office/client-developer/outlook/auxiliary/tzrule) 37 | * @see [TZREG | Microsoft Learn](https://learn.microsoft.com/en-us/office/client-developer/outlook/auxiliary/tzreg) 38 | */ 39 | export interface TzDefinitionRule { 40 | /** 41 | * The flags set for this member identify specific details for this time zone rule. The possible flags are as follows: 42 | * 43 | * - TZRULE_FLAG_EFFECTIVE_TZREG (2) — Identifies the rule as the one that should be used currently. Only one rule can be marked as the effective rule. All other rules are for comparison purposes only. 44 | * - TZRULE_FLAG_RECUR_CURRENT_TZREG (1) — On recurring meetings, identifies the rule as matching the rule in PidLidTimeZoneStruct. This can be used to detect whether PidLidTimeZoneStruct has been modified significantly by a legacy client, which would be otherwise unaware of the new, more complete property. 45 | * 46 | * @see [Constants (Outlook exported APIs) | Microsoft Learn](https://learn.microsoft.com/en-us/office/client-developer/outlook/auxiliary/constants-outlook-exported-apis) 47 | */ 48 | flags: number; 49 | 50 | /** 51 | * The time in Coordinated Universal Time (UTC) that the time zone rule started. 52 | */ 53 | start: string | null; 54 | 55 | /** 56 | * The offset from Greenwich Mean Time (GMT). 57 | */ 58 | bias: number; 59 | 60 | /** 61 | * The offset from bias during standard time. 62 | */ 63 | standardBias: number; 64 | 65 | /** 66 | * The offset from bias during daylight saving time. 67 | */ 68 | daylightBias: number; 69 | 70 | /** 71 | * The time to switch to standard time. 72 | */ 73 | standardDate: TransitionSystemTime; 74 | 75 | /** 76 | * The time to switch to daylight saving time. 77 | */ 78 | daylightDate: TransitionSystemTime; 79 | } 80 | 81 | /** 82 | * 83 | * @see [TZDEFINITION | Microsoft Learn](https://learn.microsoft.com/en-us/office/client-developer/outlook/auxiliary/tzdefinition) 84 | */ 85 | export interface TzDefinition { 86 | /** 87 | * The name of the key for this time zone in the Windows registry. This name must not be localized. 88 | * 89 | * e.g. `Tokyo Standard Time` 90 | */ 91 | keyName?: string; 92 | 93 | /** 94 | * An array of rules that describe when shifts occur. 95 | */ 96 | rules: TzDefinitionRule[]; 97 | } 98 | 99 | /** 100 | * @internal 101 | */ 102 | export function parse(ds: DataStream): TzDefinition | null { 103 | // About persisting TZDEFINITION to a stream to commit to a binary property 104 | // https://learn.microsoft.com/en-us/office/client-developer/outlook/auxiliary/about-persisting-tzdefinition-to-a-stream-to-commit-to-a-binary-property?redirectedfrom=MSDN 105 | 106 | const tz = { rules: [] } as TzDefinition; 107 | 108 | if (!ds.isEof()) { 109 | const bMajorVersion = ds.readUint8(); 110 | if (bMajorVersion !== 2) { 111 | throw new Error("TZDEFINITION major version not supported"); 112 | } 113 | const bMinorVersion = ds.readUint8(); 114 | if (bMajorVersion < 1) { 115 | throw new Error("TZDEFINITION minor version not supported"); 116 | } 117 | const cbHeader = ds.readUint16(); 118 | const wFlags = ds.readUint16(); 119 | if (wFlags & TZDEFINITION_FLAG_VALID_GUID) { 120 | ds.readInt32(); 121 | ds.readInt32(); 122 | ds.readInt32(); 123 | ds.readInt32(); 124 | } 125 | if (wFlags & TZDEFINITION_FLAG_VALID_KEYNAME) { 126 | const cchKeyName = ds.readUint16(); 127 | tz.keyName = ds.readUCS2String(cchKeyName); 128 | } 129 | const cRules = ds.readUint16(); 130 | ds.seek(4 + cbHeader); 131 | 132 | for (let x = 0; x < cRules; x++) { 133 | const bMajorVersion = ds.readUint8(); 134 | if (bMajorVersion !== 2) { 135 | break; 136 | } 137 | const bMinorVersion = ds.readUint8(); 138 | if (bMajorVersion < 1) { 139 | break; 140 | } 141 | const cbRule = ds.readUint16(); 142 | const basePos = ds.position; 143 | const wFlags = ds.readUint16(); 144 | const stStart = readSystemTime(ds); 145 | const lBias = ds.readInt32(); 146 | const lStandardBias = ds.readInt32(); 147 | const lDaylightBias = ds.readInt32(); 148 | const stStandardDate = readTransitionSystemTime(ds); 149 | const stDaylightDate = readTransitionSystemTime(ds); 150 | 151 | const rule = Object.assign( 152 | {}, 153 | { 154 | flags: wFlags, 155 | start: stStart?.toUTCString() || null, 156 | bias: lBias, 157 | standardBias: lStandardBias, 158 | daylightBias: lDaylightBias, 159 | standardDate: stStandardDate, 160 | daylightDate: stDaylightDate, 161 | } 162 | ); 163 | tz.rules.push(rule); 164 | 165 | ds.seek(basePos + cbRule); 166 | } 167 | } 168 | return tz; 169 | } 170 | -------------------------------------------------------------------------------- /others/AppointmentRecurrencePattern.bt: -------------------------------------------------------------------------------- 1 | //------------------------------------------------ 2 | //--- 010 Editor v11.0.1 Binary Template 3 | // 4 | // File: __substg1.0_XXXX0102 5 | // Authors: kenjiuno 6 | // Version: 7 | // Purpose: 2.2.1.44.5 AppointmentRecurrencePattern Structure 8 | // Category: 9 | // File Mask: 10 | // ID Bytes: 04 30 04 30 11 | // History: 12 | //------------------------------------------------ 13 | 14 | LittleEndian(); 15 | 16 | enum RecurFrequencyT { 17 | Daily=0x200A, 18 | Weekly=0x200B, 19 | Monthly=0x200C, 20 | Yearly=0x200D, 21 | }; 22 | 23 | enum PatternTypeT { 24 | 25 | Day=0x0000,//The event has a daily recurrence. 26 | Week=0x0001,//The event has a weekly recurrence. 27 | Month=0x0002,//The event has a monthly recurrence. 28 | MonthEnd=0x0004,//The event has a month-end recurrence.<12> 29 | MonthNth=0x0003,//The event has an every nth month pattern. 30 | HjMonth=0x000A,//The event has a monthly recurrence in the Hijri calendar. For this value in the PatternType field, the value of the CalendarType field SHOULD be set to 0x0000.<13> 31 | HjMonthNth=0x000B,//The event has an every nth month pattern in the Hijri calendar. For this value in the PatternType field, the value of the CalendarType field MUST be set to 0x0000. 32 | HjMonthEnd=0x000C,//The event has a month end recurrence in the Hijri calendar. For this value in the PatternType field, the value of the CalendarType field MUST be set to 0x0000. 33 | 34 | }; 35 | 36 | enum CalendarTypeT { 37 | 38 | Default=0x0000,//The default value for the calendar type is Gregorian. 39 | CAL_GREGORIAN=0x0001,//Gregorian (localized) calendar 40 | CAL_GREGORIAN_US=0x0002,//Gregorian (U.S.) calendar 41 | CAL_JAPAN=0x0003,//Japanese Emperor era calendar 42 | CAL_TAIWAN=0x0004,//Taiwan calendar 43 | CAL_KOREA=0x0005,//Korean Tangun era calendar 44 | CAL_HIJRI=0x0006,//Hijri (Arabic Lunar) calendar 45 | CAL_THAI=0x0007,//Thai calendar 46 | CAL_HEBREW=0x0008,//Hebrew lunar calendar 47 | CAL_GREGORIAN_ME_FRENCH=0x0009,//Gregorian Middle East French calendar 48 | CAL_GREGORIAN_ARABIC=0x000A,//Gregorian Arabic calendar 49 | CAL_GREGORIAN_XLIT_ENGLISH=0x000B,//Gregorian transliterated English calendar 50 | CAL_GREGORIAN_XLIT_FRENCH=0x000C,//Gregorian transliterated French calendar 51 | CAL_LUNAR_JAPANESE=0x000E,//Japanese lunar calendar 52 | CAL_CHINESE_LUNAR=0x000F,//Chinese lunar calendar 53 | CAL_SAKA=0x0010,//Saka era calendar 54 | CAL_LUNAR_ETO_CHN=0x0011,//Lunar ETO Chinese calendar 55 | CAL_LUNAR_ETO_KOR=0x0012,//Lunar ETO Korean calendar 56 | CAL_LUNAR_ROKUYOU=0x0013,//Lunar Rokuyou calendar 57 | CAL_LUNAR_KOREAN=0x0014,//Korean lunar calendar 58 | CAL_UMALQURA=0x0017,//Um Al Qura calendar 59 | 60 | }; 61 | 62 | enum EndTypeT { 63 | EndAfterDate = 0x00002021, 64 | EndAfterNOccurrences = 0x00002022, 65 | NeverEnd = 0x00002023, 66 | NeverEnd2 = 0xFFFFFFFF, 67 | }; 68 | 69 | struct RecurrencePattern { 70 | 71 | ushort ReaderVersion; 72 | ushort WriterVersion; 73 | RecurFrequencyT RecurFrequency; 74 | PatternTypeT PatternType; 75 | CalendarTypeT CalendarType; 76 | uint FirstDateTime; 77 | uint Period; 78 | uint SlidingFlag; 79 | if (PatternType == Week) { 80 | uint PatternTypeSpecificWeek; 81 | } 82 | if (PatternType == Month || PatternType == MonthEnd || PatternType == HjMonth || PatternType == HjMonthEnd) { 83 | uint PatternTypeSpecificMonth; 84 | } 85 | if (PatternType == MonthNth || RecurFrequency == HjMonthNth) { 86 | uint64 PatternTypeSpecificMonthNth; 87 | } 88 | EndTypeT EndType; 89 | uint OccurrenceCount; 90 | uint FirstDOW; 91 | uint DeletedInstanceCount; 92 | uint DeletedInstanceDates[DeletedInstanceCount]; 93 | uint ModifiedInstanceCount; 94 | uint ModifiedInstanceDates[ModifiedInstanceCount]; 95 | uint StartDate; 96 | uint EndDate; 97 | 98 | }; 99 | 100 | enum OverrideFlagsT { 101 | ARO_SUBJECT=0x0001,//Indicates that the Subject, SubjectLength, and SubjectLength2 fields are present. 102 | ARO_MEETINGTYPE=0x0002,//Indicates that the MeetingType field is present. 103 | ARO_REMINDERDELTA=0x0004,//Indicates that the ReminderDelta field is present. 104 | ARO_REMINDER=0x0008,//Indicates that the ReminderSet field is present. 105 | ARO_LOCATION=0x0010,//Indicates that the Location, LocationLength, and LocationLength2 fields are present. 106 | ARO_BUSYSTATUS=0x0020,//Indicates that the BusyStatus field is present. 107 | ARO_ATTACHMENT=0x0040,//Indicates that the attachment field is present. 108 | ARO_SUBTYPE=0x0080,//Indicates that the SubType field is present. 109 | ARO_APPTCOLOR=0x0100,//Indicates that the AppointmentColor field is present. 110 | ARO_EXCEPTIONAL_BODY=0x0200,//Indicates that the Exception Embedded Message object has the PidTagRtfCompressed property ([MS-OXCMSG] section 2.2.1.56.4) set on it. 111 | 112 | }; 113 | 114 | typedef struct { 115 | 116 | uint StartDateTime; 117 | uint EndDateTime; 118 | uint OriginalStartTime; 119 | OverrideFlagsT OverrideFlags; 120 | if (OverrideFlags & ARO_SUBJECT) { 121 | ushort SubjectLength; 122 | ushort SubjectLength2; 123 | byte Subject[SubjectLength2]; 124 | } 125 | if (OverrideFlags & ARO_MEETINGTYPE) { 126 | uint MeetingType; 127 | } 128 | if (OverrideFlags & ARO_REMINDERDELTA) { 129 | uint ReminderDelta; 130 | } 131 | if (OverrideFlags & ARO_REMINDER) { 132 | uint ReminderSet; 133 | } 134 | if (OverrideFlags & ARO_LOCATION) { 135 | ushort LocationLength; 136 | ushort LocationLength2; 137 | byte Location[LocationLength2]; 138 | } 139 | if (OverrideFlags & ARO_BUSYSTATUS) { 140 | uint BusyStatus; 141 | } 142 | if (OverrideFlags & ARO_ATTACHMENT) { 143 | uint Attachment; 144 | } 145 | if (OverrideFlags & ARO_SUBTYPE) { 146 | uint SubType; 147 | } 148 | if (OverrideFlags & ARO_APPTCOLOR) { 149 | uint AppointmentColor; 150 | } 151 | 152 | 153 | 154 | } ExceptionInfoT; 155 | 156 | typedef struct { 157 | uint ChangeHighlightSize; 158 | byte ChangeHighlight[ChangeHighlightSize]; 159 | } ChangeHighlightT; 160 | 161 | typedef struct ExtendedExceptionT( 162 | uint OverrideFlags, 163 | uint WriterVersion2 164 | ) { 165 | if (0x00003009 <= WriterVersion2) { 166 | ChangeHighlightT ChangeHighlight; 167 | } 168 | 169 | uint ReservedBlockEE1Size; 170 | if (OverrideFlags & (ARO_SUBJECT | ARO_LOCATION)) { 171 | uint StartDateTime; 172 | uint EndDateTime; 173 | uint OriginalStartDate; 174 | 175 | if (OverrideFlags & (ARO_SUBJECT)) { 176 | ushort WideCharSubjectLength; 177 | wchar_t WideCharSubject[WideCharSubjectLength]; 178 | } 179 | if (OverrideFlags & (ARO_LOCATION)) { 180 | ushort WideCharLocationLength; 181 | wchar_t WideCharLocation[WideCharLocationLength]; 182 | } 183 | uint ReservedBlockEE2Size; 184 | } 185 | 186 | }; 187 | 188 | struct AppointmentRecurrencePattern { 189 | 190 | RecurrencePattern p; 191 | uint ReaderVersion2; 192 | uint WriterVersion2; 193 | uint StartTimeOffset; 194 | uint EndTimeOffset; 195 | ushort ExceptionCount; 196 | ExceptionInfoT ExceptionInfo[ExceptionCount] ; 197 | uint ReservedBlock1Size; 198 | local int x; 199 | for (x=0; x; 201 | } 202 | uint ReservedBlock2Size; 203 | 204 | }; 205 | 206 | AppointmentRecurrencePattern ar; 207 | -------------------------------------------------------------------------------- /test/Lanch time every friday in 2023 chgs1.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [ 4 | { 5 | "dataType": "attachment", 6 | "innerMsgContentFields": { 7 | "dataType": "msg", 8 | "attachments": [], 9 | "recipients": [ 10 | { 11 | "dataType": "recipient", 12 | "name": "Unknown", 13 | "addressType": "UNKNOWN", 14 | "email": "Unknown", 15 | "recipType": "to" 16 | } 17 | ], 18 | "messageClass": "IPM.OLE.CLASS.{00061055-0000-0000-C000-000000000046}", 19 | "subject": "Lanch time, every friday, in 2023 [rescheduled!]", 20 | "conversationTopic": "Lanch time, every friday, in 2023 [rescheduled!]", 21 | "normalizedSubject": "Lanch time, every friday, in 2023 [rescheduled!]", 22 | "apptLocation": "", 23 | "apptTZDefStartDisplay": { 24 | "rules": [ 25 | { 26 | "flags": 2, 27 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 28 | "bias": -540, 29 | "standardBias": 0, 30 | "daylightBias": 0, 31 | "standardDate": { 32 | "year": 0, 33 | "month": 0, 34 | "dayOfWeek": 0, 35 | "day": 0, 36 | "hour": 0, 37 | "minute": 0 38 | }, 39 | "daylightDate": { 40 | "year": 0, 41 | "month": 0, 42 | "dayOfWeek": 0, 43 | "day": 0, 44 | "hour": 0, 45 | "minute": 0 46 | } 47 | } 48 | ], 49 | "keyName": "Tokyo Standard Time" 50 | }, 51 | "apptTZDefEndDisplay": { 52 | "rules": [ 53 | { 54 | "flags": 2, 55 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 56 | "bias": -540, 57 | "standardBias": 0, 58 | "daylightBias": 0, 59 | "standardDate": { 60 | "year": 0, 61 | "month": 0, 62 | "dayOfWeek": 0, 63 | "day": 0, 64 | "hour": 0, 65 | "minute": 0 66 | }, 67 | "daylightDate": { 68 | "year": 0, 69 | "month": 0, 70 | "dayOfWeek": 0, 71 | "day": 0, 72 | "hour": 0, 73 | "minute": 0 74 | } 75 | } 76 | ], 77 | "keyName": "Tokyo Standard Time" 78 | }, 79 | "creationTime": "Wed, 07 Dec 2022 03:30:27 GMT", 80 | "lastModificationTime": "Wed, 07 Dec 2022 03:30:27 GMT", 81 | "messageFlags": 9, 82 | "apptStartWhole": "Thu, 12 Jan 2023 03:00:00 GMT", 83 | "apptEndWhole": "Thu, 12 Jan 2023 04:00:00 GMT", 84 | "clipStart": "Thu, 12 Jan 2023 03:00:00 GMT", 85 | "clipEnd": "Thu, 12 Jan 2023 04:00:00 GMT" 86 | }, 87 | "innerMsgContent": true, 88 | "folderId": 82, 89 | "name": "無題", 90 | "attachmentHidden": true 91 | } 92 | ], 93 | "recipients": [ 94 | { 95 | "dataType": "recipient", 96 | "name": "Unknown", 97 | "addressType": "UNKNOWN", 98 | "email": "Unknown", 99 | "recipType": "to" 100 | } 101 | ], 102 | "messageClass": "IPM.Appointment", 103 | "subject": "Lanch time, every friday, in 2023", 104 | "conversationTopic": "Lanch time, every friday, in 2023", 105 | "senderName": "Unknown", 106 | "senderAddressType": "UNKNOWN", 107 | "senderEmail": "Unknown", 108 | "normalizedSubject": "Lanch time, every friday, in 2023", 109 | "body": "Changes:\r\n-\tJan 6 cancel\r\n-\tJan 13 rescheduled to Jan 12\r\n\r\n", 110 | "apptLocation": "", 111 | "apptTZDefStartDisplay": { 112 | "rules": [ 113 | { 114 | "flags": 2, 115 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 116 | "bias": -540, 117 | "standardBias": 0, 118 | "daylightBias": 0, 119 | "standardDate": { 120 | "year": 0, 121 | "month": 0, 122 | "dayOfWeek": 0, 123 | "day": 0, 124 | "hour": 0, 125 | "minute": 0 126 | }, 127 | "daylightDate": { 128 | "year": 0, 129 | "month": 0, 130 | "dayOfWeek": 0, 131 | "day": 0, 132 | "hour": 0, 133 | "minute": 0 134 | } 135 | } 136 | ], 137 | "keyName": "Tokyo Standard Time" 138 | }, 139 | "apptTZDefEndDisplay": { 140 | "rules": [ 141 | { 142 | "flags": 2, 143 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 144 | "bias": -540, 145 | "standardBias": 0, 146 | "daylightBias": 0, 147 | "standardDate": { 148 | "year": 0, 149 | "month": 0, 150 | "dayOfWeek": 0, 151 | "day": 0, 152 | "hour": 0, 153 | "minute": 0 154 | }, 155 | "daylightDate": { 156 | "year": 0, 157 | "month": 0, 158 | "dayOfWeek": 0, 159 | "day": 0, 160 | "hour": 0, 161 | "minute": 0 162 | } 163 | } 164 | ], 165 | "keyName": "Tokyo Standard Time" 166 | }, 167 | "apptRecur": { 168 | "recurrencePattern": { 169 | "recurFrequency": 8203, 170 | "patternType": 1, 171 | "calendarType": 0, 172 | "firstDateTime": 8640, 173 | "period": 1, 174 | "slidingFlag": 0, 175 | "endType": 8225, 176 | "occurrenceCount": 52, 177 | "firstDOW": 0, 178 | "deletedInstanceDates": [ 179 | 221957280, 180 | 221967360 181 | ], 182 | "modifiedInstanceDates": [ 183 | 221965920 184 | ], 185 | "startDate": 221957280, 186 | "endDate": 222474240, 187 | "patternTypeWeek": { 188 | "dayOfWeekBits": 32 189 | } 190 | }, 191 | "startTimeOffset": 720, 192 | "endTimeOffset": 780, 193 | "exceptionInfo": [ 194 | { 195 | "startDateTime": 221966640, 196 | "endDateTime": 221966700, 197 | "originalStartTime": 221968080, 198 | "overrideFlags": 1, 199 | "subject": "Lanch time, every friday, in 2023 [rescheduled!]", 200 | "changeHighlight": 0 201 | } 202 | ] 203 | }, 204 | "timeZoneStruct": { 205 | "bias": -540, 206 | "standardBias": 0, 207 | "daylightBias": -60, 208 | "standardYear": 0, 209 | "standardDate": { 210 | "year": 0, 211 | "month": 0, 212 | "dayOfWeek": 0, 213 | "day": 0, 214 | "hour": 0, 215 | "minute": 0 216 | }, 217 | "daylightYear": 0, 218 | "daylightDate": { 219 | "year": 0, 220 | "month": 0, 221 | "dayOfWeek": 0, 222 | "day": 0, 223 | "hour": 0, 224 | "minute": 0 225 | } 226 | }, 227 | "timeZoneDesc": "(UTC+09:00) 大阪、札幌、東京", 228 | "apptTZDefRecur": { 229 | "rules": [ 230 | { 231 | "flags": 2, 232 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 233 | "bias": -540, 234 | "standardBias": 0, 235 | "daylightBias": -60, 236 | "standardDate": { 237 | "year": 0, 238 | "month": 0, 239 | "dayOfWeek": 0, 240 | "day": 0, 241 | "hour": 0, 242 | "minute": 0 243 | }, 244 | "daylightDate": { 245 | "year": 0, 246 | "month": 0, 247 | "dayOfWeek": 0, 248 | "day": 0, 249 | "hour": 0, 250 | "minute": 0 251 | } 252 | } 253 | ], 254 | "keyName": "Tokyo Standard Time" 255 | }, 256 | "globalAppointmentID": "040000008200E00074C5B7101A82E008000000002000F9F62D0AD901000000000000000010000000F00F89203A1BCA479377447BDED6505A", 257 | "creationTime": "Wed, 07 Dec 2022 03:30:27 GMT", 258 | "lastModificationTime": "Wed, 07 Dec 2022 03:30:27 GMT", 259 | "clientSubmitTime": "Wed, 07 Dec 2022 02:23:01 GMT", 260 | "messageDeliveryTime": "Wed, 07 Dec 2022 02:23:01 GMT", 261 | "messageFlags": 17, 262 | "internetCodepage": 50220, 263 | "messageLocaleId": 1041, 264 | "apptStartWhole": "Fri, 06 Jan 2023 03:00:00 GMT", 265 | "apptEndWhole": "Fri, 06 Jan 2023 04:00:00 GMT", 266 | "clipStart": "Thu, 05 Jan 2023 15:00:00 GMT", 267 | "clipEnd": "Sat, 30 Dec 2023 15:00:00 GMT" 268 | } -------------------------------------------------------------------------------- /test/Lanch time every friday in 2023 chgs2.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [ 4 | { 5 | "dataType": "attachment", 6 | "innerMsgContentFields": { 7 | "dataType": "msg", 8 | "attachments": [ 9 | { 10 | "dataType": "attachment", 11 | "name": "JSCoffeeScript_64x.png", 12 | "dataId": 112, 13 | "contentLength": 1538, 14 | "extension": ".png", 15 | "fileNameShort": "JSCoffeeScript_64x.png", 16 | "fileName": "JSCoffeeScript_64x.png", 17 | "creationTime": "Wed, 23 Aug 2017 13:29:43 GMT", 18 | "lastModificationTime": "Fri, 08 Jan 2016 01:37:42 GMT", 19 | "attachmentHidden": false 20 | } 21 | ], 22 | "recipients": [ 23 | { 24 | "dataType": "recipient", 25 | "name": "Unknown", 26 | "addressType": "UNKNOWN", 27 | "email": "Unknown", 28 | "recipType": "to" 29 | } 30 | ], 31 | "messageClass": "IPM.OLE.CLASS.{00061055-0000-0000-C000-000000000046}", 32 | "subject": "Lanch time, every friday, in 2023 [rescheduled!]", 33 | "conversationTopic": "Lanch time, every friday, in 2023 [rescheduled!]", 34 | "normalizedSubject": "Lanch time, every friday, in 2023 [rescheduled!]", 35 | "body": "Changes:\r\n-\tJan 6 cancel\r\n-\tJan 13 rescheduled to Jan 12 (alarm set to 30 mins before, set location, change busy flag, add attachment file, set importance higher)\r\n\r\n \r\n", 36 | "apptLocation": "Awesome coffee shop", 37 | "apptTZDefStartDisplay": { 38 | "rules": [ 39 | { 40 | "flags": 2, 41 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 42 | "bias": -540, 43 | "standardBias": 0, 44 | "daylightBias": 0, 45 | "standardDate": { 46 | "year": 0, 47 | "month": 0, 48 | "dayOfWeek": 0, 49 | "day": 0, 50 | "hour": 0, 51 | "minute": 0 52 | }, 53 | "daylightDate": { 54 | "year": 0, 55 | "month": 0, 56 | "dayOfWeek": 0, 57 | "day": 0, 58 | "hour": 0, 59 | "minute": 0 60 | } 61 | } 62 | ], 63 | "keyName": "Tokyo Standard Time" 64 | }, 65 | "apptTZDefEndDisplay": { 66 | "rules": [ 67 | { 68 | "flags": 2, 69 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 70 | "bias": -540, 71 | "standardBias": 0, 72 | "daylightBias": 0, 73 | "standardDate": { 74 | "year": 0, 75 | "month": 0, 76 | "dayOfWeek": 0, 77 | "day": 0, 78 | "hour": 0, 79 | "minute": 0 80 | }, 81 | "daylightDate": { 82 | "year": 0, 83 | "month": 0, 84 | "dayOfWeek": 0, 85 | "day": 0, 86 | "hour": 0, 87 | "minute": 0 88 | } 89 | } 90 | ], 91 | "keyName": "Tokyo Standard Time" 92 | }, 93 | "creationTime": "Wed, 07 Dec 2022 04:32:54 GMT", 94 | "lastModificationTime": "Wed, 07 Dec 2022 04:32:54 GMT", 95 | "messageFlags": 25, 96 | "messageLocaleId": 1041, 97 | "apptStartWhole": "Thu, 12 Jan 2023 03:00:00 GMT", 98 | "apptEndWhole": "Thu, 12 Jan 2023 04:00:00 GMT", 99 | "clipStart": "Thu, 12 Jan 2023 03:00:00 GMT", 100 | "clipEnd": "Thu, 12 Jan 2023 04:00:00 GMT" 101 | }, 102 | "innerMsgContent": true, 103 | "folderId": 83, 104 | "name": "無題", 105 | "attachmentHidden": true 106 | } 107 | ], 108 | "recipients": [ 109 | { 110 | "dataType": "recipient", 111 | "name": "Unknown", 112 | "addressType": "UNKNOWN", 113 | "email": "Unknown", 114 | "recipType": "to" 115 | } 116 | ], 117 | "messageClass": "IPM.Appointment", 118 | "subject": "Lanch time, every friday, in 2023", 119 | "conversationTopic": "Lanch time, every friday, in 2023", 120 | "senderName": "Unknown", 121 | "senderAddressType": "UNKNOWN", 122 | "senderEmail": "Unknown", 123 | "normalizedSubject": "Lanch time, every friday, in 2023", 124 | "body": "Changes:\r\n-\tJan 6 cancel\r\n-\tJan 13 rescheduled to Jan 12\r\n\r\n", 125 | "apptLocation": "", 126 | "apptTZDefStartDisplay": { 127 | "rules": [ 128 | { 129 | "flags": 2, 130 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 131 | "bias": -540, 132 | "standardBias": 0, 133 | "daylightBias": 0, 134 | "standardDate": { 135 | "year": 0, 136 | "month": 0, 137 | "dayOfWeek": 0, 138 | "day": 0, 139 | "hour": 0, 140 | "minute": 0 141 | }, 142 | "daylightDate": { 143 | "year": 0, 144 | "month": 0, 145 | "dayOfWeek": 0, 146 | "day": 0, 147 | "hour": 0, 148 | "minute": 0 149 | } 150 | } 151 | ], 152 | "keyName": "Tokyo Standard Time" 153 | }, 154 | "apptTZDefEndDisplay": { 155 | "rules": [ 156 | { 157 | "flags": 2, 158 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 159 | "bias": -540, 160 | "standardBias": 0, 161 | "daylightBias": 0, 162 | "standardDate": { 163 | "year": 0, 164 | "month": 0, 165 | "dayOfWeek": 0, 166 | "day": 0, 167 | "hour": 0, 168 | "minute": 0 169 | }, 170 | "daylightDate": { 171 | "year": 0, 172 | "month": 0, 173 | "dayOfWeek": 0, 174 | "day": 0, 175 | "hour": 0, 176 | "minute": 0 177 | } 178 | } 179 | ], 180 | "keyName": "Tokyo Standard Time" 181 | }, 182 | "apptRecur": { 183 | "recurrencePattern": { 184 | "recurFrequency": 8203, 185 | "patternType": 1, 186 | "calendarType": 0, 187 | "firstDateTime": 8640, 188 | "period": 1, 189 | "slidingFlag": 0, 190 | "endType": 8225, 191 | "occurrenceCount": 52, 192 | "firstDOW": 0, 193 | "deletedInstanceDates": [ 194 | 221957280, 195 | 221967360 196 | ], 197 | "modifiedInstanceDates": [ 198 | 221965920 199 | ], 200 | "startDate": 221957280, 201 | "endDate": 222474240, 202 | "patternTypeWeek": { 203 | "dayOfWeekBits": 32 204 | } 205 | }, 206 | "startTimeOffset": 720, 207 | "endTimeOffset": 780, 208 | "exceptionInfo": [ 209 | { 210 | "startDateTime": 221966640, 211 | "endDateTime": 221966700, 212 | "originalStartTime": 221968080, 213 | "overrideFlags": 629, 214 | "subject": "Lanch time, every friday, in 2023 [rescheduled!]", 215 | "reminderDelta": 15, 216 | "location": "Awesome coffee shop", 217 | "busyStatus": 1, 218 | "attachment": 1, 219 | "changeHighlight": 0 220 | } 221 | ] 222 | }, 223 | "timeZoneStruct": { 224 | "bias": -540, 225 | "standardBias": 0, 226 | "daylightBias": -60, 227 | "standardYear": 0, 228 | "standardDate": { 229 | "year": 0, 230 | "month": 0, 231 | "dayOfWeek": 0, 232 | "day": 0, 233 | "hour": 0, 234 | "minute": 0 235 | }, 236 | "daylightYear": 0, 237 | "daylightDate": { 238 | "year": 0, 239 | "month": 0, 240 | "dayOfWeek": 0, 241 | "day": 0, 242 | "hour": 0, 243 | "minute": 0 244 | } 245 | }, 246 | "timeZoneDesc": "(UTC+09:00) 大阪、札幌、東京", 247 | "apptTZDefRecur": { 248 | "rules": [ 249 | { 250 | "flags": 2, 251 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 252 | "bias": -540, 253 | "standardBias": 0, 254 | "daylightBias": -60, 255 | "standardDate": { 256 | "year": 0, 257 | "month": 0, 258 | "dayOfWeek": 0, 259 | "day": 0, 260 | "hour": 0, 261 | "minute": 0 262 | }, 263 | "daylightDate": { 264 | "year": 0, 265 | "month": 0, 266 | "dayOfWeek": 0, 267 | "day": 0, 268 | "hour": 0, 269 | "minute": 0 270 | } 271 | } 272 | ], 273 | "keyName": "Tokyo Standard Time" 274 | }, 275 | "globalAppointmentID": "040000008200E00074C5B7101A82E008000000002000F9F62D0AD901000000000000000010000000F00F89203A1BCA479377447BDED6505A", 276 | "creationTime": "Wed, 07 Dec 2022 04:32:54 GMT", 277 | "lastModificationTime": "Wed, 07 Dec 2022 04:32:54 GMT", 278 | "clientSubmitTime": "Wed, 07 Dec 2022 02:23:01 GMT", 279 | "messageDeliveryTime": "Wed, 07 Dec 2022 02:23:01 GMT", 280 | "messageFlags": 17, 281 | "internetCodepage": 50220, 282 | "messageLocaleId": 1041, 283 | "apptStartWhole": "Fri, 06 Jan 2023 03:00:00 GMT", 284 | "apptEndWhole": "Fri, 06 Jan 2023 04:00:00 GMT", 285 | "clipStart": "Thu, 05 Jan 2023 15:00:00 GMT", 286 | "clipEnd": "Sat, 30 Dec 2023 15:00:00 GMT" 287 | } -------------------------------------------------------------------------------- /src/const.ts: -------------------------------------------------------------------------------- 1 | import { uInt2int } from './utils' 2 | 3 | export default { 4 | FILE_HEADER: uInt2int([0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1]), 5 | MSG: { 6 | UNUSED_BLOCK: -1, 7 | END_OF_CHAIN: -2, 8 | 9 | S_BIG_BLOCK_SIZE: 0x0200, 10 | S_BIG_BLOCK_MARK: 9, 11 | 12 | L_BIG_BLOCK_SIZE: 0x1000, 13 | L_BIG_BLOCK_MARK: 12, 14 | 15 | SMALL_BLOCK_SIZE: 0x0040, 16 | BIG_BLOCK_MIN_DOC_SIZE: 0x1000, 17 | HEADER: { 18 | PROPERTY_START_OFFSET: 0x30, 19 | 20 | BAT_START_OFFSET: 0x4c, 21 | BAT_COUNT_OFFSET: 0x2C, 22 | 23 | SBAT_START_OFFSET: 0x3C, 24 | SBAT_COUNT_OFFSET: 0x40, 25 | 26 | XBAT_START_OFFSET: 0x44, 27 | XBAT_COUNT_OFFSET: 0x48 28 | }, 29 | PROP: { 30 | NO_INDEX: -1, 31 | PROPERTY_SIZE: 0x0080, 32 | 33 | NAME_SIZE_OFFSET: 0x40, 34 | MAX_NAME_LENGTH: (/*NAME_SIZE_OFFSET*/0x40 / 2) - 1, 35 | TYPE_OFFSET: 0x42, 36 | PREVIOUS_PROPERTY_OFFSET: 0x44, 37 | NEXT_PROPERTY_OFFSET: 0x48, 38 | CHILD_PROPERTY_OFFSET: 0x4C, 39 | START_BLOCK_OFFSET: 0x74, 40 | SIZE_OFFSET: 0x78, 41 | TYPE_ENUM: { 42 | DIRECTORY: 1, 43 | DOCUMENT: 2, 44 | ROOT: 5 45 | } 46 | }, 47 | FIELD: { 48 | PREFIX: { 49 | ATTACHMENT: '__attach_version1.0', 50 | RECIPIENT: '__recip_version1.0', 51 | DOCUMENT: '__substg1.', 52 | NAMEID: '__nameid_version1.0' 53 | }, 54 | // example (use fields as needed) 55 | NAME_MAPPING: { 56 | // email specific 57 | '001a': 'messageClass', 58 | '0037': 'subject', 59 | '0c1a': 'senderName', 60 | '0c1e': 'senderAddressType', 61 | '0c1f': 'senderEmail', 62 | '5d01': 'senderSmtpAddress', 63 | '5d02': 'sentRepresentingSmtpAddress', 64 | '5d0a': 'creatorSMTPAddress', 65 | '5d0b': 'lastModifierSMTPAddress', 66 | '1000': 'body', 67 | '007d': 'headers', 68 | '1009': 'compressedRtf', 69 | '3ffa': 'lastModifierName', 70 | '0039': 'clientSubmitTime', 71 | '0e06': 'messageDeliveryTime', 72 | '3fde': 'internetCodepage', 73 | '3ffd': 'messageCodepage', 74 | '3ff1': 'messageLocaleId', 75 | '0e07': 'messageFlags', 76 | '1035': 'messageId', 77 | '3fd9': 'preview', 78 | // attachment specific 79 | '3007': 'creationTime', 80 | '3008': 'lastModificationTime', 81 | '3703': 'extension', 82 | '3704': 'fileNameShort', 83 | '3707': 'fileName', 84 | '3712': 'pidContentId', 85 | '7ffe': 'attachmentHidden', 86 | '370e': 'attachMimeTag', 87 | // recipient specific 88 | '0c15': 'recipType', 89 | '3001': 'name', 90 | '3002': 'addressType', 91 | '3003': 'email', 92 | '39fe': 'smtpAddress', 93 | // contact specific 94 | '3a18': 'departmentName', 95 | '3a44': 'middleName', 96 | '3a05': 'generation', 97 | '3a11': 'surname', 98 | '3a27': 'addressCity', 99 | '3a16': 'companyName', 100 | '3a24': 'businessFaxNumber', 101 | '3a29': 'streetAddress', 102 | '3a51': 'businessHomePage', 103 | '3a06': 'givenName', 104 | '3a09': 'homeTelephoneNumber', 105 | '3a15': 'postalAddress', 106 | '3a17': 'title', 107 | '3a1c': 'mobileTelephoneNumber', 108 | '3a26': 'country', 109 | '3a28': 'stateOrProvince', 110 | '3a2a': 'postalCode', 111 | '3a45': 'displayNamePrefix', 112 | '0070': 'conversationTopic', 113 | '0e1d': 'normalizedSubject', 114 | '3a08': 'businessTelephoneNumber', 115 | '3a0d': 'location', 116 | }, 117 | FULL_NAME_MAPPING: { 118 | '1013001f': 'bodyHtml', 119 | '10130102': 'html', 120 | }, 121 | PIDLID_MAPPING: { 122 | // PSETID_Common 123 | "00062008-0000-0000-c000-000000000046": { 124 | 0x00008520: { id: "PidLidVerbStream", }, 125 | 0x00008524: { id: "PidLidVerbResponse", dispid: "votingResponse", }, 126 | 0x00008580: { id: "PidLidInternetAccountName", dispid: "inetAcctName", }, 127 | }, 128 | // PSETID_Appointment 129 | "00062002-0000-0000-c000-000000000046": { 130 | 0x0000820D: { id: "PidLidAppointmentStartWhole", dispid: "apptStartWhole", }, 131 | 0x0000820E: { id: "PidLidAppointmentEndWhole", dispid: "apptEndWhole", }, 132 | 0x00008235: { id: "PidLidClipStart", dispid: "clipStart", }, 133 | 0x00008236: { id: "PidLidClipEnd", dispid: "clipEnd", }, 134 | 0x00008233: { id: "PidLidTimeZoneStruct", dispid: "timeZoneStruct" }, 135 | 0x00008234: { id: "PidLidTimeZoneDescription", dispid: "timeZoneDesc" }, 136 | 0x0000825E: { id: "PidLidAppointmentTimeZoneDefinitionStartDisplay", dispid: "apptTZDefStartDisplay" }, 137 | 0x0000825F: { id: "PidLidAppointmentTimeZoneDefinitionEndDisplay", dispid: "apptTZDefEndDisplay" }, 138 | 0x00008260: { id: "PidLidAppointmentTimeZoneDefinitionRecur", dispid: "apptTZDefRecur" }, 139 | 0x00008216: { id: "PidLidAppointmentRecur", dispid: "apptRecur" }, 140 | 0x00008208: { id: "PidLidLocation", dispid: "apptLocation", }, 141 | }, 142 | // PSETID_Address 143 | "00062004-0000-0000-c000-000000000046": { 144 | 0x0000802c: { id: "dispidYomiFirstName", dispid: "yomiFirstName", }, 145 | 0x00008083: { id: "dispidEmail1EmailAddress", dispid: "email1EmailAddress", }, 146 | 0x0000802e: { id: "dispidYomiCompanyName", dispid: "yomiCompanyName", }, 147 | 0x000080d2: { id: "PidLidFax3AddressType", dispid: "fax3AddrType", }, 148 | 0x00008080: { id: "PidLidEmail1DisplayName", dispid: "email1DisplayName", }, 149 | 0x00008084: { id: "PidLidEmail1OriginalDisplayName", dispid: "email1OriginalDisplayName", }, 150 | 0x00008005: { id: "PidLidFileUnder", dispid: "fileUnder", }, 151 | 0x0000802d: { id: "PidLidYomiLastName", dispid: "yomiLastName", }, 152 | 0x000080b2: { id: "PidLidFax1AddressType", dispid: "fax1AddrType", }, 153 | 0x000080c3: { id: "PidLidFax2EmailAddress", dispid: "fax2EmailAddress", }, 154 | 0x00008046: { id: "PidLidWorkAddressCity", dispid: "workAddressCity", }, 155 | 0x000080dd: { id: "PidLidAddressCountryCode", dispid: "addressCountryCode", }, 156 | 0x000080c2: { id: "PidLidFax2AddressType", dispid: "fax2AddrType", }, 157 | 0x000080c4: { id: "PidLidFax2OriginalDisplayName", dispid: "fax2OriginalDisplayName", }, 158 | 0x00008048: { id: "PidLidWorkAddressPostalCode", dispid: "workAddressPostalCode", }, 159 | 0x00008045: { id: "PidLidWorkAddressStreet", dispid: "workAddressStreet", }, 160 | 0x00008047: { id: "PidLidWorkAddressState", dispid: "workAddressState", }, 161 | 0x000080db: { id: "PidLidWorkAddressCountryCode", dispid: "workAddressCountryCode", }, 162 | 0x00008049: { id: "PidLidWorkAddressCountry", dispid: "workAddressCountry", }, 163 | 0x0000802b: { id: "PidLidHtml", dispid: "contactHtml", }, 164 | 0x0000801b: { id: "PidLidWorkAddress", dispid: "workAddress", }, 165 | 0x000080b4: { id: "PidLidFax1OriginalDisplayName", dispid: "fax1OriginalDisplayName", }, 166 | 0x00008062: { id: "PidLidInstantMessagingAddress", dispid: "instMsg", }, 167 | 0x00008010: { id: "PidLidDepartment", dispid: "department", }, 168 | 0x000080b3: { id: "PidLidFax1EmailAddress", dispid: "fax1EmailAddress", }, 169 | 0x000080d4: { id: "PidLidFax3OriginalDisplayName", dispid: "fax3OriginalDisplayName", }, 170 | 0x000080d3: { id: "PidLidFax3EmailAddress", dispid: "fax3EmailAddress", }, 171 | }, 172 | // PSETID_Meeting 173 | "6ed8da90-450b-101b-98da-00aa003f1305": { 174 | 0x00000003: { id: "PidLidGlobalObjectId", dispid: "globalAppointmentID", }, 175 | 0x00000028: { id: "PidLidOldLocation", dispid: "apptOldLocation", }, 176 | }, 177 | }, 178 | CLASS_MAPPING: { 179 | ATTACHMENT_DATA: '3701' 180 | }, 181 | TYPE_MAPPING: { 182 | '001e': 'string', 183 | '001f': 'unicode', 184 | '0040': 'time', 185 | '0102': 'binary', 186 | '0003': 'integer', 187 | '000b': 'boolean', 188 | }, 189 | DIR_TYPE: { 190 | INNER_MSG: '000d' 191 | } 192 | } 193 | } 194 | } -------------------------------------------------------------------------------- /test/Friday Lunch.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": "msg", 3 | "attachments": [ 4 | { 5 | "dataType": "attachment", 6 | "innerMsgContentFields": { 7 | "dataType": "msg", 8 | "attachments": [], 9 | "recipients": [ 10 | { 11 | "dataType": "recipient", 12 | "name": "UnoKenji", 13 | "addressType": "EX", 14 | "email": "/o=ExchangeLabs/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=a17940f5293148ccb36a0455deaf4f84-ku", 15 | "smtpAddress": "ku@digitaldolphins.onmicrosoft.com", 16 | "recipType": "to" 17 | } 18 | ], 19 | "messageClass": "IPM.OLE.CLASS.{00061055-0000-0000-C000-000000000046}", 20 | "subject": "Monday Lunch", 21 | "conversationTopic": "Monday Lunch", 22 | "normalizedSubject": "Monday Lunch", 23 | "lastModifierName": "ku@digitaldolphins.onmicrosoft.com", 24 | "apptLocation": "", 25 | "apptTZDefStartDisplay": { 26 | "rules": [ 27 | { 28 | "flags": 2, 29 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 30 | "bias": -540, 31 | "standardBias": 0, 32 | "daylightBias": -60, 33 | "standardDate": { 34 | "year": 0, 35 | "month": 0, 36 | "dayOfWeek": 0, 37 | "day": 0, 38 | "hour": 0, 39 | "minute": 0 40 | }, 41 | "daylightDate": { 42 | "year": 0, 43 | "month": 0, 44 | "dayOfWeek": 0, 45 | "day": 0, 46 | "hour": 0, 47 | "minute": 0 48 | } 49 | } 50 | ], 51 | "keyName": "Tokyo Standard Time" 52 | }, 53 | "apptTZDefEndDisplay": { 54 | "rules": [ 55 | { 56 | "flags": 2, 57 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 58 | "bias": -540, 59 | "standardBias": 0, 60 | "daylightBias": -60, 61 | "standardDate": { 62 | "year": 0, 63 | "month": 0, 64 | "dayOfWeek": 0, 65 | "day": 0, 66 | "hour": 0, 67 | "minute": 0 68 | }, 69 | "daylightDate": { 70 | "year": 0, 71 | "month": 0, 72 | "dayOfWeek": 0, 73 | "day": 0, 74 | "hour": 0, 75 | "minute": 0 76 | } 77 | } 78 | ], 79 | "keyName": "Tokyo Standard Time" 80 | }, 81 | "creationTime": "Fri, 06 Jan 2023 16:26:34 GMT", 82 | "lastModificationTime": "Fri, 06 Jan 2023 16:26:34 GMT", 83 | "messageFlags": 9, 84 | "apptStartWhole": "Mon, 09 Jan 2023 03:00:00 GMT", 85 | "apptEndWhole": "Mon, 09 Jan 2023 04:00:00 GMT", 86 | "clipStart": "Mon, 09 Jan 2023 03:00:00 GMT", 87 | "clipEnd": "Mon, 09 Jan 2023 04:00:00 GMT" 88 | }, 89 | "innerMsgContent": true, 90 | "folderId": 89, 91 | "name": "無題", 92 | "attachmentHidden": true 93 | }, 94 | { 95 | "dataType": "attachment", 96 | "innerMsgContentFields": { 97 | "dataType": "msg", 98 | "attachments": [], 99 | "recipients": [], 100 | "messageClass": "IPM.OLE.CLASS.{00061055-0000-0000-C000-000000000046}", 101 | "lastModifierName": "ku@digitaldolphins.onmicrosoft.com", 102 | "apptTZDefStartDisplay": { 103 | "rules": [ 104 | { 105 | "flags": 2, 106 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 107 | "bias": -540, 108 | "standardBias": 0, 109 | "daylightBias": -60, 110 | "standardDate": { 111 | "year": 0, 112 | "month": 0, 113 | "dayOfWeek": 0, 114 | "day": 0, 115 | "hour": 0, 116 | "minute": 0 117 | }, 118 | "daylightDate": { 119 | "year": 0, 120 | "month": 0, 121 | "dayOfWeek": 0, 122 | "day": 0, 123 | "hour": 0, 124 | "minute": 0 125 | } 126 | } 127 | ], 128 | "keyName": "Tokyo Standard Time" 129 | }, 130 | "apptTZDefEndDisplay": { 131 | "rules": [ 132 | { 133 | "flags": 2, 134 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 135 | "bias": -540, 136 | "standardBias": 0, 137 | "daylightBias": -60, 138 | "standardDate": { 139 | "year": 0, 140 | "month": 0, 141 | "dayOfWeek": 0, 142 | "day": 0, 143 | "hour": 0, 144 | "minute": 0 145 | }, 146 | "daylightDate": { 147 | "year": 0, 148 | "month": 0, 149 | "dayOfWeek": 0, 150 | "day": 0, 151 | "hour": 0, 152 | "minute": 0 153 | } 154 | } 155 | ], 156 | "keyName": "Tokyo Standard Time" 157 | } 158 | }, 159 | "innerMsgContent": true, 160 | "folderId": 122, 161 | "name": "無題", 162 | "attachmentHidden": true 163 | } 164 | ], 165 | "recipients": [ 166 | { 167 | "dataType": "recipient", 168 | "name": "UnoKenji", 169 | "addressType": "EX", 170 | "email": "/o=ExchangeLabs/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=a17940f5293148ccb36a0455deaf4f84-ku", 171 | "smtpAddress": "ku@digitaldolphins.onmicrosoft.com", 172 | "recipType": "to" 173 | } 174 | ], 175 | "messageClass": "IPM.Appointment", 176 | "subject": "Friday Lunch", 177 | "conversationTopic": "Friday Lunch", 178 | "senderName": "UnoKenji", 179 | "senderAddressType": "EX", 180 | "senderEmail": "/o=ExchangeLabs/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=a17940f5293148ccb36a0455deaf4f84-ku", 181 | "normalizedSubject": "Friday Lunch", 182 | "body": "\r\n", 183 | "lastModifierName": "ku@digitaldolphins.onmicrosoft.com", 184 | "apptLocation": "", 185 | "apptTZDefStartDisplay": { 186 | "rules": [ 187 | { 188 | "flags": 2, 189 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 190 | "bias": -540, 191 | "standardBias": 0, 192 | "daylightBias": -60, 193 | "standardDate": { 194 | "year": 0, 195 | "month": 0, 196 | "dayOfWeek": 0, 197 | "day": 0, 198 | "hour": 0, 199 | "minute": 0 200 | }, 201 | "daylightDate": { 202 | "year": 0, 203 | "month": 0, 204 | "dayOfWeek": 0, 205 | "day": 0, 206 | "hour": 0, 207 | "minute": 0 208 | } 209 | } 210 | ], 211 | "keyName": "Tokyo Standard Time" 212 | }, 213 | "apptTZDefEndDisplay": { 214 | "rules": [ 215 | { 216 | "flags": 2, 217 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 218 | "bias": -540, 219 | "standardBias": 0, 220 | "daylightBias": -60, 221 | "standardDate": { 222 | "year": 0, 223 | "month": 0, 224 | "dayOfWeek": 0, 225 | "day": 0, 226 | "hour": 0, 227 | "minute": 0 228 | }, 229 | "daylightDate": { 230 | "year": 0, 231 | "month": 0, 232 | "dayOfWeek": 0, 233 | "day": 0, 234 | "hour": 0, 235 | "minute": 0 236 | } 237 | } 238 | ], 239 | "keyName": "Tokyo Standard Time" 240 | }, 241 | "apptRecur": { 242 | "recurrencePattern": { 243 | "recurFrequency": 8203, 244 | "patternType": 1, 245 | "calendarType": 0, 246 | "firstDateTime": 8640, 247 | "period": 1, 248 | "slidingFlag": 0, 249 | "endType": 8225, 250 | "occurrenceCount": 52, 251 | "firstDOW": 0, 252 | "deletedInstanceDates": [ 253 | 221957280, 254 | 221967360, 255 | 221977440 256 | ], 257 | "modifiedInstanceDates": [ 258 | 221961600, 259 | 221977440 260 | ], 261 | "startDate": 221957280, 262 | "endDate": 222474240, 263 | "patternTypeWeek": { 264 | "dayOfWeekBits": 32 265 | } 266 | }, 267 | "startTimeOffset": 720, 268 | "endTimeOffset": 780, 269 | "exceptionInfo": [ 270 | { 271 | "startDateTime": 221962320, 272 | "endDateTime": 221962380, 273 | "originalStartTime": 221968080, 274 | "overrideFlags": 1, 275 | "subject": "Monday Lunch", 276 | "changeHighlight": 0 277 | }, 278 | { 279 | "startDateTime": 221978160, 280 | "endDateTime": 221978220, 281 | "originalStartTime": 221978160, 282 | "overrideFlags": 32, 283 | "busyStatus": 3, 284 | "changeHighlight": 0 285 | } 286 | ] 287 | }, 288 | "timeZoneStruct": { 289 | "bias": -540, 290 | "standardBias": 0, 291 | "daylightBias": -60, 292 | "standardYear": 0, 293 | "standardDate": { 294 | "year": 0, 295 | "month": 0, 296 | "dayOfWeek": 0, 297 | "day": 0, 298 | "hour": 0, 299 | "minute": 0 300 | }, 301 | "daylightYear": 0, 302 | "daylightDate": { 303 | "year": 0, 304 | "month": 0, 305 | "dayOfWeek": 0, 306 | "day": 0, 307 | "hour": 0, 308 | "minute": 0 309 | } 310 | }, 311 | "globalAppointmentID": "040000008200E00074C5B7101A82E00800000000404F1AC33622D901000000000000000010000000417B5EFFCF8AF14DB668B9BA15609337", 312 | "timeZoneDesc": "(UTC+09:00) 大阪、札幌、東京", 313 | "apptTZDefRecur": { 314 | "rules": [ 315 | { 316 | "flags": 2, 317 | "start": "Mon, 01 Jan 1601 00:00:00 GMT", 318 | "bias": -540, 319 | "standardBias": 0, 320 | "daylightBias": -60, 321 | "standardDate": { 322 | "year": 0, 323 | "month": 0, 324 | "dayOfWeek": 0, 325 | "day": 0, 326 | "hour": 0, 327 | "minute": 0 328 | }, 329 | "daylightDate": { 330 | "year": 0, 331 | "month": 0, 332 | "dayOfWeek": 0, 333 | "day": 0, 334 | "hour": 0, 335 | "minute": 0 336 | } 337 | } 338 | ], 339 | "keyName": "Tokyo Standard Time" 340 | }, 341 | "inetAcctName": "ku@digitaldolphins.onmicrosoft.com", 342 | "creationTime": "Fri, 06 Jan 2023 16:26:34 GMT", 343 | "lastModificationTime": "Fri, 06 Jan 2023 16:26:34 GMT", 344 | "clientSubmitTime": "Fri, 06 Jan 2023 16:25:22 GMT", 345 | "messageDeliveryTime": "Fri, 06 Jan 2023 16:25:22 GMT", 346 | "messageFlags": 17, 347 | "internetCodepage": 50220, 348 | "messageLocaleId": 1041, 349 | "apptStartWhole": "Fri, 06 Jan 2023 03:00:00 GMT", 350 | "apptEndWhole": "Fri, 06 Jan 2023 04:00:00 GMT", 351 | "clipStart": "Thu, 05 Jan 2023 15:00:00 GMT", 352 | "clipEnd": "Sat, 30 Dec 2023 15:00:00 GMT" 353 | } -------------------------------------------------------------------------------- /test/A schedule.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\adeflang1095\ansi\ansicpg932\uc2\adeff31507\deff0\stshfdbch31505\stshfloch31506\stshfhich31506\stshfbi31507\deflang1033\deflangfe1041\themelang1033\themelangfe1041\themelangcs1095{\fonttbl{\f1\fbidi \fswiss\fcharset0\fprq2{\*\panose 020b0604020202020204}Arial;} 2 | {\f15\fbidi \fmodern\fcharset128\fprq1{\*\panose 020b0609070205080204}\'82\'6c\'82\'72 \'83\'53\'83\'56\'83\'62\'83\'4e{\*\falt MS Gothic};}{\f29\fbidi \froman\fcharset1\fprq2{\*\panose 020b0502040204020203}Shruti;} 3 | {\f29\fbidi \froman\fcharset1\fprq2{\*\panose 020b0502040204020203}Shruti;}{\f38\fbidi \fmodern\fcharset128\fprq1{\*\panose 020b0609070205080204}@\'82\'6c\'82\'72 \'83\'53\'83\'56\'83\'62\'83\'4e;} 4 | {\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f31501\fbidi \fmodern\fcharset128\fprq1{\*\panose 020b0609070205080204}\'82\'6c\'82\'72 \'83\'53\'83\'56\'83\'62\'83\'4e{\*\falt MS Gothic};} 5 | {\f31502\fbidi \fswiss\fcharset0\fprq2{\*\panose 020b0604020202020204}Arial;}{\f31503\fbidi \froman\fcharset1\fprq2{\*\panose 020b0502040204020203}Shruti;}{\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} 6 | {\f31505\fbidi \fmodern\fcharset128\fprq1{\*\panose 020b0609070205080204}\'82\'6c\'82\'72 \'83\'53\'83\'56\'83\'62\'83\'4e{\*\falt MS Gothic};}{\f31506\fbidi \fswiss\fcharset0\fprq2{\*\panose 020b0604020202020204}Arial;} 7 | {\f31507\fbidi \froman\fcharset1\fprq2{\*\panose 020b0502040204020203}Shruti;}{\f49\fbidi \fswiss\fcharset238\fprq2 Arial CE;}{\f50\fbidi \fswiss\fcharset204\fprq2 Arial Cyr;}{\f52\fbidi \fswiss\fcharset161\fprq2 Arial Greek;} 8 | {\f53\fbidi \fswiss\fcharset162\fprq2 Arial Tur;}{\f54\fbidi \fswiss\fcharset177\fprq2 Arial (Hebrew);}{\f55\fbidi \fswiss\fcharset178\fprq2 Arial (Arabic);}{\f56\fbidi \fswiss\fcharset186\fprq2 Arial Baltic;} 9 | {\f57\fbidi \fswiss\fcharset163\fprq2 Arial (Vietnamese);}{\f191\fbidi \fmodern\fcharset0\fprq1 MS Gothic Western{\*\falt MS Gothic};}{\f189\fbidi \fmodern\fcharset238\fprq1 MS Gothic CE{\*\falt MS Gothic};} 10 | {\f190\fbidi \fmodern\fcharset204\fprq1 MS Gothic Cyr{\*\falt MS Gothic};}{\f192\fbidi \fmodern\fcharset161\fprq1 MS Gothic Greek{\*\falt MS Gothic};}{\f193\fbidi \fmodern\fcharset162\fprq1 MS Gothic Tur{\*\falt MS Gothic};} 11 | {\f196\fbidi \fmodern\fcharset186\fprq1 MS Gothic Baltic{\*\falt MS Gothic};}{\f421\fbidi \fmodern\fcharset0\fprq1 @\'82\'6c\'82\'72 \'83\'53\'83\'56\'83\'62\'83\'4e Western;} 12 | {\f419\fbidi \fmodern\fcharset238\fprq1 @\'82\'6c\'82\'72 \'83\'53\'83\'56\'83\'62\'83\'4e CE;}{\f420\fbidi \fmodern\fcharset204\fprq1 @\'82\'6c\'82\'72 \'83\'53\'83\'56\'83\'62\'83\'4e Cyr;} 13 | {\f422\fbidi \fmodern\fcharset161\fprq1 @\'82\'6c\'82\'72 \'83\'53\'83\'56\'83\'62\'83\'4e Greek;}{\f423\fbidi \fmodern\fcharset162\fprq1 @\'82\'6c\'82\'72 \'83\'53\'83\'56\'83\'62\'83\'4e Tur;} 14 | {\f426\fbidi \fmodern\fcharset186\fprq1 @\'82\'6c\'82\'72 \'83\'53\'83\'56\'83\'62\'83\'4e Baltic;}{\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} 15 | {\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} 16 | {\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} 17 | {\f31520\fbidi \fmodern\fcharset0\fprq1 MS Gothic Western{\*\falt MS Gothic};}{\f31518\fbidi \fmodern\fcharset238\fprq1 MS Gothic CE{\*\falt MS Gothic};}{\f31519\fbidi \fmodern\fcharset204\fprq1 MS Gothic Cyr{\*\falt MS Gothic};} 18 | {\f31521\fbidi \fmodern\fcharset161\fprq1 MS Gothic Greek{\*\falt MS Gothic};}{\f31522\fbidi \fmodern\fcharset162\fprq1 MS Gothic Tur{\*\falt MS Gothic};}{\f31525\fbidi \fmodern\fcharset186\fprq1 MS Gothic Baltic{\*\falt MS Gothic};} 19 | {\f31528\fbidi \fswiss\fcharset238\fprq2 Arial CE;}{\f31529\fbidi \fswiss\fcharset204\fprq2 Arial Cyr;}{\f31531\fbidi \fswiss\fcharset161\fprq2 Arial Greek;}{\f31532\fbidi \fswiss\fcharset162\fprq2 Arial Tur;} 20 | {\f31533\fbidi \fswiss\fcharset177\fprq2 Arial (Hebrew);}{\f31534\fbidi \fswiss\fcharset178\fprq2 Arial (Arabic);}{\f31535\fbidi \fswiss\fcharset186\fprq2 Arial Baltic;}{\f31536\fbidi \fswiss\fcharset163\fprq2 Arial (Vietnamese);} 21 | {\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} 22 | {\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} 23 | {\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f31560\fbidi \fmodern\fcharset0\fprq1 MS Gothic Western{\*\falt MS Gothic};}{\f31558\fbidi \fmodern\fcharset238\fprq1 MS Gothic CE{\*\falt MS Gothic};} 24 | {\f31559\fbidi \fmodern\fcharset204\fprq1 MS Gothic Cyr{\*\falt MS Gothic};}{\f31561\fbidi \fmodern\fcharset161\fprq1 MS Gothic Greek{\*\falt MS Gothic};}{\f31562\fbidi \fmodern\fcharset162\fprq1 MS Gothic Tur{\*\falt MS Gothic};} 25 | {\f31565\fbidi \fmodern\fcharset186\fprq1 MS Gothic Baltic{\*\falt MS Gothic};}{\f31568\fbidi \fswiss\fcharset238\fprq2 Arial CE;}{\f31569\fbidi \fswiss\fcharset204\fprq2 Arial Cyr;}{\f31571\fbidi \fswiss\fcharset161\fprq2 Arial Greek;} 26 | {\f31572\fbidi \fswiss\fcharset162\fprq2 Arial Tur;}{\f31573\fbidi \fswiss\fcharset177\fprq2 Arial (Hebrew);}{\f31574\fbidi \fswiss\fcharset178\fprq2 Arial (Arabic);}{\f31575\fbidi \fswiss\fcharset186\fprq2 Arial Baltic;} 27 | {\f31576\fbidi \fswiss\fcharset163\fprq2 Arial (Vietnamese);}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255; 28 | \red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;\red5\green99\blue193;\red149\green79\blue114;}{\*\defchp 29 | \fs21\kerning2\loch\af31506\hich\af31506\dbch\af31505 }{\*\defpap \ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{ 30 | \qj \li0\ri0\nowidctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \fs21\lang1033\langfe1041\kerning2\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1041 31 | \snext0 \sqformat \spriority0 Normal;} 32 | {\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;} 33 | {\*\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv 34 | \ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \fs21\lang1033\langfe1041\kerning2\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1041 35 | \snext11 \ssemihidden \sunhideused Normal Table;} 36 | {\*\cs15 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf17 \sbasedon10 \ssemihidden \sunhideused \styrsid7929896 Hyperlink;} 37 | {\*\cs16 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf18 \sbasedon10 \ssemihidden \sunhideused \styrsid7929896 FollowedHyperlink;} 38 | {\*\cs17 \additive \rtlch\fcs1 \af31507\afs22 \ltrch\fcs0 \fs22\cf0\loch\f31506\hich\af31506\dbch\af31505 \sbasedon10 \ssemihidden \spriority0 \spersonal \scompose \styrsid7929896 39 | \'93\'64\'8e\'71\'83\'81\'81\'5b\'83\'8b\'82\'cc\'83\'58\'83\'5e\'83\'43\'83\'8b17;}}{\*\revtbl {Unknown;}}{\*\rsidtbl \rsid5716985\rsid7929896}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1} 40 | {\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}}\paperw12240\paperh15840\margl1701\margr1701\margt1985\margb1701\gutter0\ltrsect 41 | \deftab840\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont1\relyonvml0\donotembedlingdata0\grfdocevents0\validatexml1\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors1\formshade\horzdoc\dgmargin\dghspace180\dgvspace180 42 | \dghorigin150\dgvorigin0\dghshow0\dgvshow2\jcompress\viewkind5\viewscale100\splytwnine\ftnlytwnine\htmautsp\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct\asianbrkrule\newtblstyruls 43 | \nogrowautofit\usenormstyforlist\noindnmbrts\felnbrelev\nocxsptable\indrlsweleven\noafcnsttbl\afelev\utinl\hwelev\spltpgpar\notcvasp\notbrkcnstfrctbl\notvatxbx\krnprsnet\cachedcolbal \nouicompat \fet0{\*\wgrffmtfilter 2450}\nofeaturethrottle1 44 | \ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\endnhere\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta \dbch .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta \dbch .}}{\*\pnseclvl3 45 | \pndec\pnstart1\pnindent720\pnhang {\pntxta \dbch .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta \dbch )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb \dbch (}{\pntxta \dbch )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang 46 | {\pntxtb \dbch (}{\pntxta \dbch )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb \dbch (}{\pntxta \dbch )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb \dbch (}{\pntxta \dbch )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang 47 | {\pntxtb \dbch (}{\pntxta \dbch )}}\pard\plain \ltrpar\qj \li0\ri0\nowidctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7929896 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 48 | \fs21\lang1033\langfe1041\kerning2\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1041 {\rtlch\fcs1 \af31507 \ltrch\fcs0 \fs20\cf0\insrsid7929896 \hich\af31506\dbch\af31505\loch\f31506 A}{\rtlch\fcs1 \af31507 \ltrch\fcs0 49 | \fs20\cf0\insrsid7929896 \hich\af31506\dbch\af31505\loch\f31506 message body.}{\rtlch\fcs1 \af31507 \ltrch\fcs0 \fs20\cf0\insrsid7929896\charrsid7929896 50 | \par }} -------------------------------------------------------------------------------- /cli.js: -------------------------------------------------------------------------------- 1 | const { Command } = require('commander'); 2 | const program = new Command(); 3 | 4 | const MsgReader = require('./lib/MsgReader').default; 5 | const { props, typeNames } = require('./lib/Defs'); 6 | const { Reader, TypeEnum } = require('./lib/Reader'); 7 | const { burn } = require('./lib/Burner'); 8 | 9 | const fs = require('fs'); 10 | const path = require('path'); 11 | const { decompressRTF } = require('@kenjiuno/decompressrtf'); 12 | 13 | program 14 | .command('parse ') 15 | .description('Parse msg file and print parsed structure') 16 | .option('-f, --full-json', 'print full JSON') 17 | .option('-i, --include-raw-props', 'include raw (and also unknown) props') 18 | .action((msgFilePath, options) => { 19 | const msgFileBuffer = fs.readFileSync(msgFilePath) 20 | const testMsg = new MsgReader(msgFileBuffer) 21 | testMsg.parserConfig = testMsg.parserConfig || {}; 22 | if (options.includeRawProps) { 23 | testMsg.parserConfig.includeRawProps = true; 24 | } 25 | const testMsgInfo = testMsg.getFileData(); 26 | console.log( 27 | options.fullJson 28 | ? JSON.stringify(testMsgInfo, null, 2) 29 | : testMsgInfo 30 | ); 31 | }); 32 | 33 | program 34 | .command('rtf [saveToRtfFilePath]') 35 | .description('Parse msg file and print decompressed rtf') 36 | .action((msgFilePath, saveToRtfFilePath) => { 37 | const msgFileBuffer = fs.readFileSync(msgFilePath) 38 | const testMsg = new MsgReader(msgFileBuffer) 39 | const testMsgInfo = testMsg.getFileData() 40 | 41 | const body = Buffer.from(decompressRTF(testMsgInfo.compressedRtf)) 42 | 43 | if (typeof saveToRtfFilePath === "string" && saveToRtfFilePath.length >= 1) { 44 | fs.writeFileSync(saveToRtfFilePath, body); 45 | } 46 | else { 47 | console.log(body.toString("utf8")); 48 | } 49 | }); 50 | 51 | function listAttachmentsRecursively(fieldsData, delimiter) { 52 | const attachments = [] 53 | 54 | const walk = (fieldsData, prefix, attachments) => { 55 | for (const att of fieldsData.attachments) { 56 | if (att.innerMsgContent) { 57 | attachments.push({ 58 | fileName: prefix + att.name + ".msg", 59 | attachmentRef: att, 60 | }) 61 | walk(att.innerMsgContentFields, att.name + delimiter, attachments); 62 | } 63 | else { 64 | attachments.push({ 65 | fileName: prefix + att.fileName, 66 | attachmentRef: att, 67 | }) 68 | } 69 | } 70 | } 71 | 72 | walk(fieldsData, "", attachments) 73 | 74 | return attachments 75 | } 76 | 77 | program 78 | .command('list-att ') 79 | .description('Parse msg file and list attachment file names') 80 | .action((msgFilePath) => { 81 | const msgFileBuffer = fs.readFileSync(msgFilePath) 82 | const testMsg = new MsgReader(msgFileBuffer) 83 | const testMsgInfo = testMsg.getFileData() 84 | 85 | const attachments = listAttachmentsRecursively(testMsgInfo, "_"); 86 | for (let attachment of attachments) { 87 | console.log(attachment.fileName) 88 | } 89 | }); 90 | 91 | program 92 | .command('save-att ') 93 | .description('Parse msg file and write all attachment files') 94 | .action((msgFilePath, saveToDir) => { 95 | const msgFileBuffer = fs.readFileSync(msgFilePath) 96 | const testMsg = new MsgReader(msgFileBuffer) 97 | const testMsgInfo = testMsg.getFileData() 98 | 99 | fs.mkdirSync(path.resolve(saveToDir), { recursive: true }) 100 | 101 | const attachments = listAttachmentsRecursively(testMsgInfo, "_"); 102 | for (let attachment of attachments) { 103 | const attFilePath = path.resolve(saveToDir, attachment.fileName); 104 | fs.writeFileSync(attFilePath, testMsg.getAttachment(attachment.attachmentRef).content) 105 | } 106 | }); 107 | 108 | program 109 | .command('dump ') 110 | .description('Dump msg file and print data') 111 | .option('-p, --print-raw-data', 'print raw data') 112 | .action((msgFilePath, options) => { 113 | const msgFileBuffer = fs.readFileSync(msgFilePath) 114 | const testMsg = new MsgReader(msgFileBuffer) 115 | let msgIndex = 0 116 | testMsg.parserConfig = { 117 | propertyObserver: (fields, tag, raw) => { 118 | if (fields.msgIndex === undefined) { 119 | fields.msgIndex = msgIndex++; 120 | } 121 | { 122 | const key = tag.toString(16).padStart(8, "0").toUpperCase(); 123 | const prop = props.filter(it => it.key === key).shift(); 124 | const type = typeNames[parseInt(key.substr(4), 16)]; 125 | console.info( 126 | "msgIdx:", fields.msgIndex, 127 | "dataType:", `'${fields.dataType}'`, 128 | "tag:", `0x${key}`, 129 | "name:", prop && prop.name || null, 130 | "type:", type && type || null, 131 | "size:", raw && raw.byteLength, 132 | "data:", options.printRawData ? raw : undefined, 133 | ) 134 | } 135 | } 136 | } 137 | const testMsgInfo = testMsg.getFileData() 138 | }); 139 | 140 | program 141 | .command('expose ') 142 | .description('Expose files/folders in Compound File Binary Format (CFBF)') 143 | .action((msgFilePath, exportToDir, options) => { 144 | const msgFileBuffer = fs.readFileSync(msgFilePath); 145 | const store = new Reader(msgFileBuffer); 146 | store.parse(); 147 | function expose(folder, saveTo) { 148 | fs.mkdir(saveTo, { recursive: true }, (err) => { 149 | if (err) { 150 | return; 151 | } 152 | for (let fileName of folder.fileNames()) { 153 | const array = folder.readFile(fileName); 154 | const path = saveTo + "/" + fileName; 155 | console.info(path); 156 | fs.writeFileSync(path, (array === null) ? [] : array); 157 | } 158 | for (let subFolder of folder.subFolders()) { 159 | expose(subFolder, saveTo + "/" + subFolder.name); 160 | } 161 | }); 162 | } 163 | expose(store.rootFolder(), exportToDir); 164 | }); 165 | 166 | 167 | program 168 | .command('makecfbf ') 169 | .description('Create a Compound File Binary Format (CFBF) from files/folders') 170 | .action((msgFilePath, importFromDir, options) => { 171 | const entries = [ 172 | { 173 | name: "Root Entry", 174 | type: TypeEnum.ROOT, 175 | children: [], 176 | length: 0, 177 | } 178 | ]; 179 | 180 | function addFolder(dir, parentIndex) { 181 | for (const fileName of fs.readdirSync(dir)) { 182 | const fullPath = path.join(dir, fileName); 183 | const stat = fs.statSync(fullPath); 184 | const index = entries.length; 185 | entries[parentIndex].children.push(index); 186 | if (stat.isDirectory()) { 187 | entries.push({ 188 | name: fileName, 189 | type: TypeEnum.DIRECTORY, 190 | children: [], 191 | length: 0, 192 | }); 193 | addFolder(fullPath, index); 194 | } 195 | else { 196 | entries.push({ 197 | name: fileName, 198 | type: TypeEnum.DOCUMENT, 199 | binaryProvider: () => { 200 | const b = fs.readFileSync(fullPath); 201 | const a = new Uint8Array(b, b.byteOffset, b.byteLength); 202 | a.set(b); 203 | return a; 204 | }, 205 | length: stat.size, 206 | }); 207 | } 208 | } 209 | } 210 | 211 | addFolder(importFromDir, 0); 212 | 213 | const array = burn(entries); 214 | fs.writeFileSync(msgFilePath, array); 215 | }); 216 | 217 | program 218 | .command('html ') 219 | .description('Parse msg file and display 1013001f:bodyHtml or 10130102:html') 220 | .option('-e, --encoding ', 'The encoding type to decode binary html.', 'utf8') 221 | .action((msgFilePath, options) => { 222 | const msgFileBuffer = fs.readFileSync(msgFilePath); 223 | const testMsg = new MsgReader(msgFileBuffer); 224 | const testMsgInfo = testMsg.getFileData(); 225 | if (testMsgInfo.html !== undefined) { 226 | console.log(Buffer.from(testMsgInfo.html).toString(options.encoding)); 227 | } 228 | else if (testMsgInfo.bodyHtml !== undefined) { 229 | console.log(testMsgInfo.bodyHtml); 230 | } 231 | else { 232 | console.warn("no html is contained."); 233 | } 234 | }); 235 | 236 | program 237 | .command('walk ') 238 | .description('Walk entire msg file as a raw CFBF') 239 | .action((msgFilePath, options) => { 240 | const msgFileBuffer = fs.readFileSync(msgFilePath); 241 | const reader = new Reader(msgFileBuffer); 242 | reader.parse(); 243 | 244 | function walk(folder, prefix) { 245 | console.info("Walking folder:", prefix); 246 | for (let fileSet of folder.fileNameSets()) { 247 | const contents = fileSet.provider(); 248 | console.info("Verify file:", fileSet.name, "(", fileSet.length, ")", "read", contents.length, "bytes"); 249 | if (fileSet.length != contents.length) { 250 | throw new Error(); 251 | } 252 | } 253 | for (let subFolder of folder.subFolders()) { 254 | walk(subFolder, `${prefix}${subFolder.name}/`); 255 | } 256 | } 257 | 258 | walk(reader.rootFolder(), "/"); 259 | }); 260 | 261 | program 262 | .command('dummy1') 263 | .action(() => { 264 | const msgFileBuffer = fs.readFileSync('test/msgInMsg.msg'); 265 | const testMsg = new MsgReader(msgFileBuffer); 266 | const testMsgInfo = testMsg.getFileData(); 267 | const testMsgAttachment0 = testMsg.getAttachment(0); 268 | console.log(testMsgAttachment0); 269 | }); 270 | 271 | program 272 | .command('dummy2') 273 | .action(() => { 274 | const msgFileBuffer = fs.readFileSync('test/msgInMsg.msg'); 275 | const testMsg = new MsgReader(msgFileBuffer) 276 | const testMsgInfo = testMsg.getFileData() 277 | 278 | for (const att of testMsgInfo.attachments) { 279 | const attachment = testMsg.getAttachment(att); 280 | console.log(attachment.fileName); 281 | 282 | //console.log(Buffer.from(attachment.content).toString('base64')); 283 | fs.writeFileSync("save-" + attachment.fileName, attachment.content); 284 | } 285 | }); 286 | 287 | program 288 | .parse(process.argv); 289 | -------------------------------------------------------------------------------- /test/sent2.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\adeflang1095\ansi\ansicpg932\uc2\adeff31507\deff0\stshfdbch31505\stshfloch31506\stshfhich31506\stshfbi31507\deflang1033\deflangfe1041\themelang1033\themelangfe1041\themelangcs1095{\fonttbl{\f1\fbidi \fswiss\fcharset0\fprq2{\*\panose 020b0604020202020204}Arial;}{\f2\fbidi \fmodern\fcharset0\fprq1{\*\panose 02070309020205020404}Courier New;} 2 | {\f15\fbidi \fmodern\fcharset128\fprq1{\*\panose 020b0609070205080204}\'82\'6c\'82\'72 \'83\'53\'83\'56\'83\'62\'83\'4e{\*\falt MS Gothic};}{\f29\fbidi \froman\fcharset1\fprq2{\*\panose 020b0502040204020203}Shruti{\*\falt Segoe UI};} 3 | {\f29\fbidi \froman\fcharset1\fprq2{\*\panose 020b0502040204020203}Shruti{\*\falt Segoe UI};}{\f39\fbidi \fmodern\fcharset128\fprq1{\*\panose 020b0609070205080204}@\'82\'6c\'82\'72 \'83\'53\'83\'56\'83\'62\'83\'4e;} 4 | {\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f31501\fbidi \fmodern\fcharset128\fprq1{\*\panose 020b0609070205080204}\'82\'6c\'82\'72 \'83\'53\'83\'56\'83\'62\'83\'4e{\*\falt MS Gothic};} 5 | {\f31502\fbidi \fswiss\fcharset0\fprq2{\*\panose 020b0604020202020204}Arial;}{\f31503\fbidi \froman\fcharset1\fprq2{\*\panose 020b0502040204020203}Shruti{\*\falt Segoe UI};} 6 | {\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f31505\fbidi \fmodern\fcharset128\fprq1{\*\panose 020b0609070205080204}\'82\'6c\'82\'72 \'83\'53\'83\'56\'83\'62\'83\'4e{\*\falt MS Gothic};} 7 | {\f31506\fbidi \fswiss\fcharset0\fprq2{\*\panose 020b0604020202020204}Arial;}{\f31507\fbidi \froman\fcharset1\fprq2{\*\panose 020b0502040204020203}Shruti{\*\falt Segoe UI};}{\f543\fbidi \fswiss\fcharset238\fprq2 Arial CE;} 8 | {\f544\fbidi \fswiss\fcharset204\fprq2 Arial Cyr;}{\f546\fbidi \fswiss\fcharset161\fprq2 Arial Greek;}{\f547\fbidi \fswiss\fcharset162\fprq2 Arial Tur;}{\f548\fbidi \fswiss\fcharset177\fprq2 Arial (Hebrew);} 9 | {\f549\fbidi \fswiss\fcharset178\fprq2 Arial (Arabic);}{\f550\fbidi \fswiss\fcharset186\fprq2 Arial Baltic;}{\f551\fbidi \fswiss\fcharset163\fprq2 Arial (Vietnamese);}{\f553\fbidi \fmodern\fcharset238\fprq1 Courier New CE;} 10 | {\f554\fbidi \fmodern\fcharset204\fprq1 Courier New Cyr;}{\f556\fbidi \fmodern\fcharset161\fprq1 Courier New Greek;}{\f557\fbidi \fmodern\fcharset162\fprq1 Courier New Tur;}{\f558\fbidi \fmodern\fcharset177\fprq1 Courier New (Hebrew);} 11 | {\f559\fbidi \fmodern\fcharset178\fprq1 Courier New (Arabic);}{\f560\fbidi \fmodern\fcharset186\fprq1 Courier New Baltic;}{\f561\fbidi \fmodern\fcharset163\fprq1 Courier New (Vietnamese);} 12 | {\f685\fbidi \fmodern\fcharset0\fprq1 MS Gothic Western{\*\falt MS Gothic};}{\f683\fbidi \fmodern\fcharset238\fprq1 MS Gothic CE{\*\falt MS Gothic};}{\f684\fbidi \fmodern\fcharset204\fprq1 MS Gothic Cyr{\*\falt MS Gothic};} 13 | {\f686\fbidi \fmodern\fcharset161\fprq1 MS Gothic Greek{\*\falt MS Gothic};}{\f687\fbidi \fmodern\fcharset162\fprq1 MS Gothic Tur{\*\falt MS Gothic};}{\f690\fbidi \fmodern\fcharset186\fprq1 MS Gothic Baltic{\*\falt MS Gothic};} 14 | {\f925\fbidi \fmodern\fcharset0\fprq1 @\'82\'6c\'82\'72 \'83\'53\'83\'56\'83\'62\'83\'4e Western;}{\f923\fbidi \fmodern\fcharset238\fprq1 @\'82\'6c\'82\'72 \'83\'53\'83\'56\'83\'62\'83\'4e CE;} 15 | {\f924\fbidi \fmodern\fcharset204\fprq1 @\'82\'6c\'82\'72 \'83\'53\'83\'56\'83\'62\'83\'4e Cyr;}{\f926\fbidi \fmodern\fcharset161\fprq1 @\'82\'6c\'82\'72 \'83\'53\'83\'56\'83\'62\'83\'4e Greek;} 16 | {\f927\fbidi \fmodern\fcharset162\fprq1 @\'82\'6c\'82\'72 \'83\'53\'83\'56\'83\'62\'83\'4e Tur;}{\f930\fbidi \fmodern\fcharset186\fprq1 @\'82\'6c\'82\'72 \'83\'53\'83\'56\'83\'62\'83\'4e Baltic;} 17 | {\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} 18 | {\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} 19 | {\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f31520\fbidi \fmodern\fcharset0\fprq1 MS Gothic Western{\*\falt MS Gothic};}{\f31518\fbidi \fmodern\fcharset238\fprq1 MS Gothic CE{\*\falt MS Gothic};} 20 | {\f31519\fbidi \fmodern\fcharset204\fprq1 MS Gothic Cyr{\*\falt MS Gothic};}{\f31521\fbidi \fmodern\fcharset161\fprq1 MS Gothic Greek{\*\falt MS Gothic};}{\f31522\fbidi \fmodern\fcharset162\fprq1 MS Gothic Tur{\*\falt MS Gothic};} 21 | {\f31525\fbidi \fmodern\fcharset186\fprq1 MS Gothic Baltic{\*\falt MS Gothic};}{\f31528\fbidi \fswiss\fcharset238\fprq2 Arial CE;}{\f31529\fbidi \fswiss\fcharset204\fprq2 Arial Cyr;}{\f31531\fbidi \fswiss\fcharset161\fprq2 Arial Greek;} 22 | {\f31532\fbidi \fswiss\fcharset162\fprq2 Arial Tur;}{\f31533\fbidi \fswiss\fcharset177\fprq2 Arial (Hebrew);}{\f31534\fbidi \fswiss\fcharset178\fprq2 Arial (Arabic);}{\f31535\fbidi \fswiss\fcharset186\fprq2 Arial Baltic;} 23 | {\f31536\fbidi \fswiss\fcharset163\fprq2 Arial (Vietnamese);}{\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} 24 | {\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} 25 | {\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f31560\fbidi \fmodern\fcharset0\fprq1 MS Gothic Western{\*\falt MS Gothic};} 26 | {\f31558\fbidi \fmodern\fcharset238\fprq1 MS Gothic CE{\*\falt MS Gothic};}{\f31559\fbidi \fmodern\fcharset204\fprq1 MS Gothic Cyr{\*\falt MS Gothic};}{\f31561\fbidi \fmodern\fcharset161\fprq1 MS Gothic Greek{\*\falt MS Gothic};} 27 | {\f31562\fbidi \fmodern\fcharset162\fprq1 MS Gothic Tur{\*\falt MS Gothic};}{\f31565\fbidi \fmodern\fcharset186\fprq1 MS Gothic Baltic{\*\falt MS Gothic};}{\f31568\fbidi \fswiss\fcharset238\fprq2 Arial CE;} 28 | {\f31569\fbidi \fswiss\fcharset204\fprq2 Arial Cyr;}{\f31571\fbidi \fswiss\fcharset161\fprq2 Arial Greek;}{\f31572\fbidi \fswiss\fcharset162\fprq2 Arial Tur;}{\f31573\fbidi \fswiss\fcharset177\fprq2 Arial (Hebrew);} 29 | {\f31574\fbidi \fswiss\fcharset178\fprq2 Arial (Arabic);}{\f31575\fbidi \fswiss\fcharset186\fprq2 Arial Baltic;}{\f31576\fbidi \fswiss\fcharset163\fprq2 Arial (Vietnamese);}{\f885\fbidi \fmodern\fcharset0\fprq2 Arial Unicode MS Western;} 30 | {\f883\fbidi \fmodern\fcharset238\fprq2 Arial Unicode MS CE;}{\f884\fbidi \fmodern\fcharset204\fprq2 Arial Unicode MS Cyr;}{\f886\fbidi \fmodern\fcharset161\fprq2 Arial Unicode MS Greek;}{\f887\fbidi \fmodern\fcharset162\fprq2 Arial Unicode MS Tur;} 31 | {\f888\fbidi \fmodern\fcharset177\fprq2 Arial Unicode MS (Hebrew);}{\f889\fbidi \fmodern\fcharset178\fprq2 Arial Unicode MS (Arabic);}{\f890\fbidi \fmodern\fcharset186\fprq2 Arial Unicode MS Baltic;} 32 | {\f891\fbidi \fmodern\fcharset163\fprq2 Arial Unicode MS (Vietnamese);}{\f892\fbidi \fmodern\fcharset222\fprq2 Arial Unicode MS (Thai);}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255; 33 | \red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192; 34 | \red5\green99\blue193;\red149\green79\blue114;}{\*\defchp \fs21\kerning2\loch\af31506\hich\af31506\dbch\af31505 }{\*\defpap \ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{ 35 | \qj \li0\ri0\nowidctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \fs21\lang1033\langfe1041\kerning2\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1041 36 | \snext0 \sqformat \spriority0 Normal;} 37 | {\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;} 38 | {\*\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv 39 | \ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \fs21\lang1033\langfe1041\kerning2\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1041 40 | \snext11 \ssemihidden \sunhideused Normal Table;} 41 | {\*\cs15 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf17 \sbasedon10 \ssemihidden \sunhideused \styrsid2587925 Hyperlink;} 42 | {\*\cs16 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf18 \sbasedon10 \ssemihidden \sunhideused \styrsid2587925 FollowedHyperlink;} 43 | {\*\cs17 \additive \rtlch\fcs1 \af31507\afs22 \ltrch\fcs0 \fs22\cf0\loch\f31506\hich\af31506\dbch\af31505 \sbasedon10 \ssemihidden \spriority0 \spersonal \scompose \styrsid2587925 44 | \'93\'64\'8e\'71\'83\'81\'81\'5b\'83\'8b\'82\'cc\'83\'58\'83\'5e\'83\'43\'83\'8b17;}{\s18\ql \li0\ri0\nowidctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af2\afs21\alang1025 \ltrch\fcs0 45 | \fs20\lang1033\langfe1041\kerning2\loch\f15\hich\af2\dbch\af15\cgrid\langnp1033\langfenp1041 \sbasedon0 \snext18 \slink19 \ssemihidden \sunhideused \styrsid2587925 Plain Text;} 46 | {\*\cs19 \additive \rtlch\fcs1 \af2\afs21 \ltrch\fcs0 \fs21\loch\f15\hich\af2\dbch\af15 \sbasedon10 \slink18 \slocked \ssemihidden \styrsid2587925 \'8f\'91\'8e\'ae\'82\'c8\'82\'b5 (\'95\'b6\'8e\'9a);}}{\*\revtbl {Unknown;}}{\*\rsidtbl \rsid1732230\rsid2587925}{\mmathPr 47 | \mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}} 48 | \paperw12240\paperh15840\margl1701\margr1701\margt1985\margb1701\gutter0\ltrsect 49 | \deftab840\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont1\relyonvml0\donotembedlingdata0\grfdocevents0\validatexml1\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors1\formshade\horzdoc\dgmargin\dghspace180\dgvspace180 50 | \dghorigin150\dgvorigin0\dghshow0\dgvshow2\jcompress\viewkind5\viewscale100\splytwnine\ftnlytwnine\htmautsp\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct\asianbrkrule\newtblstyruls 51 | \nogrowautofit\usenormstyforlist\noindnmbrts\felnbrelev\nocxsptable\indrlsweleven\noafcnsttbl\afelev\utinl\hwelev\spltpgpar\notcvasp\notbrkcnstfrctbl\notvatxbx\krnprsnet\cachedcolbal \nouicompat \fet0{\*\wgrffmtfilter 2450}\nofeaturethrottle1 52 | \ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\endnhere\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta \dbch .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta \dbch .}}{\*\pnseclvl3 53 | \pndec\pnstart1\pnindent720\pnhang {\pntxta \dbch .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta \dbch )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb \dbch (}{\pntxta \dbch )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang 54 | {\pntxtb \dbch (}{\pntxta \dbch )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb \dbch (}{\pntxta \dbch )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb \dbch (}{\pntxta \dbch )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang 55 | {\pntxtb \dbch (}{\pntxta \dbch )}}\pard\plain \ltrpar\qj \li0\ri0\nowidctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid2587925 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 56 | \fs21\lang1033\langfe1041\kerning2\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1041 {\rtlch\fcs1 \af31507 \ltrch\fcs0 \cf0\insrsid2587925\charrsid2587925 \hich\af31506\dbch\af31505\loch\f31506 Hello.}{\rtlch\fcs1 \af31507 \ltrch\fcs0 57 | \cf0\insrsid2587925\charrsid2587925 58 | \par }} -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2019 HIRAOKA HYPERS TOOLS, Inc. 190 | Copyright 2016 Yury Karpovich 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | --------------------------------------------------------------------------------