2017-03-11 07:15:15 -05:00
package ofxgo
import (
2017-03-11 12:59:47 -05:00
"errors"
2017-03-22 20:59:40 -04:00
"github.com/aclindsa/go/src/encoding/xml"
2017-03-11 07:15:15 -05:00
)
2017-04-13 10:18:07 -04:00
// ProfileRequest represents a request for a server to provide a profile of its
// capabilities (which message sets and versions it supports, how to access
// them, which languages and which types of synchronization they support, etc.)
2017-03-11 07:18:02 -05:00
type ProfileRequest struct {
2017-03-30 10:24:26 -04:00
XMLName xml . Name ` xml:"PROFTRNRQ" `
TrnUID UID ` xml:"TRNUID" `
CltCookie String ` xml:"CLTCOOKIE,omitempty" `
TAN String ` xml:"TAN,omitempty" ` // Transaction authorization number
// TODO `xml:"OFXEXTENSION,omitempty"`
ClientRouting String ` xml:"PROFRQ>CLIENTROUTING" ` // Forced to NONE
2017-04-13 10:18:07 -04:00
DtProfUp Date ` xml:"PROFRQ>DTPROFUP" ` // Date and time client last received a profile update
2017-03-11 07:15:15 -05:00
}
2017-04-13 10:18:07 -04:00
// Name returns the name of the top-level transaction XML/SGML element
2017-03-11 07:18:02 -05:00
func ( r * ProfileRequest ) Name ( ) string {
2017-03-11 07:15:15 -05:00
return "PROFTRNRQ"
}
2017-04-13 10:18:07 -04:00
// Valid returns (true, nil) if this struct would be valid OFX if marshalled
// into XML/SGML
2017-03-11 07:18:02 -05:00
func ( r * ProfileRequest ) Valid ( ) ( bool , error ) {
2017-03-29 05:41:28 -04:00
// TODO implement
2017-03-11 07:15:15 -05:00
r . ClientRouting = "NONE"
return true , nil
}
2017-03-11 12:59:47 -05:00
2017-04-13 10:18:07 -04:00
// Type returns which message set this message belongs to (which Request
// element of type []Message it should appended to)
2017-03-31 09:25:07 -04:00
func ( r * ProfileRequest ) Type ( ) messageType {
2017-04-03 19:50:16 -04:00
return ProfRq
2017-03-31 09:25:07 -04:00
}
2017-04-13 10:18:07 -04:00
// SignonInfo provides the requirements to login to a single signon realm. A
// signon realm consists of all MessageSets which can be accessed using one set
// of login credentials. Most FI's only use one signon realm to make it easier
// and less confusing for the user.
2017-03-11 12:59:47 -05:00
type SignonInfo struct {
XMLName xml . Name ` xml:"SIGNONINFO" `
2017-04-13 10:18:07 -04:00
SignonRealm String ` xml:"SIGNONREALM" ` // The SignonRealm for which this SignonInfo provides information. This SignonInfo is valid for all MessageSets with SignonRealm fields matching this one
2017-03-11 12:59:47 -05:00
Min Int ` xml:"MIN" ` // Minimum number of password characters
Max Int ` xml:"MAX" ` // Maximum number of password characters
2017-04-06 05:58:22 -04:00
CharType charType ` xml:"CHARTYPE" ` // One of ALPHAONLY, NUMERICONLY, ALPHAORNUMERIC, ALPHAANDNUMERIC
2017-03-11 12:59:47 -05:00
CaseSen Boolean ` xml:"CASESEN" ` // Password is case-sensitive?
Special Boolean ` xml:"SPECIAL" ` // Special characters allowed?
Spaces Boolean ` xml:"SPACES" ` // Spaces allowed?
2017-04-03 19:55:58 -04:00
PinCh Boolean ` xml:"PINCH" ` // Pin change <PINCHRQ> requests allowed
2017-03-11 12:59:47 -05:00
ChgPinFirst Boolean ` xml:"CHGPINFIRST" ` // Server requires user to change password at first signon
UserCred1Label String ` xml:"USERCRED1LABEL,omitempty" ` // Prompt for USERCRED1 (if this field is present, USERCRED1 is required)
UserCred2Label String ` xml:"USERCRED2LABEL,omitempty" ` // Prompt for USERCRED2 (if this field is present, USERCRED2 is required)
ClientUIDReq Boolean ` xml:"CLIENTUIDREQ,omitempty" ` // CLIENTUID required?
AuthTokenFirst Boolean ` xml:"AUTHTOKENFIRST,omitempty" ` // Server requires AUTHTOKEN as part of first signon
AuthTokenLabel String ` xml:"AUTHTOKENLABEL,omitempty" `
AuthTokenInfoURL String ` xml:"AUTHTOKENINFOURL,omitempty" `
MFAChallengeSupt Boolean ` xml:"MFACHALLENGESUPT,omitempty" ` // Server supports MFACHALLENGE
MFAChallengeFIRST Boolean ` xml:"MFACHALLENGEFIRST,omitempty" ` // Server requires MFACHALLENGE to be sent with first signon
AccessTokenReq Boolean ` xml:"ACCESSTOKENREQ,omitempty" ` // Server requires ACCESSTOKEN to be sent with all requests except profile
}
2017-04-13 10:18:07 -04:00
// MessageSet represents one message set supported by an FI and its
// capabilities
2017-03-11 12:59:47 -05:00
type MessageSet struct {
XMLName xml . Name // <xxxMSGSETVn>
2017-04-03 19:56:16 -04:00
Name string // <xxxMSGSETVn> (copy of XMLName.Local)
2017-04-13 10:18:07 -04:00
Ver Int ` xml:"MSGSETCORE>VER" ` // Message set version - should always match 'n' in <xxxMSGSETVn> of Name
URL String ` xml:"MSGSETCORE>URL" ` // URL where messages in this set are to be set
2017-04-06 05:58:22 -04:00
OfxSec ofxSec ` xml:"MSGSETCORE>OFXSEC" ` // NONE or 'TYPE 1'
2017-04-01 21:29:27 -04:00
TranspSec Boolean ` xml:"MSGSETCORE>TRANSPSEC" ` // Transport-level security must be used
SignonRealm String ` xml:"MSGSETCORE>SIGNONREALM" ` // Used to identify which SignonInfo to use for to this MessageSet
Language [ ] String ` xml:"MSGSETCORE>LANGUAGE" ` // List of supported languages
2017-04-06 05:58:22 -04:00
SyncMode syncMode ` xml:"MSGSETCORE>SYNCMODE" ` // One of FULL, LITE
2017-04-01 21:29:27 -04:00
RefreshSupt Boolean ` xml:"MSGSETCORE>REFRESHSUPT,omitempty" ` // Y if server supports <REFRESH>Y within synchronizations. This option is irrelevant for full synchronization servers. Clients must ignore <REFRESHSUPT> (or its absence) if the profile also specifies <SYNCMODE>FULL. For lite synchronization, the default is N. Without <REFRESHSUPT>Y, lite synchronization servers are not required to support <REFRESH>Y requests
RespFileER Boolean ` xml:"MSGSETCORE>RESPFILEER" ` // server supports file-based error recovery
SpName String ` xml:"MSGSETCORE>SPNAME" ` // Name of service provider
2017-03-11 12:59:47 -05:00
// TODO MessageSet-specific stuff?
}
2017-04-13 10:18:07 -04:00
// MessageSetList is a list of MessageSets (necessary because they must be
// manually parsed)
2017-03-11 12:59:47 -05:00
type MessageSetList [ ] MessageSet
2017-04-13 10:18:07 -04:00
// UnmarshalXML handles unmarshalling a MessageSetList element from an XML string
2017-03-11 12:59:47 -05:00
func ( msl * MessageSetList ) UnmarshalXML ( d * xml . Decoder , start xml . StartElement ) error {
for {
var msgset MessageSet
2017-03-13 21:10:19 -04:00
tok , err := nextNonWhitespaceToken ( d )
2017-03-11 12:59:47 -05:00
if err != nil {
return err
} else if end , ok := tok . ( xml . EndElement ) ; ok && end . Name . Local == start . Name . Local {
// If we found the end of our starting element, we're done parsing
return nil
} else if _ , ok := tok . ( xml . StartElement ) ; ok {
// Found starting tag for <xxxMSGSET>. Get the next one (xxxMSGSETVn) and decode that struct
2017-03-13 21:10:19 -04:00
tok , err := nextNonWhitespaceToken ( d )
2017-03-11 12:59:47 -05:00
if err != nil {
return err
} else if versionStart , ok := tok . ( xml . StartElement ) ; ok {
if err := d . DecodeElement ( & msgset , & versionStart ) ; err != nil {
return err
}
} else {
return errors . New ( "Invalid MSGSETLIST formatting" )
}
2017-04-03 19:56:16 -04:00
msgset . Name = msgset . XMLName . Local
2017-03-11 12:59:47 -05:00
// Eat ending tags for <xxxMSGSET>
2017-03-13 21:10:19 -04:00
tok , err = nextNonWhitespaceToken ( d )
2017-03-11 12:59:47 -05:00
if err != nil {
return err
} else if _ , ok := tok . ( xml . EndElement ) ; ! ok {
return errors . New ( "Invalid MSGSETLIST formatting" )
}
} else {
return errors . New ( "MSGSETLIST didn't find an opening xxxMSGSETVn element" )
}
* msl = MessageSetList ( append ( * ( * [ ] MessageSet ) ( msl ) , msgset ) )
}
}
2017-04-13 10:18:07 -04:00
// ProfileResponse contains a requested profile of the server's capabilities
// (which message sets and versions it supports, how to access them, which
// languages and which types of synchronization they support, etc.). Note that
// if the server does not support ClientRouting=NONE (as we always send with
// ProfileRequest), this may be an error)
2017-03-11 12:59:47 -05:00
type ProfileResponse struct {
2017-03-30 10:24:26 -04:00
XMLName xml . Name ` xml:"PROFTRNRS" `
TrnUID UID ` xml:"TRNUID" `
Status Status ` xml:"STATUS" `
CltCookie String ` xml:"CLTCOOKIE,omitempty" `
// TODO `xml:"OFXEXTENSION,omitempty"`
2017-03-11 12:59:47 -05:00
MessageSetList MessageSetList ` xml:"PROFRS>MSGSETLIST" `
SignonInfoList [ ] SignonInfo ` xml:"PROFRS>SIGNONINFOLIST>SIGNONINFO" `
2017-03-11 21:13:06 -05:00
DtProfUp Date ` xml:"PROFRS>DTPROFUP" `
FiName String ` xml:"PROFRS>FINAME" `
2017-03-11 12:59:47 -05:00
Addr1 String ` xml:"PROFRS>ADDR1" `
Addr2 String ` xml:"PROFRS>ADDR2,omitempty" `
Addr3 String ` xml:"PROFRS>ADDR3,omitempty" `
City String ` xml:"PROFRS>CITY" `
State String ` xml:"PROFRS>STATE" `
PostalCode String ` xml:"PROFRS>POSTALCODE" `
Country String ` xml:"PROFRS>COUNTRY" `
CsPhone String ` xml:"PROFRS>CSPHONE,omitempty" `
TsPhone String ` xml:"PROFRS>TSPHONE,omitempty" `
FaxPhone String ` xml:"PROFRS>FAXPHONE,omitempty" `
URL String ` xml:"PROFRS>URL,omitempty" `
Email String ` xml:"PROFRS>EMAIL,omitempty" `
}
2017-04-13 10:18:07 -04:00
// Name returns the name of the top-level transaction XML/SGML element
2017-03-31 11:54:43 -04:00
func ( pr * ProfileResponse ) Name ( ) string {
2017-03-11 12:59:47 -05:00
return "PROFTRNRS"
}
2017-04-13 10:18:07 -04:00
// Valid returns (true, nil) if this struct was valid OFX when unmarshalled
2017-03-31 11:54:43 -04:00
func ( pr * ProfileResponse ) Valid ( ) ( bool , error ) {
2017-03-11 12:59:47 -05:00
//TODO implement
return true , nil
}
2017-04-13 10:18:07 -04:00
// Type returns which message set this message belongs to (which Response
// element of type []Message it belongs to)
2017-03-31 11:54:43 -04:00
func ( pr * ProfileResponse ) Type ( ) messageType {
2017-04-03 19:50:16 -04:00
return ProfRs
2017-03-31 09:25:07 -04:00
}