Go language library for reading and writing Microsoft Excel™ (XLAM / XLSM / XLSX / XLTM / XLTX) spreadsheets
修订版 | cfa2d603ddb0fac50ca1af3fe1d28fe17a65a6f3 (tree) |
---|---|
时间 | 2022-08-20 16:51:03 |
作者 | Sangua633 <76948439+Sangua633@user...> |
Commiter | GitHub |
@@ -25,7 +25,9 @@ import ( | ||
25 | 25 | "encoding/xml" |
26 | 26 | "hash" |
27 | 27 | "math" |
28 | + "path/filepath" | |
28 | 29 | "reflect" |
30 | + "sort" | |
29 | 31 | "strings" |
30 | 32 | |
31 | 33 | "github.com/richardlehane/mscfb" |
@@ -37,6 +39,10 @@ import ( | ||
37 | 39 | var ( |
38 | 40 | blockKey = []byte{0x14, 0x6e, 0x0b, 0xe7, 0xab, 0xac, 0xd0, 0xd6} // Block keys used for encryption |
39 | 41 | oleIdentifier = []byte{0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1} |
42 | + headerCLSID = make([]byte, 16) | |
43 | + difSect = -4 | |
44 | + endOfChain = -2 | |
45 | + fatSect = -3 | |
40 | 46 | iterCount = 50000 |
41 | 47 | packageEncryptionChunkSize = 4096 |
42 | 48 | packageOffset = 8 // First 8 bytes are the size of the stream |
@@ -150,7 +156,7 @@ func Decrypt(raw []byte, opt *Options) (packageBuf []byte, err error) { | ||
150 | 156 | } |
151 | 157 | |
152 | 158 | // Encrypt API encrypt data with the password. |
153 | -func Encrypt(raw []byte, opt *Options) (packageBuf []byte, err error) { | |
159 | +func Encrypt(raw []byte, opt *Options) ([]byte, error) { | |
154 | 160 | encryptor := encryption{ |
155 | 161 | EncryptedVerifierHashInput: make([]byte, 16), |
156 | 162 | EncryptedVerifierHashValue: make([]byte, 32), |
@@ -169,9 +175,13 @@ func Encrypt(raw []byte, opt *Options) (packageBuf []byte, err error) { | ||
169 | 175 | binary.LittleEndian.PutUint64(encryptedPackage, uint64(len(raw))) |
170 | 176 | encryptedPackage = append(encryptedPackage, encryptor.encrypt(raw)...) |
171 | 177 | // Create a new CFB |
172 | - compoundFile := cfb{} | |
173 | - packageBuf = compoundFile.Writer(encryptionInfoBuffer, encryptedPackage) | |
174 | - return packageBuf, nil | |
178 | + compoundFile := &cfb{ | |
179 | + paths: []string{"Root Entry/"}, | |
180 | + sectors: []sector{{name: "Root Entry", typeID: 5}}, | |
181 | + } | |
182 | + compoundFile.put("EncryptionInfo", encryptionInfoBuffer) | |
183 | + compoundFile.put("EncryptedPackage", encryptedPackage) | |
184 | + return compoundFile.write(), nil | |
175 | 185 | } |
176 | 186 | |
177 | 187 | // extractPart extract data from storage by specified part name. |
@@ -618,6 +628,15 @@ func genISOPasswdHash(passwd, hashAlgorithm, salt string, spinCount int) (hashVa | ||
618 | 628 | type cfb struct { |
619 | 629 | stream []byte |
620 | 630 | position int |
631 | + paths []string | |
632 | + sectors []sector | |
633 | +} | |
634 | + | |
635 | +// sector structure used for FAT, directory, miniFAT, and miniStream sectors. | |
636 | +type sector struct { | |
637 | + clsID, content []byte | |
638 | + name string | |
639 | + C, L, R, color, size, start, state, typeID int | |
621 | 640 | } |
622 | 641 | |
623 | 642 | // writeBytes write bytes in the stream by a given value with an offset. |
@@ -666,415 +685,156 @@ func (c *cfb) writeStrings(value string) { | ||
666 | 685 | c.writeBytes(buffer) |
667 | 686 | } |
668 | 687 | |
669 | -// writeVersionStream provides a function to write compound file version | |
670 | -// stream. | |
671 | -func (c *cfb) writeVersionStream() []byte { | |
672 | - var storage cfb | |
673 | - storage.writeUint32(0x3c) | |
674 | - storage.writeStrings("Microsoft.Container.DataSpaces") | |
675 | - storage.writeUint32(0x01) | |
676 | - storage.writeUint32(0x01) | |
677 | - storage.writeUint32(0x01) | |
678 | - return storage.stream | |
679 | -} | |
680 | - | |
681 | -// writeDataSpaceMapStream provides a function to write compound file | |
682 | -// DataSpaceMap stream. | |
683 | -func (c *cfb) writeDataSpaceMapStream() []byte { | |
684 | - var storage cfb | |
685 | - storage.writeUint32(0x08) | |
686 | - storage.writeUint32(0x01) | |
687 | - storage.writeUint32(0x68) | |
688 | - storage.writeUint32(0x01) | |
689 | - storage.writeUint32(0x00) | |
690 | - storage.writeUint32(0x20) | |
691 | - storage.writeStrings("EncryptedPackage") | |
692 | - storage.writeUint32(0x32) | |
693 | - storage.writeStrings("StrongEncryptionDataSpace") | |
694 | - storage.writeUint16(0x00) | |
695 | - return storage.stream | |
696 | -} | |
697 | - | |
698 | -// writeStrongEncryptionDataSpaceStream provides a function to write compound | |
699 | -// file StrongEncryptionDataSpace stream. | |
700 | -func (c *cfb) writeStrongEncryptionDataSpaceStream() []byte { | |
701 | - var storage cfb | |
702 | - storage.writeUint32(0x08) | |
703 | - storage.writeUint32(0x01) | |
704 | - storage.writeUint32(0x32) | |
705 | - storage.writeStrings("StrongEncryptionTransform") | |
706 | - storage.writeUint16(0x00) | |
707 | - return storage.stream | |
708 | -} | |
709 | - | |
710 | -// writePrimaryStream provides a function to write compound file Primary | |
711 | -// stream. | |
712 | -func (c *cfb) writePrimaryStream() []byte { | |
713 | - var storage cfb | |
714 | - storage.writeUint32(0x6C) | |
715 | - storage.writeUint32(0x01) | |
716 | - storage.writeUint32(0x4C) | |
717 | - storage.writeStrings("{FF9A3F03-56EF-4613-BDD5-5A41C1D07246}") | |
718 | - storage.writeUint32(0x4E) | |
719 | - storage.writeUint16(0x00) | |
720 | - storage.writeUint32(0x01) | |
721 | - storage.writeUint32(0x01) | |
722 | - storage.writeUint32(0x01) | |
723 | - storage.writeStrings("AES128") | |
724 | - storage.writeUint32(0x00) | |
725 | - storage.writeUint32(0x04) | |
726 | - return storage.stream | |
727 | -} | |
728 | - | |
729 | -// writeFileStream provides a function to write encrypted package in compound | |
730 | -// file by a given buffer and the short sector allocation table. | |
731 | -func (c *cfb) writeFileStream(encryptionInfoBuffer []byte, SSAT []int) ([]byte, []int) { | |
732 | - var ( | |
733 | - storage cfb | |
734 | - miniProperties int | |
735 | - stream = make([]byte, 0x100) | |
736 | - ) | |
737 | - if encryptionInfoBuffer != nil { | |
738 | - copy(stream, encryptionInfoBuffer) | |
739 | - } | |
740 | - storage.writeBytes(stream) | |
741 | - streamBlocks := len(stream) / 64 | |
742 | - if len(stream)%64 > 0 { | |
743 | - streamBlocks++ | |
744 | - } | |
745 | - for i := 1; i < streamBlocks; i++ { | |
746 | - SSAT = append(SSAT, i) | |
747 | - } | |
748 | - SSAT = append(SSAT, -2) | |
749 | - miniProperties += streamBlocks | |
750 | - versionStream := make([]byte, 0x80) | |
751 | - version := c.writeVersionStream() | |
752 | - copy(versionStream, version) | |
753 | - storage.writeBytes(versionStream) | |
754 | - versionBlocks := len(versionStream) / 64 | |
755 | - if len(versionStream)%64 > 0 { | |
756 | - versionBlocks++ | |
757 | - } | |
758 | - for i := 1; i < versionBlocks; i++ { | |
759 | - SSAT = append(SSAT, i+miniProperties) | |
760 | - } | |
761 | - SSAT = append(SSAT, -2) | |
762 | - miniProperties += versionBlocks | |
763 | - dataSpaceMap := make([]byte, 0x80) | |
764 | - dataStream := c.writeDataSpaceMapStream() | |
765 | - copy(dataSpaceMap, dataStream) | |
766 | - storage.writeBytes(dataSpaceMap) | |
767 | - dataSpaceMapBlocks := len(dataSpaceMap) / 64 | |
768 | - if len(dataSpaceMap)%64 > 0 { | |
769 | - dataSpaceMapBlocks++ | |
770 | - } | |
771 | - for i := 1; i < dataSpaceMapBlocks; i++ { | |
772 | - SSAT = append(SSAT, i+miniProperties) | |
773 | - } | |
774 | - SSAT = append(SSAT, -2) | |
775 | - miniProperties += dataSpaceMapBlocks | |
776 | - dataSpaceStream := c.writeStrongEncryptionDataSpaceStream() | |
777 | - storage.writeBytes(dataSpaceStream) | |
778 | - dataSpaceStreamBlocks := len(dataSpaceStream) / 64 | |
779 | - if len(dataSpaceStream)%64 > 0 { | |
780 | - dataSpaceStreamBlocks++ | |
781 | - } | |
782 | - for i := 1; i < dataSpaceStreamBlocks; i++ { | |
783 | - SSAT = append(SSAT, i+miniProperties) | |
784 | - } | |
785 | - SSAT = append(SSAT, -2) | |
786 | - miniProperties += dataSpaceStreamBlocks | |
787 | - primaryStream := make([]byte, 0x1C0) | |
788 | - primary := c.writePrimaryStream() | |
789 | - copy(primaryStream, primary) | |
790 | - storage.writeBytes(primaryStream) | |
791 | - primaryBlocks := len(primary) / 64 | |
792 | - if len(primary)%64 > 0 { | |
793 | - primaryBlocks++ | |
794 | - } | |
795 | - for i := 1; i < primaryBlocks; i++ { | |
796 | - SSAT = append(SSAT, i+miniProperties) | |
797 | - } | |
798 | - SSAT = append(SSAT, -2) | |
799 | - if len(SSAT) < 128 { | |
800 | - for i := len(SSAT); i < 128; i++ { | |
801 | - SSAT = append(SSAT, -1) | |
688 | +// put provides a function to add an entry to compound file by given entry name | |
689 | +// and raw bytes. | |
690 | +func (c *cfb) put(name string, content []byte) { | |
691 | + path := c.paths[0] | |
692 | + if len(path) <= len(name) && name[:len(path)] == path { | |
693 | + path = name | |
694 | + } else { | |
695 | + if len(path) > 0 && string(path[len(path)-1]) != "/" { | |
696 | + path += "/" | |
697 | + } | |
698 | + path = strings.ReplaceAll(path+name, "//", "/") | |
699 | + } | |
700 | + file := sector{name: path, typeID: 2, content: content, size: len(content)} | |
701 | + c.sectors = append(c.sectors, file) | |
702 | + c.paths = append(c.paths, path) | |
703 | +} | |
704 | + | |
705 | +// compare provides a function to compare object path, each set of sibling | |
706 | +// objects in one level of the containment hierarchy (all child objects under | |
707 | +// a storage object) is represented as a red-black tree. The parent object of | |
708 | +// this set of siblings will have a pointer to the top of this tree. | |
709 | +func (c *cfb) compare(left, right string) int { | |
710 | + L, R, i, j := strings.Split(left, "/"), strings.Split(right, "/"), 0, 0 | |
711 | + for Z := int(math.Min(float64(len(L)), float64(len(R)))); i < Z; i++ { | |
712 | + if j = len(L[i]) - len(R[i]); j != 0 { | |
713 | + return j | |
714 | + } | |
715 | + if L[i] != R[i] { | |
716 | + if L[i] < R[i] { | |
717 | + return -1 | |
718 | + } | |
719 | + return 1 | |
802 | 720 | } |
803 | 721 | } |
804 | - storage.position = 0 | |
805 | - return storage.stream, SSAT | |
806 | -} | |
807 | - | |
808 | -// writeRootEntry provides a function to write compound file root directory | |
809 | -// entry. The first entry in the first sector of the directory chain | |
810 | -// (also referred to as the first element of the directory array, or stream | |
811 | -// ID #0) is known as the root directory entry, and it is reserved for two | |
812 | -// purposes. First, it provides a root parent for all objects that are | |
813 | -// stationed at the root of the compound file. Second, its function is | |
814 | -// overloaded to store the size and starting sector for the mini stream. | |
815 | -func (c *cfb) writeRootEntry(customSectID int) []byte { | |
816 | - storage := cfb{stream: make([]byte, 128)} | |
817 | - storage.writeStrings("Root Entry") | |
818 | - storage.position = 0x40 | |
819 | - storage.writeUint16(0x16) | |
820 | - storage.writeBytes([]byte{5, 0}) | |
821 | - storage.writeUint32(-1) | |
822 | - storage.writeUint32(-1) | |
823 | - storage.writeUint32(1) | |
824 | - storage.position = 0x64 | |
825 | - storage.writeUint32(0) | |
826 | - storage.writeUint32(0) | |
827 | - storage.writeUint32(0) | |
828 | - storage.writeUint32(0) | |
829 | - storage.writeUint32(customSectID) | |
830 | - storage.writeUint32(0x340) | |
831 | - return storage.stream | |
832 | -} | |
833 | - | |
834 | -// writeEncryptionInfo provides a function to write compound file | |
835 | -// writeEncryptionInfo stream. The writeEncryptionInfo stream contains | |
836 | -// detailed information that is used to initialize the cryptography used to | |
837 | -// encrypt the EncryptedPackage stream. | |
838 | -func (c *cfb) writeEncryptionInfo() []byte { | |
839 | - storage := cfb{stream: make([]byte, 128)} | |
840 | - storage.writeStrings("EncryptionInfo") | |
841 | - storage.position = 0x40 | |
842 | - storage.writeUint16(0x1E) | |
843 | - storage.writeBytes([]byte{2, 1}) | |
844 | - storage.writeUint32(0x03) | |
845 | - storage.writeUint32(0x02) | |
846 | - storage.writeUint32(-1) | |
847 | - storage.position = 0x64 | |
848 | - storage.writeUint32(0) | |
849 | - storage.writeUint32(0) | |
850 | - storage.writeUint32(0) | |
851 | - storage.writeUint32(0) | |
852 | - storage.writeUint32(0) | |
853 | - storage.writeUint32(0xF8) | |
854 | - return storage.stream | |
855 | -} | |
856 | - | |
857 | -// writeEncryptedPackage provides a function to write compound file | |
858 | -// writeEncryptedPackage stream. The writeEncryptedPackage stream is an | |
859 | -// encrypted stream of bytes containing the entire ECMA-376 source file in | |
860 | -// compressed form. | |
861 | -func (c *cfb) writeEncryptedPackage(propertyCount, size int) []byte { | |
862 | - storage := cfb{stream: make([]byte, 128)} | |
863 | - storage.writeStrings("EncryptedPackage") | |
864 | - storage.position = 0x40 | |
865 | - storage.writeUint16(0x22) | |
866 | - storage.writeBytes([]byte{2, 0}) | |
867 | - storage.writeUint32(-1) | |
868 | - storage.writeUint32(-1) | |
869 | - storage.writeUint32(-1) | |
870 | - storage.position = 0x64 | |
871 | - storage.writeUint32(0) | |
872 | - storage.writeUint32(0) | |
873 | - storage.writeUint32(0) | |
874 | - storage.writeUint32(0) | |
875 | - storage.writeUint32(propertyCount) | |
876 | - storage.writeUint32(size) | |
877 | - return storage.stream | |
878 | -} | |
879 | - | |
880 | -// writeDataSpaces provides a function to write compound file writeDataSpaces | |
881 | -// stream. The data spaces structure consists of a set of interrelated | |
882 | -// storages and streams in an OLE compound file. | |
883 | -func (c *cfb) writeDataSpaces() []byte { | |
884 | - storage := cfb{stream: make([]byte, 128)} | |
885 | - storage.writeUint16(0x06) | |
886 | - storage.position = 0x40 | |
887 | - storage.writeUint16(0x18) | |
888 | - storage.writeBytes([]byte{1, 0}) | |
889 | - storage.writeUint32(-1) | |
890 | - storage.writeUint32(-1) | |
891 | - storage.writeUint32(5) | |
892 | - storage.position = 0x64 | |
893 | - storage.writeUint32(0) | |
894 | - storage.writeUint32(0) | |
895 | - storage.writeUint32(0) | |
896 | - storage.writeUint32(0) | |
897 | - storage.writeUint32(0) | |
898 | - storage.writeUint32(0) | |
899 | - return storage.stream | |
900 | -} | |
901 | - | |
902 | -// writeVersion provides a function to write compound file version. The | |
903 | -// writeVersion structure specifies the version of a product or feature. It | |
904 | -// contains a major and a minor version number. | |
905 | -func (c *cfb) writeVersion() []byte { | |
906 | - storage := cfb{stream: make([]byte, 128)} | |
907 | - storage.writeStrings("Version") | |
908 | - storage.position = 0x40 | |
909 | - storage.writeUint16(0x10) | |
910 | - storage.writeBytes([]byte{2, 1}) | |
911 | - storage.writeUint32(-1) | |
912 | - storage.writeUint32(-1) | |
913 | - storage.writeUint32(-1) | |
914 | - storage.position = 0x64 | |
915 | - storage.writeUint32(0) | |
916 | - storage.writeUint32(0) | |
917 | - storage.writeUint32(0) | |
918 | - storage.writeUint32(0) | |
919 | - storage.writeUint32(4) | |
920 | - storage.writeUint32(76) | |
921 | - return storage.stream | |
922 | -} | |
923 | - | |
924 | -// writeDataSpaceMap provides a function to write compound file | |
925 | -// writeDataSpaceMap stream. The writeDataSpaceMap structure associates | |
926 | -// protected content with data space definitions. The data space definition, | |
927 | -// in turn, describes the series of transforms that MUST be applied to that | |
928 | -// protected content to restore it to its original form. By using a map to | |
929 | -// associate data space definitions with content, a single data space | |
930 | -// definition can be used to define the transforms applied to more than one | |
931 | -// piece of protected content. However, a given piece of protected content can | |
932 | -// be referenced only by a single data space definition. | |
933 | -func (c *cfb) writeDataSpaceMap() []byte { | |
934 | - storage := cfb{stream: make([]byte, 128)} | |
935 | - storage.writeStrings("DataSpaceMap") | |
936 | - storage.position = 0x40 | |
937 | - storage.writeUint16(0x1A) | |
938 | - storage.writeBytes([]byte{2, 1}) | |
939 | - storage.writeUint32(0x04) | |
940 | - storage.writeUint32(0x06) | |
941 | - storage.writeUint32(-1) | |
942 | - storage.position = 0x64 | |
943 | - storage.writeUint32(0) | |
944 | - storage.writeUint32(0) | |
945 | - storage.writeUint32(0) | |
946 | - storage.writeUint32(0) | |
947 | - storage.writeUint32(6) | |
948 | - storage.writeUint32(112) | |
949 | - return storage.stream | |
950 | -} | |
951 | - | |
952 | -// writeDataSpaceInfo provides a function to write compound file | |
953 | -// writeDataSpaceInfo storage. The writeDataSpaceInfo is a storage containing | |
954 | -// the data space definitions used in the file. This storage must contain one | |
955 | -// or more streams, each of which contains a DataSpaceDefinition structure. | |
956 | -// The storage must contain exactly one stream for each DataSpaceMapEntry | |
957 | -// structure in the DataSpaceMap stream. The name of each stream must be equal | |
958 | -// to the DataSpaceName field of exactly one DataSpaceMapEntry structure | |
959 | -// contained in the DataSpaceMap stream. | |
960 | -func (c *cfb) writeDataSpaceInfo() []byte { | |
961 | - storage := cfb{stream: make([]byte, 128)} | |
962 | - storage.writeStrings("DataSpaceInfo") | |
963 | - storage.position = 0x40 | |
964 | - storage.writeUint16(0x1C) | |
965 | - storage.writeBytes([]byte{1, 1}) | |
966 | - storage.writeUint32(-1) | |
967 | - storage.writeUint32(8) | |
968 | - storage.writeUint32(7) | |
969 | - storage.position = 0x64 | |
970 | - storage.writeUint32(0) | |
971 | - storage.writeUint32(0) | |
972 | - storage.writeUint32(0) | |
973 | - storage.writeUint32(0) | |
974 | - storage.writeUint32(0) | |
975 | - storage.writeUint32(0) | |
976 | - return storage.stream | |
977 | -} | |
978 | - | |
979 | -// writeStrongEncryptionDataSpace provides a function to write compound file | |
980 | -// writeStrongEncryptionDataSpace stream. | |
981 | -func (c *cfb) writeStrongEncryptionDataSpace() []byte { | |
982 | - storage := cfb{stream: make([]byte, 128)} | |
983 | - storage.writeStrings("StrongEncryptionDataSpace") | |
984 | - storage.position = 0x40 | |
985 | - storage.writeUint16(0x34) | |
986 | - storage.writeBytes([]byte{2, 1}) | |
987 | - storage.writeUint32(-1) | |
988 | - storage.writeUint32(-1) | |
989 | - storage.writeUint32(-1) | |
990 | - storage.position = 0x64 | |
991 | - storage.writeUint32(0) | |
992 | - storage.writeUint32(0) | |
993 | - storage.writeUint32(0) | |
994 | - storage.writeUint32(0) | |
995 | - storage.writeUint32(8) | |
996 | - storage.writeUint32(64) | |
997 | - return storage.stream | |
998 | -} | |
999 | - | |
1000 | -// writeTransformInfo provides a function to write compound file | |
1001 | -// writeTransformInfo storage. writeTransformInfo is a storage containing | |
1002 | -// definitions for the transforms used in the data space definitions stored in | |
1003 | -// the DataSpaceInfo storage. The stream contains zero or more definitions for | |
1004 | -// the possible transforms that can be applied to the data in content | |
1005 | -// streams. | |
1006 | -func (c *cfb) writeTransformInfo() []byte { | |
1007 | - storage := cfb{stream: make([]byte, 128)} | |
1008 | - storage.writeStrings("TransformInfo") | |
1009 | - storage.position = 0x40 | |
1010 | - storage.writeUint16(0x1C) | |
1011 | - storage.writeBytes([]byte{1, 0}) | |
1012 | - storage.writeUint32(-1) | |
1013 | - storage.writeUint32(-1) | |
1014 | - storage.writeUint32(9) | |
1015 | - storage.position = 0x64 | |
1016 | - storage.writeUint32(0) | |
1017 | - storage.writeUint32(0) | |
1018 | - storage.writeUint32(0) | |
1019 | - storage.writeUint32(0) | |
1020 | - storage.writeUint32(0) | |
1021 | - storage.writeUint32(0) | |
1022 | - return storage.stream | |
722 | + return len(L) - len(R) | |
1023 | 723 | } |
1024 | 724 | |
1025 | -// writeStrongEncryptionTransform provides a function to write compound file | |
1026 | -// writeStrongEncryptionTransform storage. | |
1027 | -func (c *cfb) writeStrongEncryptionTransform() []byte { | |
1028 | - storage := cfb{stream: make([]byte, 128)} | |
1029 | - storage.writeStrings("StrongEncryptionTransform") | |
1030 | - storage.position = 0x40 | |
1031 | - storage.writeUint16(0x34) | |
1032 | - storage.writeBytes([]byte{1}) | |
1033 | - storage.writeBytes([]byte{1}) | |
1034 | - storage.writeUint32(-1) | |
1035 | - storage.writeUint32(-1) | |
1036 | - storage.writeUint32(0x0A) | |
1037 | - storage.position = 0x64 | |
1038 | - storage.writeUint32(0) | |
1039 | - storage.writeUint32(0) | |
1040 | - storage.writeUint32(0) | |
1041 | - storage.writeUint32(0) | |
1042 | - storage.writeUint32(0) | |
1043 | - storage.writeUint32(0) | |
1044 | - return storage.stream | |
725 | +// prepare provides a function to prepare object before write stream. | |
726 | +func (c *cfb) prepare() { | |
727 | + type object struct { | |
728 | + path string | |
729 | + sector sector | |
730 | + } | |
731 | + var objects []object | |
732 | + for i := 0; i < len(c.paths); i++ { | |
733 | + if c.sectors[i].typeID == 0 { | |
734 | + continue | |
735 | + } | |
736 | + objects = append(objects, object{path: c.paths[i], sector: c.sectors[i]}) | |
737 | + } | |
738 | + sort.Slice(objects, func(i, j int) bool { | |
739 | + return c.compare(objects[i].path, objects[j].path) == 0 | |
740 | + }) | |
741 | + c.paths, c.sectors = []string{}, []sector{} | |
742 | + for i := 0; i < len(objects); i++ { | |
743 | + c.paths = append(c.paths, objects[i].path) | |
744 | + c.sectors = append(c.sectors, objects[i].sector) | |
745 | + } | |
746 | + for i := 0; i < len(objects); i++ { | |
747 | + sector, path := &c.sectors[i], c.paths[i] | |
748 | + sector.name, sector.color = filepath.Base(path), 1 | |
749 | + sector.L, sector.R, sector.C = -1, -1, -1 | |
750 | + sector.size, sector.start = len(sector.content), 0 | |
751 | + if len(sector.clsID) == 0 { | |
752 | + sector.clsID = headerCLSID | |
753 | + } | |
754 | + if i == 0 { | |
755 | + sector.C = -1 | |
756 | + if len(objects) > 1 { | |
757 | + sector.C = 1 | |
758 | + } | |
759 | + sector.size, sector.typeID = 0, 5 | |
760 | + } else { | |
761 | + if len(c.paths) > i+1 && filepath.Dir(c.paths[i+1]) == filepath.Dir(path) { | |
762 | + sector.R = i + 1 | |
763 | + } | |
764 | + sector.typeID = 2 | |
765 | + } | |
766 | + } | |
1045 | 767 | } |
1046 | 768 | |
1047 | -// writePrimary provides a function to write compound file writePrimary stream. | |
1048 | -func (c *cfb) writePrimary() []byte { | |
1049 | - storage := cfb{stream: make([]byte, 128)} | |
1050 | - storage.writeUint16(0x06) | |
1051 | - storage.writeStrings("Primary") | |
1052 | - storage.position = 0x40 | |
1053 | - storage.writeUint16(0x12) | |
1054 | - storage.writeBytes([]byte{2, 1}) | |
1055 | - storage.writeUint32(-1) | |
1056 | - storage.writeUint32(-1) | |
1057 | - storage.writeUint32(-1) | |
1058 | - storage.position = 0x64 | |
1059 | - storage.writeUint32(0) | |
1060 | - storage.writeUint32(0) | |
1061 | - storage.writeUint32(0) | |
1062 | - storage.writeUint32(0) | |
1063 | - storage.writeUint32(9) | |
1064 | - storage.writeUint32(208) | |
1065 | - return storage.stream | |
769 | +// locate provides a function to locate sectors location and size of the | |
770 | +// compound file. | |
771 | +func (c *cfb) locate() []int { | |
772 | + var miniStreamSectorSize, FATSectorSize int | |
773 | + for i := 0; i < len(c.sectors); i++ { | |
774 | + sector := c.sectors[i] | |
775 | + if len(sector.content) == 0 { | |
776 | + continue | |
777 | + } | |
778 | + size := len(sector.content) | |
779 | + if size > 0 { | |
780 | + if size < 0x1000 { | |
781 | + miniStreamSectorSize += (size + 0x3F) >> 6 | |
782 | + } else { | |
783 | + FATSectorSize += (size + 0x01FF) >> 9 | |
784 | + } | |
785 | + } | |
786 | + } | |
787 | + directorySectors := (len(c.paths) + 3) >> 2 | |
788 | + miniStreamSectors := (miniStreamSectorSize + 7) >> 3 | |
789 | + miniFATSectors := (miniStreamSectorSize + 0x7F) >> 7 | |
790 | + sectors := miniStreamSectors + FATSectorSize + directorySectors + miniFATSectors | |
791 | + FATSectors := (sectors + 0x7F) >> 7 | |
792 | + DIFATSectors := 0 | |
793 | + if FATSectors > 109 { | |
794 | + DIFATSectors = int(math.Ceil((float64(FATSectors) - 109) / 0x7F)) | |
795 | + } | |
796 | + for ((sectors + FATSectors + DIFATSectors + 0x7F) >> 7) > FATSectors { | |
797 | + FATSectors++ | |
798 | + if FATSectors <= 109 { | |
799 | + DIFATSectors = 0 | |
800 | + } else { | |
801 | + DIFATSectors = int(math.Ceil((float64(FATSectors) - 109) / 0x7F)) | |
802 | + } | |
803 | + } | |
804 | + location := []int{1, DIFATSectors, FATSectors, miniFATSectors, directorySectors, FATSectorSize, miniStreamSectorSize, 0} | |
805 | + c.sectors[0].size = miniStreamSectorSize << 6 | |
806 | + c.sectors[0].start = location[0] + location[1] + location[2] + location[3] + location[4] + location[5] | |
807 | + location[7] = c.sectors[0].start + ((location[6] + 7) >> 3) | |
808 | + return location | |
1066 | 809 | } |
1067 | 810 | |
1068 | -// writeNoneDir provides a function to write compound file writeNoneDir stream. | |
1069 | -func (c *cfb) writeNoneDir() []byte { | |
1070 | - storage := cfb{stream: make([]byte, 128)} | |
1071 | - storage.position = 0x40 | |
1072 | - storage.writeUint16(0x00) | |
1073 | - storage.writeUint16(0x00) | |
1074 | - storage.writeUint32(-1) | |
1075 | - storage.writeUint32(-1) | |
1076 | - storage.writeUint32(-1) | |
1077 | - return storage.stream | |
811 | +// writeMSAT provides a function to write compound file master sector allocation | |
812 | +// table. | |
813 | +func (c *cfb) writeMSAT(location []int) { | |
814 | + var i, offset int | |
815 | + for i = 0; i < 109; i++ { | |
816 | + if i < location[2] { | |
817 | + c.writeUint32(location[1] + i) | |
818 | + } else { | |
819 | + c.writeUint32(-1) | |
820 | + } | |
821 | + } | |
822 | + if location[1] != 0 { | |
823 | + for offset = 0; offset < location[1]; offset++ { | |
824 | + for ; i < 236+offset*127; i++ { | |
825 | + if i < location[2] { | |
826 | + c.writeUint32(location[1] + i) | |
827 | + } else { | |
828 | + c.writeUint32(-1) | |
829 | + } | |
830 | + } | |
831 | + if offset == location[1]-1 { | |
832 | + c.writeUint32(endOfChain) | |
833 | + } else { | |
834 | + c.writeUint32(offset + 1) | |
835 | + } | |
836 | + } | |
837 | + } | |
1078 | 838 | } |
1079 | 839 | |
1080 | 840 | // writeDirectoryEntry provides a function to write compound file directory |
@@ -1083,189 +843,175 @@ func (c *cfb) writeNoneDir() []byte { | ||
1083 | 843 | // within a compound file is represented by a single directory entry. The |
1084 | 844 | // space for the directory sectors that are holding the array is allocated |
1085 | 845 | // from the FAT. |
1086 | -func (c *cfb) writeDirectoryEntry(propertyCount, customSectID, size int) []byte { | |
1087 | - var storage cfb | |
1088 | - if size < 0 { | |
1089 | - size = 0 | |
1090 | - } | |
1091 | - for _, entry := range [][]byte{ | |
1092 | - c.writeRootEntry(customSectID), | |
1093 | - c.writeEncryptionInfo(), | |
1094 | - c.writeEncryptedPackage(propertyCount, size), | |
1095 | - c.writeDataSpaces(), | |
1096 | - c.writeVersion(), | |
1097 | - c.writeDataSpaceMap(), | |
1098 | - c.writeDataSpaceInfo(), | |
1099 | - c.writeStrongEncryptionDataSpace(), | |
1100 | - c.writeTransformInfo(), | |
1101 | - c.writeStrongEncryptionTransform(), | |
1102 | - c.writePrimary(), | |
1103 | - c.writeNoneDir(), | |
1104 | - } { | |
1105 | - storage.writeBytes(entry) | |
1106 | - } | |
1107 | - return storage.stream | |
1108 | -} | |
1109 | - | |
1110 | -// writeMSAT provides a function to write compound file master sector allocation | |
1111 | -// table. | |
1112 | -func (c *cfb) writeMSAT(MSATBlocks, SATBlocks int, MSAT []int) []int { | |
1113 | - if MSATBlocks > 0 { | |
1114 | - cnt, MSATIdx := MSATBlocks*128+109, 0 | |
1115 | - for i := 0; i < cnt; i++ { | |
1116 | - if i < SATBlocks { | |
1117 | - bufferSize := i - 109 | |
1118 | - if bufferSize > 0 && bufferSize%0x80 == 0 { | |
1119 | - MSATIdx++ | |
1120 | - MSAT = append(MSAT, MSATIdx) | |
1121 | - } | |
1122 | - MSAT = append(MSAT, i+MSATBlocks) | |
1123 | - continue | |
1124 | - } | |
1125 | - MSAT = append(MSAT, -1) | |
846 | +func (c *cfb) writeDirectoryEntry(location []int) { | |
847 | + var sector sector | |
848 | + var j, sectorSize int | |
849 | + for i := 0; i < location[4]<<2; i++ { | |
850 | + var path string | |
851 | + if i < len(c.paths) { | |
852 | + path = c.paths[i] | |
1126 | 853 | } |
1127 | - return MSAT | |
1128 | - } | |
1129 | - for i := 0; i < 109; i++ { | |
1130 | - if i < SATBlocks { | |
1131 | - MSAT = append(MSAT, i) | |
854 | + if i >= len(c.paths) || len(path) == 0 { | |
855 | + for j = 0; j < 17; j++ { | |
856 | + c.writeUint32(0) | |
857 | + } | |
858 | + for j = 0; j < 3; j++ { | |
859 | + c.writeUint32(-1) | |
860 | + } | |
861 | + for j = 0; j < 12; j++ { | |
862 | + c.writeUint32(0) | |
863 | + } | |
1132 | 864 | continue |
1133 | 865 | } |
1134 | - MSAT = append(MSAT, -1) | |
1135 | - } | |
1136 | - return MSAT | |
1137 | -} | |
1138 | - | |
1139 | -// writeSAT provides a function to write compound file sector allocation | |
1140 | -// table. | |
1141 | -func (c *cfb) writeSAT(MSATBlocks, SATBlocks, SSATBlocks, directoryBlocks, fileBlocks, streamBlocks int, SAT []int) (int, []int) { | |
1142 | - var blocks int | |
1143 | - if SATBlocks > 0 { | |
1144 | - for i := 1; i <= MSATBlocks; i++ { | |
1145 | - SAT = append(SAT, -4) | |
866 | + sector = c.sectors[i] | |
867 | + if i == 0 { | |
868 | + if sector.size > 0 { | |
869 | + sector.start = sector.start - 1 | |
870 | + } else { | |
871 | + sector.start = endOfChain | |
872 | + } | |
1146 | 873 | } |
1147 | - blocks = MSATBlocks | |
1148 | - for i := 1; i <= SATBlocks; i++ { | |
1149 | - SAT = append(SAT, -3) | |
874 | + name := sector.name | |
875 | + sectorSize = 2 * (len(name) + 1) | |
876 | + c.writeStrings(name) | |
877 | + c.position += 64 - 2*(len(name)) | |
878 | + c.writeUint16(sectorSize) | |
879 | + c.writeBytes([]byte(string(rune(sector.typeID)))) | |
880 | + c.writeBytes([]byte(string(rune(sector.color)))) | |
881 | + c.writeUint32(sector.L) | |
882 | + c.writeUint32(sector.R) | |
883 | + c.writeUint32(sector.C) | |
884 | + if len(sector.clsID) == 0 { | |
885 | + for j = 0; j < 4; j++ { | |
886 | + c.writeUint32(0) | |
887 | + } | |
888 | + } else { | |
889 | + c.writeBytes(sector.clsID) | |
1150 | 890 | } |
1151 | - blocks += SATBlocks | |
1152 | - for i := 1; i < SSATBlocks; i++ { | |
1153 | - SAT = append(SAT, i) | |
891 | + c.writeUint32(sector.state) | |
892 | + c.writeUint32(0) | |
893 | + c.writeUint32(0) | |
894 | + c.writeUint32(0) | |
895 | + c.writeUint32(0) | |
896 | + c.writeUint32(sector.start) | |
897 | + c.writeUint32(sector.size) | |
898 | + c.writeUint32(0) | |
899 | + } | |
900 | +} | |
901 | + | |
902 | +// writeSectorChains provides a function to write compound file sector chains. | |
903 | +func (c *cfb) writeSectorChains(location []int) sector { | |
904 | + var i, j, offset, sectorSize int | |
905 | + writeSectorChain := func(head, offset int) int { | |
906 | + for offset += head; i < offset-1; i++ { | |
907 | + c.writeUint32(i + 1) | |
1154 | 908 | } |
1155 | - SAT = append(SAT, -2) | |
1156 | - blocks += SSATBlocks | |
1157 | - for i := 1; i < directoryBlocks; i++ { | |
1158 | - SAT = append(SAT, i+blocks) | |
909 | + if head != 0 { | |
910 | + i++ | |
911 | + c.writeUint32(endOfChain) | |
1159 | 912 | } |
1160 | - SAT = append(SAT, -2) | |
1161 | - blocks += directoryBlocks | |
1162 | - for i := 1; i < fileBlocks; i++ { | |
1163 | - SAT = append(SAT, i+blocks) | |
913 | + return offset | |
914 | + } | |
915 | + for offset += location[1]; i < offset; i++ { | |
916 | + c.writeUint32(difSect) | |
917 | + } | |
918 | + for offset += location[2]; i < offset; i++ { | |
919 | + c.writeUint32(fatSect) | |
920 | + } | |
921 | + offset = writeSectorChain(location[3], offset) | |
922 | + offset = writeSectorChain(location[4], offset) | |
923 | + sector := c.sectors[0] | |
924 | + for ; j < len(c.sectors); j++ { | |
925 | + if sector = c.sectors[j]; len(sector.content) == 0 { | |
926 | + continue | |
1164 | 927 | } |
1165 | - SAT = append(SAT, -2) | |
1166 | - blocks += fileBlocks | |
1167 | - for i := 1; i < streamBlocks; i++ { | |
1168 | - SAT = append(SAT, i+blocks) | |
928 | + if sectorSize = len(sector.content); sectorSize < 0x1000 { | |
929 | + continue | |
1169 | 930 | } |
1170 | - SAT = append(SAT, -2) | |
931 | + c.sectors[j].start = offset | |
932 | + offset = writeSectorChain((sectorSize+0x01FF)>>9, offset) | |
1171 | 933 | } |
1172 | - return blocks, SAT | |
1173 | -} | |
1174 | - | |
1175 | -// Writer provides a function to create compound file with given info stream | |
1176 | -// and package stream. | |
1177 | -// | |
1178 | -// MSAT - The master sector allocation table | |
1179 | -// SSAT - The short sector allocation table | |
1180 | -// SAT - The sector allocation table | |
1181 | -func (c *cfb) Writer(encryptionInfoBuffer, encryptedPackage []byte) []byte { | |
1182 | - var ( | |
1183 | - storage cfb | |
1184 | - MSAT, SAT, SSAT []int | |
1185 | - directoryBlocks, fileBlocks, SSATBlocks = 3, 2, 1 | |
1186 | - size = int(math.Max(float64(len(encryptedPackage)), float64(packageEncryptionChunkSize))) | |
1187 | - streamBlocks = len(encryptedPackage) / 0x200 | |
1188 | - ) | |
1189 | - if len(encryptedPackage)%0x200 > 0 { | |
1190 | - streamBlocks++ | |
1191 | - } | |
1192 | - propertyBlocks := directoryBlocks + fileBlocks + SSATBlocks | |
1193 | - blockSize := (streamBlocks + propertyBlocks) * 4 | |
1194 | - SATBlocks := blockSize / 0x200 | |
1195 | - if blockSize%0x200 > 0 { | |
1196 | - SATBlocks++ | |
1197 | - } | |
1198 | - MSATBlocks, blocksChanged := 0, true | |
1199 | - for blocksChanged { | |
1200 | - var SATCap, MSATCap int | |
1201 | - blocksChanged = false | |
1202 | - blockSize = (streamBlocks + propertyBlocks + SATBlocks + MSATBlocks) * 4 | |
1203 | - SATCap = blockSize / 0x200 | |
1204 | - if blockSize%0x200 > 0 { | |
1205 | - SATCap++ | |
934 | + writeSectorChain((location[6]+7)>>3, offset) | |
935 | + for c.position&0x1FF != 0 { | |
936 | + c.writeUint32(endOfChain) | |
937 | + } | |
938 | + i, offset = 0, 0 | |
939 | + for j = 0; j < len(c.sectors); j++ { | |
940 | + if sector = c.sectors[j]; len(sector.content) == 0 { | |
941 | + continue | |
1206 | 942 | } |
1207 | - if SATCap > SATBlocks { | |
1208 | - SATBlocks, blocksChanged = SATCap, true | |
943 | + if sectorSize = len(sector.content); sectorSize == 0 || sectorSize >= 0x1000 { | |
1209 | 944 | continue |
1210 | 945 | } |
1211 | - if SATBlocks > 109 { | |
1212 | - blockRemains := (SATBlocks - 109) * 4 | |
1213 | - blockBuffer := blockRemains % 0x200 | |
1214 | - MSATCap = blockRemains / 0x200 | |
1215 | - if blockBuffer > 0 { | |
1216 | - MSATCap++ | |
1217 | - } | |
1218 | - if blockBuffer+(4*MSATCap) > 0x200 { | |
1219 | - MSATCap++ | |
946 | + sector.start = offset | |
947 | + offset = writeSectorChain((sectorSize+0x3F)>>6, offset) | |
948 | + } | |
949 | + for c.position&0x1FF != 0 { | |
950 | + c.writeUint32(endOfChain) | |
951 | + } | |
952 | + return sector | |
953 | +} | |
954 | + | |
955 | +// write provides a function to create compound file package stream. | |
956 | +func (c *cfb) write() []byte { | |
957 | + c.prepare() | |
958 | + location := c.locate() | |
959 | + c.stream = make([]byte, location[7]<<9) | |
960 | + var i, j int | |
961 | + for i = 0; i < 8; i++ { | |
962 | + c.writeBytes([]byte{oleIdentifier[i]}) | |
963 | + } | |
964 | + c.writeBytes(make([]byte, 16)) | |
965 | + c.writeUint16(0x003E) | |
966 | + c.writeUint16(0x0003) | |
967 | + c.writeUint16(0xFFFE) | |
968 | + c.writeUint16(0x0009) | |
969 | + c.writeUint16(0x0006) | |
970 | + c.writeBytes(make([]byte, 10)) | |
971 | + c.writeUint32(location[2]) | |
972 | + c.writeUint32(location[0] + location[1] + location[2] + location[3] - 1) | |
973 | + c.writeUint32(0) | |
974 | + c.writeUint32(1 << 12) | |
975 | + if location[3] != 0 { | |
976 | + c.writeUint32(location[0] + location[1] + location[2] - 1) | |
977 | + } else { | |
978 | + c.writeUint32(endOfChain) | |
979 | + } | |
980 | + c.writeUint32(location[3]) | |
981 | + if location[1] != 0 { | |
982 | + c.writeUint32(location[0] - 1) | |
983 | + } else { | |
984 | + c.writeUint32(endOfChain) | |
985 | + } | |
986 | + c.writeUint32(location[1]) | |
987 | + c.writeMSAT(location) | |
988 | + sector := c.writeSectorChains(location) | |
989 | + c.writeDirectoryEntry(location) | |
990 | + for i = 1; i < len(c.sectors); i++ { | |
991 | + sector = c.sectors[i] | |
992 | + if sector.size >= 0x1000 { | |
993 | + c.position = (sector.start + 1) << 9 | |
994 | + for j = 0; j < sector.size; j++ { | |
995 | + c.writeBytes([]byte{sector.content[j]}) | |
1220 | 996 | } |
1221 | - if MSATCap > MSATBlocks { | |
1222 | - MSATBlocks, blocksChanged = MSATCap, true | |
997 | + for ; j&0x1FF != 0; j++ { | |
998 | + c.writeBytes([]byte{0}) | |
1223 | 999 | } |
1224 | 1000 | } |
1225 | 1001 | } |
1226 | - MSAT = c.writeMSAT(MSATBlocks, SATBlocks, MSAT) | |
1227 | - blocks, SAT := c.writeSAT(MSATBlocks, SATBlocks, SSATBlocks, directoryBlocks, fileBlocks, streamBlocks, SAT) | |
1228 | - for i := 0; i < 8; i++ { | |
1229 | - storage.writeBytes([]byte{oleIdentifier[i]}) | |
1230 | - } | |
1231 | - storage.writeBytes(make([]byte, 16)) | |
1232 | - storage.writeUint16(0x003E) | |
1233 | - storage.writeUint16(0x0003) | |
1234 | - storage.writeUint16(-2) | |
1235 | - storage.writeUint16(9) | |
1236 | - storage.writeUint32(6) | |
1237 | - storage.writeUint32(0) | |
1238 | - storage.writeUint32(0) | |
1239 | - storage.writeUint32(SATBlocks) | |
1240 | - storage.writeUint32(MSATBlocks + SATBlocks + SSATBlocks) | |
1241 | - storage.writeUint32(0) | |
1242 | - storage.writeUint32(0x00001000) | |
1243 | - storage.writeUint32(SATBlocks + MSATBlocks) | |
1244 | - storage.writeUint32(SSATBlocks) | |
1245 | - if MSATBlocks > 0 { | |
1246 | - storage.writeUint32(0) | |
1247 | - storage.writeUint32(MSATBlocks) | |
1248 | - } else { | |
1249 | - storage.writeUint32(-2) | |
1250 | - storage.writeUint32(0) | |
1251 | - } | |
1252 | - for _, block := range MSAT { | |
1253 | - storage.writeUint32(block) | |
1254 | - } | |
1255 | - for i := 0; i < SATBlocks*128; i++ { | |
1256 | - if i < len(SAT) { | |
1257 | - storage.writeUint32(SAT[i]) | |
1258 | - continue | |
1002 | + for i = 1; i < len(c.sectors); i++ { | |
1003 | + sector = c.sectors[i] | |
1004 | + if sector.size > 0 && sector.size < 0x1000 { | |
1005 | + for j = 0; j < sector.size; j++ { | |
1006 | + c.writeBytes([]byte{sector.content[j]}) | |
1007 | + } | |
1008 | + for ; j&0x3F != 0; j++ { | |
1009 | + c.writeBytes([]byte{0}) | |
1010 | + } | |
1259 | 1011 | } |
1260 | - storage.writeUint32(-1) | |
1261 | 1012 | } |
1262 | - fileStream, SSATStream := c.writeFileStream(encryptionInfoBuffer, SSAT) | |
1263 | - for _, block := range SSATStream { | |
1264 | - storage.writeUint32(block) | |
1013 | + for c.position < len(c.stream) { | |
1014 | + c.writeBytes([]byte{0}) | |
1265 | 1015 | } |
1266 | - directoryEntry := c.writeDirectoryEntry(blocks, blocks-fileBlocks, size) | |
1267 | - storage.writeBytes(directoryEntry) | |
1268 | - storage.writeBytes(fileStream) | |
1269 | - storage.writeBytes(encryptedPackage) | |
1270 | - return storage.stream | |
1016 | + return c.stream | |
1271 | 1017 | } |
@@ -59,11 +59,6 @@ func TestEncryptionMechanism(t *testing.T) { | ||
59 | 59 | assert.EqualError(t, err, ErrUnknownEncryptMechanism.Error()) |
60 | 60 | } |
61 | 61 | |
62 | -func TestEncryptionWriteDirectoryEntry(t *testing.T) { | |
63 | - cfb := cfb{} | |
64 | - assert.Equal(t, 1536, len(cfb.writeDirectoryEntry(0, 0, -1))) | |
65 | -} | |
66 | - | |
67 | 62 | func TestHashing(t *testing.T) { |
68 | 63 | assert.Equal(t, hashing("unsupportedHashAlgorithm", []byte{}), []byte(nil)) |
69 | 64 | } |