imapflow 1.2.5 → 1.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +15 -0
- package/lib/commands/append.js +2 -7
- package/lib/commands/copy.js +2 -7
- package/lib/commands/create.js +8 -17
- package/lib/commands/delete.js +2 -7
- package/lib/commands/expunge.js +2 -7
- package/lib/commands/move.js +2 -7
- package/lib/commands/quota.js +2 -7
- package/lib/commands/rename.js +2 -7
- package/lib/commands/search.js +2 -7
- package/lib/commands/select.js +2 -6
- package/lib/commands/store.js +2 -7
- package/lib/commands/subscribe.js +2 -7
- package/lib/commands/unsubscribe.js +2 -8
- package/lib/handler/imap-stream.js +15 -0
- package/lib/imap-flow.js +68 -63
- package/lib/jp-decoder.js +6 -0
- package/lib/tools.js +26 -15
- package/package.json +7 -7
- package/test/connection-edge-cases-test.js +518 -0
- package/test/memory-leak-test.js +667 -0
- package/test/tools-test.js +204 -0
package/test/tools-test.js
CHANGED
|
@@ -387,6 +387,12 @@ module.exports['Tools: formatDateTime with string'] = test => {
|
|
|
387
387
|
test.done();
|
|
388
388
|
};
|
|
389
389
|
|
|
390
|
+
module.exports['Tools: formatDateTime with invalid date string'] = test => {
|
|
391
|
+
let result = tools.formatDateTime('invalid-date-string');
|
|
392
|
+
test.equal(result, undefined);
|
|
393
|
+
test.done();
|
|
394
|
+
};
|
|
395
|
+
|
|
390
396
|
// ============================================
|
|
391
397
|
// formatFlag tests
|
|
392
398
|
// ============================================
|
|
@@ -666,6 +672,52 @@ module.exports['Tools: parseEnvelope with invalid date'] = test => {
|
|
|
666
672
|
test.done();
|
|
667
673
|
};
|
|
668
674
|
|
|
675
|
+
module.exports['Tools: parseEnvelope with Buffer value'] = test => {
|
|
676
|
+
let entry = [
|
|
677
|
+
{ value: Buffer.from('Mon, 15 Jun 2023 10:00:00 +0000') }, // date as Buffer
|
|
678
|
+
{ value: Buffer.from('Buffer Subject') }, // subject as Buffer
|
|
679
|
+
[[{ value: Buffer.from('Sender Name') }, null, { value: Buffer.from('sender') }, { value: Buffer.from('example.com') }]], // from with Buffers
|
|
680
|
+
[], // sender
|
|
681
|
+
[], // reply-to
|
|
682
|
+
[], // to
|
|
683
|
+
[], // cc
|
|
684
|
+
[], // bcc
|
|
685
|
+
null, // in-reply-to
|
|
686
|
+
{ value: Buffer.from('<msg-id@example.com>') } // message-id as Buffer
|
|
687
|
+
];
|
|
688
|
+
|
|
689
|
+
let result = tools.parseEnvelope(entry);
|
|
690
|
+
test.equal(result.subject, 'Buffer Subject');
|
|
691
|
+
test.equal(result.from[0].address, 'sender@example.com');
|
|
692
|
+
test.equal(result.messageId, '<msg-id@example.com>');
|
|
693
|
+
test.done();
|
|
694
|
+
};
|
|
695
|
+
|
|
696
|
+
module.exports['Tools: parseEnvelope with empty address parts'] = test => {
|
|
697
|
+
// When both local part and domain are null/empty, address should be empty string
|
|
698
|
+
let entry = [
|
|
699
|
+
null, // date
|
|
700
|
+
null, // subject
|
|
701
|
+
[[{ value: 'Group Name' }, null, null, null]], // from with no email parts (group syntax)
|
|
702
|
+
[], // sender
|
|
703
|
+
[], // reply-to
|
|
704
|
+
[], // to
|
|
705
|
+
[], // cc
|
|
706
|
+
[], // bcc
|
|
707
|
+
null, // in-reply-to
|
|
708
|
+
null // message-id
|
|
709
|
+
];
|
|
710
|
+
|
|
711
|
+
let result = tools.parseEnvelope(entry);
|
|
712
|
+
// Address '@' should be converted to empty string and filtered out if no name
|
|
713
|
+
test.ok(result.from);
|
|
714
|
+
// The entry has a name but no valid address, should still be included with empty address
|
|
715
|
+
test.equal(result.from.length, 1);
|
|
716
|
+
test.equal(result.from[0].name, 'Group Name');
|
|
717
|
+
test.equal(result.from[0].address, '');
|
|
718
|
+
test.done();
|
|
719
|
+
};
|
|
720
|
+
|
|
669
721
|
// ============================================
|
|
670
722
|
// getStructuredParams tests
|
|
671
723
|
// ============================================
|
|
@@ -751,6 +803,158 @@ module.exports['Tools: parseBodystructure with attachment'] = test => {
|
|
|
751
803
|
test.done();
|
|
752
804
|
};
|
|
753
805
|
|
|
806
|
+
module.exports['Tools: parseBodystructure with md5'] = test => {
|
|
807
|
+
// Non-text type with extension data including md5
|
|
808
|
+
let entry = [
|
|
809
|
+
{ value: 'APPLICATION' },
|
|
810
|
+
{ value: 'OCTET-STREAM' },
|
|
811
|
+
null, // params
|
|
812
|
+
null, // id
|
|
813
|
+
null, // description
|
|
814
|
+
{ value: 'BASE64' }, // encoding
|
|
815
|
+
{ value: '1000' }, // size
|
|
816
|
+
{ value: 'd41d8cd98f00b204e9800998ecf8427e' }, // md5
|
|
817
|
+
null, // disposition
|
|
818
|
+
null // language (to ensure we have enough elements)
|
|
819
|
+
];
|
|
820
|
+
|
|
821
|
+
let result = tools.parseBodystructure(entry);
|
|
822
|
+
test.equal(result.type, 'application/octet-stream');
|
|
823
|
+
test.equal(result.md5, 'd41d8cd98f00b204e9800998ecf8427e');
|
|
824
|
+
test.done();
|
|
825
|
+
};
|
|
826
|
+
|
|
827
|
+
module.exports['Tools: parseBodystructure with language'] = test => {
|
|
828
|
+
// Non-text type with language extension
|
|
829
|
+
let entry = [
|
|
830
|
+
{ value: 'APPLICATION' },
|
|
831
|
+
{ value: 'PDF' },
|
|
832
|
+
null, // params
|
|
833
|
+
null, // id
|
|
834
|
+
null, // description
|
|
835
|
+
{ value: 'BASE64' }, // encoding
|
|
836
|
+
{ value: '5000' }, // size
|
|
837
|
+
null, // md5
|
|
838
|
+
null, // disposition
|
|
839
|
+
[{ value: 'EN' }, { value: 'DE' }], // language (array of values)
|
|
840
|
+
null // location (to ensure enough elements)
|
|
841
|
+
];
|
|
842
|
+
|
|
843
|
+
let result = tools.parseBodystructure(entry);
|
|
844
|
+
test.equal(result.type, 'application/pdf');
|
|
845
|
+
test.ok(Array.isArray(result.language));
|
|
846
|
+
test.deepEqual(result.language, ['en', 'de']);
|
|
847
|
+
test.done();
|
|
848
|
+
};
|
|
849
|
+
|
|
850
|
+
module.exports['Tools: parseBodystructure with location'] = test => {
|
|
851
|
+
// Non-text type with location extension
|
|
852
|
+
let entry = [
|
|
853
|
+
{ value: 'IMAGE' },
|
|
854
|
+
{ value: 'PNG' },
|
|
855
|
+
null, // params
|
|
856
|
+
null, // id
|
|
857
|
+
null, // description
|
|
858
|
+
{ value: 'BASE64' }, // encoding
|
|
859
|
+
{ value: '10000' }, // size
|
|
860
|
+
null, // md5
|
|
861
|
+
null, // disposition
|
|
862
|
+
null, // language
|
|
863
|
+
{ value: 'http://example.com/image.png' }, // location
|
|
864
|
+
null // extra element to ensure we have enough
|
|
865
|
+
];
|
|
866
|
+
|
|
867
|
+
let result = tools.parseBodystructure(entry);
|
|
868
|
+
test.equal(result.type, 'image/png');
|
|
869
|
+
test.equal(result.location, 'http://example.com/image.png');
|
|
870
|
+
test.done();
|
|
871
|
+
};
|
|
872
|
+
|
|
873
|
+
module.exports['Tools: parseBodystructure with all extension fields'] = test => {
|
|
874
|
+
// Non-text type with all extension fields
|
|
875
|
+
let entry = [
|
|
876
|
+
{ value: 'APPLICATION' },
|
|
877
|
+
{ value: 'ZIP' },
|
|
878
|
+
[{ value: 'NAME' }, { value: 'archive.zip' }], // params
|
|
879
|
+
{ value: '<id123@example.com>' }, // id
|
|
880
|
+
{ value: 'A zip archive' }, // description
|
|
881
|
+
{ value: 'BASE64' }, // encoding
|
|
882
|
+
{ value: '50000' }, // size
|
|
883
|
+
{ value: 'abc123def456' }, // md5
|
|
884
|
+
[{ value: 'ATTACHMENT' }, [{ value: 'FILENAME' }, { value: 'archive.zip' }]], // disposition with params
|
|
885
|
+
[{ value: 'EN' }], // language
|
|
886
|
+
{ value: 'http://example.com/archive.zip' }, // location
|
|
887
|
+
null // extra element
|
|
888
|
+
];
|
|
889
|
+
|
|
890
|
+
let result = tools.parseBodystructure(entry);
|
|
891
|
+
test.equal(result.type, 'application/zip');
|
|
892
|
+
test.equal(result.parameters.name, 'archive.zip');
|
|
893
|
+
test.equal(result.id, '<id123@example.com>');
|
|
894
|
+
test.equal(result.description, 'A zip archive');
|
|
895
|
+
test.equal(result.encoding, 'base64');
|
|
896
|
+
test.equal(result.size, 50000);
|
|
897
|
+
test.equal(result.md5, 'abc123def456');
|
|
898
|
+
test.equal(result.disposition, 'attachment');
|
|
899
|
+
test.equal(result.dispositionParameters.filename, 'archive.zip');
|
|
900
|
+
test.deepEqual(result.language, ['en']);
|
|
901
|
+
test.equal(result.location, 'http://example.com/archive.zip');
|
|
902
|
+
test.done();
|
|
903
|
+
};
|
|
904
|
+
|
|
905
|
+
module.exports['Tools: parseBodystructure with message/rfc822'] = test => {
|
|
906
|
+
// message/rfc822 has special handling with envelope and nested bodystructure
|
|
907
|
+
let nestedBody = [
|
|
908
|
+
{ value: 'TEXT' },
|
|
909
|
+
{ value: 'PLAIN' },
|
|
910
|
+
[{ value: 'CHARSET' }, { value: 'UTF-8' }],
|
|
911
|
+
null,
|
|
912
|
+
null,
|
|
913
|
+
{ value: '7BIT' },
|
|
914
|
+
{ value: '500' },
|
|
915
|
+
{ value: '20' } // line count for text
|
|
916
|
+
];
|
|
917
|
+
|
|
918
|
+
let envelope = [
|
|
919
|
+
{ value: 'Mon, 15 Jun 2023 10:00:00 +0000' }, // date
|
|
920
|
+
{ value: 'Nested Subject' }, // subject
|
|
921
|
+
[[null, null, { value: 'sender' }, { value: 'example.com' }]], // from
|
|
922
|
+
[],
|
|
923
|
+
[],
|
|
924
|
+
[],
|
|
925
|
+
[],
|
|
926
|
+
[], // sender, reply-to, to, cc, bcc
|
|
927
|
+
null, // in-reply-to
|
|
928
|
+
{ value: '<nested@example.com>' } // message-id
|
|
929
|
+
];
|
|
930
|
+
|
|
931
|
+
let entry = [
|
|
932
|
+
{ value: 'MESSAGE' },
|
|
933
|
+
{ value: 'RFC822' },
|
|
934
|
+
null, // params
|
|
935
|
+
null, // id
|
|
936
|
+
null, // description
|
|
937
|
+
{ value: '7BIT' }, // encoding
|
|
938
|
+
{ value: '10000' }, // size
|
|
939
|
+
envelope, // envelope
|
|
940
|
+
nestedBody, // nested bodystructure
|
|
941
|
+
{ value: '100' }, // line count
|
|
942
|
+
null, // md5
|
|
943
|
+
null // disposition
|
|
944
|
+
];
|
|
945
|
+
|
|
946
|
+
let result = tools.parseBodystructure(entry);
|
|
947
|
+
test.equal(result.type, 'message/rfc822');
|
|
948
|
+
test.equal(result.size, 10000);
|
|
949
|
+
test.equal(result.lineCount, 100);
|
|
950
|
+
test.ok(result.envelope);
|
|
951
|
+
test.equal(result.envelope.subject, 'Nested Subject');
|
|
952
|
+
test.ok(result.childNodes);
|
|
953
|
+
test.equal(result.childNodes.length, 1);
|
|
954
|
+
test.equal(result.childNodes[0].type, 'text/plain');
|
|
955
|
+
test.done();
|
|
956
|
+
};
|
|
957
|
+
|
|
754
958
|
// ============================================
|
|
755
959
|
// getDecoder tests
|
|
756
960
|
// ============================================
|