mirror of
https://github.com/mayswind/ezbookkeeping.git
synced 2026-05-18 08:44:25 +08:00
read and check ofx 2.x file header
This commit is contained in:
@@ -1,17 +1,25 @@
|
||||
package ofx
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/html/charset"
|
||||
|
||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/log"
|
||||
)
|
||||
|
||||
var ofx2HeaderPattern = regexp.MustCompile("<\\?OFX( +[A-Z]+=\"[^=]*\")* *\\?>")
|
||||
var ofx2HeaderAttributePattern = regexp.MustCompile(" +([A-Z]+)=\"([^=]*)\"")
|
||||
|
||||
// ofxFileReader defines the structure of open financial exchange (ofx) file reader
|
||||
type ofxFileReader struct {
|
||||
fileHeader *ofxFileHeader
|
||||
xmlDecoder *xml.Decoder
|
||||
}
|
||||
|
||||
@@ -25,27 +33,97 @@ func (r *ofxFileReader) read(ctx core.Context) (*ofxFile, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
file.FileHeader = r.fileHeader
|
||||
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func createNewOFXFileReader(data []byte) (*ofxFileReader, error) {
|
||||
if len(data) > 5 && data[0] == 0x3C && data[1] == 0x3F && data[2] == 0x78 && data[3] == 0x6D && data[4] == 0x6C { // ofx 2.x starts with <?xml
|
||||
xmlDecoder := xml.NewDecoder(bytes.NewReader(data))
|
||||
xmlDecoder.CharsetReader = charset.NewReaderLabel
|
||||
|
||||
return &ofxFileReader{
|
||||
xmlDecoder: xmlDecoder,
|
||||
}, nil
|
||||
} else if len(data) > 13 && string(data[0:13]) == "OFXHEADER:100" { // ofx 1.x starts with OFXHEADER:100
|
||||
func createNewOFXFileReader(ctx core.Context, data []byte) (*ofxFileReader, error) {
|
||||
if len(data) > 5 && string(data[0:5]) == "<?xml" { // ofx 2.x starts with <?xml
|
||||
return createNewOFX2FileReader(ctx, data, true)
|
||||
} else if len(data) > 10 && string(data[0:10]) == "OFXHEADER:" { // ofx 1.x starts with OFXHEADER:
|
||||
|
||||
} else if len(data) > 5 && string(data[0:5]) == "<OFX>" { // no ofx header
|
||||
xmlDecoder := xml.NewDecoder(bytes.NewReader(data))
|
||||
xmlDecoder.CharsetReader = charset.NewReaderLabel
|
||||
|
||||
return &ofxFileReader{
|
||||
xmlDecoder: xmlDecoder,
|
||||
}, nil
|
||||
return createNewOFX2FileReader(ctx, data, false)
|
||||
}
|
||||
|
||||
return nil, errs.ErrInvalidOFXFile
|
||||
}
|
||||
|
||||
func createNewOFX2FileReader(ctx core.Context, data []byte, withHeader bool) (*ofxFileReader, error) {
|
||||
var fileHeader *ofxFileHeader = nil
|
||||
var err error
|
||||
|
||||
if withHeader {
|
||||
fileHeader, err = readOFX2FileHeader(ctx, data)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if fileHeader.OFXDeclarationVersion != ofxVersion2 {
|
||||
log.Errorf(ctx, "[ofx_data_reader.createNewOFX2FileReader] cannot parse ofx 2.x file header, because declaration version is \"%s\"", fileHeader.OFXDeclarationVersion)
|
||||
return nil, errs.ErrInvalidOFXFile
|
||||
}
|
||||
}
|
||||
|
||||
xmlDecoder := xml.NewDecoder(bytes.NewReader(data))
|
||||
xmlDecoder.CharsetReader = charset.NewReaderLabel
|
||||
|
||||
return &ofxFileReader{
|
||||
fileHeader: fileHeader,
|
||||
xmlDecoder: xmlDecoder,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func readOFX2FileHeader(ctx core.Context, data []byte) (fileHeader *ofxFileHeader, err error) {
|
||||
reader := bytes.NewReader(data)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
fileHeader = &ofxFileHeader{}
|
||||
headerLine := ""
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
|
||||
ofxHeaderStartIndex := strings.Index(line, "<?OFX ")
|
||||
|
||||
if ofxHeaderStartIndex >= 0 {
|
||||
headerLine = ofx2HeaderPattern.FindString(line)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if headerLine == "" {
|
||||
log.Errorf(ctx, "[ofx_data_reader.readOFX2FileHeader] cannot find ofx 2.x file header")
|
||||
return nil, errs.ErrInvalidOFXFile
|
||||
}
|
||||
|
||||
headerAttributes := ofx2HeaderAttributePattern.FindAllStringSubmatch(headerLine, -1)
|
||||
|
||||
for _, attributeItems := range headerAttributes {
|
||||
if len(attributeItems) != 3 {
|
||||
log.Warnf(ctx, "[ofx_data_reader.readOFX2FileHeader] cannot parse line in ofx 2.x file header, because item is \"%s\"", attributeItems)
|
||||
continue
|
||||
}
|
||||
|
||||
name := attributeItems[1]
|
||||
value := attributeItems[2]
|
||||
|
||||
if name == "OFXHEADER" {
|
||||
fileHeader.OFXDeclarationVersion = oFXDeclarationVersion(value)
|
||||
} else if name == "VERSION" {
|
||||
fileHeader.OFXDataVersion = value
|
||||
} else if name == "SECURITY" {
|
||||
fileHeader.Security = value
|
||||
} else if name == "OLDFILEUID" {
|
||||
fileHeader.OldFileUid = value
|
||||
} else if name == "NEWFILEUID" {
|
||||
fileHeader.NewFileUid = value
|
||||
} else {
|
||||
log.Warnf(ctx, "[ofx_data_reader.readOFX2FileHeader] cannot parse unknown header line in ofx 2.x file header, because item is \"%s\"", attributeItems)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return fileHeader, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user