diff --git a/const.go b/const.go index 0e97420b100fe027047fb424a6d6f1dd9929fb38..7194bef591fca588c10cbfd74064699721630841 100644 --- a/const.go +++ b/const.go @@ -18,6 +18,7 @@ package federation // const ( + TIME_FORMAT = "2006-01-02T15:04:05Z" XMLNS = "https://joindiaspora.com/protocol" XMLNS_ME = "http://salmon-protocol.org/ns/magic-env" APPLICATION_XML = "application/xml" @@ -36,8 +37,4 @@ const ( // webfinger WebFingerOstatus = "http://ostatus.org/schema/1.0/subscribe" WebFingerHcard = "http://microformats.org/profile/hcard" - - // signature types - AuthorSignatureType = iota - ParentAuthorSignatureType ) diff --git a/encryption.go b/encryption.go index cb8c52ce4940eba8f3458830ea0232a0c0916e05..a4c0f9c4b7e33413f0d4232b56b36f2e382fcadd 100644 --- a/encryption.go +++ b/encryption.go @@ -78,6 +78,8 @@ func AuthorSignature(data interface{}, order string, privKey []byte) (string, er if tag == o { value := v.Field(i).Interface() switch v := value.(type) { + case Time: + text += v.Format(TIME_FORMAT) + ";" case string: text += v + ";" case bool: diff --git a/entities.go b/entities.go index b952c1c9352f8daefa1f2359da96096311d02a0a..24a76b6ab7df2c98f2749e897319ae77a1200631 100644 --- a/entities.go +++ b/entities.go @@ -20,6 +20,7 @@ package federation import ( "errors" "encoding/xml" + "time" ) type Message struct { @@ -49,6 +50,26 @@ type Entity struct { Data interface{} `xml:"-"` } +type Time struct { + time.Time +} + +func (t *Time) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + e.EncodeElement(t.Format(TIME_FORMAT), start) + return nil +} + +func (t *Time) UnmarshalXML(decoder *xml.Decoder, start xml.StartElement) error { + var value string + decoder.DecodeElement(&value, &start) + parse, err := time.Parse(TIME_FORMAT, value) + if err != nil { + return err + } + *t = Time{parse} + return nil +} + func (e *Entity) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { // NOTE since the encoder ignores the interface type // (see https://golang.org/src/encoding/xml/read.go#L377) @@ -69,14 +90,19 @@ func (e *Entity) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { (*e).Type = local (*e).Data = content case StatusMessage: - fallthrough - case Reshare: content := EntityStatusMessage{} if err := d.DecodeElement(&content, &start); err != nil { return err } (*e).Type = local (*e).Data = content + case Reshare: + content := EntityReshare{} + if err := d.DecodeElement(&content, &start); err != nil { + return err + } + (*e).Type = local + (*e).Data = content case Comment: content := EntityComment{} if err := d.DecodeElement(&content, &start); err != nil { diff --git a/entities_test.go b/entities_test.go index 6c6848120f517c7bb3c6093991d3367ee1f202c4..87f5cfd513039d8cb2d994c52e4b55c40f750760 100644 --- a/entities_test.go +++ b/entities_test.go @@ -28,7 +28,7 @@ func TestEntitiesUnmarshalXML(t *testing.T) { var retractionRaw = []byte(``) var profileRaw = []byte(``) var statusMessageRaw = []byte(``) - //var reshareRaw = []byte(``) + var reshareRaw = []byte(``) var commentRaw = []byte(``) var likeRaw = []byte(``) var contactRaw = []byte(``) @@ -41,6 +41,10 @@ func TestEntitiesUnmarshalXML(t *testing.T) { if data, ok := entity.Data.(EntityRetraction); !ok { t.Errorf("Expected to be 'like', got %v", data) } + err = xml.Unmarshal(retractionRaw[:len(retractionRaw)-1], &entity) + if err == nil { + t.Errorf("Expected an error, got nil") + } err = xml.Unmarshal(profileRaw, &entity) if err != nil { @@ -49,6 +53,10 @@ func TestEntitiesUnmarshalXML(t *testing.T) { if data, ok := entity.Data.(EntityProfile); !ok { t.Errorf("Expected to be 'profile', got %v", data) } + err = xml.Unmarshal(profileRaw[:len(profileRaw)-1], &entity) + if err == nil { + t.Errorf("Expected an error, got nil") + } err = xml.Unmarshal(statusMessageRaw, &entity) if err != nil { @@ -57,14 +65,22 @@ func TestEntitiesUnmarshalXML(t *testing.T) { if data, ok := entity.Data.(EntityStatusMessage); !ok { t.Errorf("Expected to be 'status_message', got %v", data) } + err = xml.Unmarshal(statusMessageRaw[:len(statusMessageRaw)-1], &entity) + if err == nil { + t.Errorf("Expected an error, got nil") + } - //err = xml.Unmarshal(reshareRaw, &entity) - //if err != nil { - // t.Errorf("Some error occured while parsing: %v", err) - //} - //if data, ok := entity.Data.(EntityStatusMessage); !ok { - // t.Errorf("Expected to be 'reshare', got %v", data) - //} + err = xml.Unmarshal(reshareRaw, &entity) + if err != nil { + t.Errorf("Some error occured while parsing: %v", err) + } + if data, ok := entity.Data.(EntityReshare); !ok { + t.Errorf("Expected to be 'reshare', got %v", data) + } + err = xml.Unmarshal(reshareRaw[:len(reshareRaw)-1], &entity) + if err == nil { + t.Errorf("Expected an error, got nil") + } err = xml.Unmarshal(commentRaw, &entity) if err != nil { @@ -73,6 +89,10 @@ func TestEntitiesUnmarshalXML(t *testing.T) { if data, ok := entity.Data.(EntityComment); !ok { t.Errorf("Expected to be 'comment', got %v", data) } + err = xml.Unmarshal(commentRaw[:len(commentRaw)-1], &entity) + if err == nil { + t.Errorf("Expected an error, got nil") + } err = xml.Unmarshal(likeRaw, &entity) if err != nil { @@ -81,6 +101,10 @@ func TestEntitiesUnmarshalXML(t *testing.T) { if data, ok := entity.Data.(EntityLike); !ok { t.Errorf("Expected to be 'like', got %v", data) } + err = xml.Unmarshal(likeRaw[:len(likeRaw)-1], &entity) + if err == nil { + t.Errorf("Expected an error, got nil") + } err = xml.Unmarshal(contactRaw, &entity) if err != nil { @@ -89,9 +113,46 @@ func TestEntitiesUnmarshalXML(t *testing.T) { if data, ok := entity.Data.(EntityContact); !ok { t.Errorf("Expected to be 'contact', got %v", data) } + err = xml.Unmarshal(contactRaw[:len(contactRaw)-1], &entity) + if err == nil { + t.Errorf("Expected an error, got nil") + } err = xml.Unmarshal(notSupportedRaw, &entity) if err == nil { t.Errorf("Expected an error, got nil") } } + +func TestEntitiesTimeMarshalAndUnmarshal(t *testing.T) { + // federation time format + // 2006-01-02T15:04:05Z + var time = "2018-01-19T01:32:23Z" + var rawXml = ""; + var origTime = struct { + XMLName xml.Name `xml:"time"` + CreatedAt Time + }{} + + err := xml.Unmarshal([]byte(rawXml), &origTime) + if err != nil { + t.Errorf("Some error occured while parsing: %v", err) + } + if origTime.CreatedAt.Time.Format(TIME_FORMAT) != time { + t.Errorf("Expected to be '%s', got '%s'", + origTime.CreatedAt.Time.Format(TIME_FORMAT)) + } + + result, err := xml.Marshal(origTime) + if err != nil { + t.Errorf("Some error occured while parsing: %v", err) + } + if string(result) != rawXml { + t.Errorf("Expected to be '%s', got '%s'", result, rawXml) + } + + err = xml.Unmarshal([]byte(""), &origTime) + if err == nil { + t.Errorf("Expected an error, got nil") + } +} diff --git a/entity_comment.go b/entity_comment.go index 3f9dae3c8fce58aa25d027058698f8447df9b7f4..f52556e33e6189886a537a6d1a9ec5453b99ef42 100644 --- a/entity_comment.go +++ b/entity_comment.go @@ -17,38 +17,28 @@ package federation // along with this program. If not, see . // -import ( - "encoding/xml" - "errors" -) +import "encoding/xml" type EntityComment struct { XMLName xml.Name `xml:"comment"` Author string `xml:"author"` - CreatedAt string `xml:"created_at"` + CreatedAt Time `xml:"created_at"` Guid string `xml:"guid"` ParentGuid string `xml:"parent_guid"` Text string `xml:"text"` AuthorSignature string `xml:"author_signature"` - ParentAuthorSignature string `xml:"parent_author_signature"` } func (e *EntityComment) SignatureOrder() string { return "author created_at guid parent_guid text" } -func (e *EntityComment) AppendSignature(privKey []byte, order string, typ int) error { +func (e *EntityComment) AppendSignature(privKey []byte, order string) error { signature, err := AuthorSignature(*e, order, privKey) if err != nil { return err } + (*e).AuthorSignature = signature - if AuthorSignatureType == typ { - (*e).AuthorSignature = signature - } else if ParentAuthorSignatureType == typ { - (*e).ParentAuthorSignature = signature - } else { - return errors.New("Unsupported author signature type!") - } return nil } diff --git a/entity_comment_test.go b/entity_comment_test.go index 81854280b6bcd69093f69f0ec993c0b5199014b7..a259b5bea725acb6b2eb9d6e01b80643ac7f327d 100644 --- a/entity_comment_test.go +++ b/entity_comment_test.go @@ -17,7 +17,10 @@ package federation // along with this program. If not, see . // -import "testing" +import ( + "testing" + "time" +) func TestCommentSignatureOrder(t *testing.T) { var comment EntityComment @@ -31,8 +34,8 @@ func TestCommentSignatureOrder(t *testing.T) { func TestCommentAppendSignature(t *testing.T) { comment := EntityComment{ Author: "author@localhost", - CreatedAt: "01.01.1970", Guid: "1234", + CreatedAt: Time{time.Now()}, ParentGuid: "4321", Text: "hello world", } @@ -41,12 +44,7 @@ func TestCommentAppendSignature(t *testing.T) { t.Errorf("Expected to be empty, got %s", comment.AuthorSignature) } - if comment.ParentAuthorSignature != "" { - t.Errorf("Expected to be empty, got %s", comment.AuthorSignature) - } - - err := comment.AppendSignature(TEST_PRIV_KEY, - comment.SignatureOrder(), AuthorSignatureType) + err := comment.AppendSignature(TEST_PRIV_KEY, comment.SignatureOrder()) if err != nil { t.Errorf("Some error occured while parsing: %v", err) } @@ -55,13 +53,8 @@ func TestCommentAppendSignature(t *testing.T) { t.Errorf("Expected signature, was empty") } - err = comment.AppendSignature(TEST_PRIV_KEY, - comment.SignatureOrder(), ParentAuthorSignatureType) + err = comment.AppendSignature(TEST_PRIV_KEY, comment.SignatureOrder()) if err != nil { t.Errorf("Some error occured while parsing: %v", err) } - - if comment.ParentAuthorSignature == "" { - t.Errorf("Expected signature, was empty") - } } diff --git a/entity_like.go b/entity_like.go index 3aa4fce3d113cfe86382568de779ad8215701bc1..d0b4fb724788d1f12a5962c6dc12854cc198cce3 100644 --- a/entity_like.go +++ b/entity_like.go @@ -17,38 +17,28 @@ package federation // along with this program. If not, see . // -import ( - "encoding/xml" - "errors" -) +import "encoding/xml" type EntityLike struct { XMLName xml.Name `xml:"like"` Positive bool `xml:"positive"` Guid string `xml:"guid"` ParentGuid string `xml:"parent_guid"` - TargetType string `xml:"parent_type"` + ParentType string `xml:"parent_type"` Author string `xml:"author"` AuthorSignature string `xml:"author_signature"` - ParentAuthorSignature string `xml:"parent_author_signature"` } func (e *EntityLike) SignatureOrder() string { - return "positive guid parent_guid target_type author" + return "positive guid parent_guid parent_type author" } -func (e *EntityLike) AppendSignature(privKey []byte, order string, typ int) error { +func (e *EntityLike) AppendSignature(privKey []byte, order string) error { signature, err := AuthorSignature(*e, order, privKey) if err != nil { return err } + (*e).AuthorSignature = signature - if AuthorSignatureType == typ { - (*e).AuthorSignature = signature - } else if ParentAuthorSignatureType == typ { - (*e).ParentAuthorSignature = signature - } else { - return errors.New("Unsupported author signature type!") - } return nil } diff --git a/entity_like_test.go b/entity_like_test.go index 3be04e53e2f55de4ab5bd0246256caa7728c1b65..5ba53a5d9740dfa9641d3090cdde29166f7bea8a 100644 --- a/entity_like_test.go +++ b/entity_like_test.go @@ -23,7 +23,7 @@ import "testing" func TestLikeSignatureOrder(t *testing.T) { var like EntityLike - expected := "positive guid parent_guid target_type author" + expected := "positive guid parent_guid parent_type author" if expected != like.SignatureOrder() { t.Errorf("Expected to be %s, got %s", expected, like.SignatureOrder()) } @@ -34,7 +34,7 @@ func TestLikeAppendSignature(t *testing.T) { Positive: true, Guid: "1234", ParentGuid: "4321", - TargetType: "Post", + ParentType: "Post", Author: "author@localhost", } @@ -42,12 +42,7 @@ func TestLikeAppendSignature(t *testing.T) { t.Errorf("Expected to be empty, got %s", like.AuthorSignature) } - if like.ParentAuthorSignature != "" { - t.Errorf("Expected to be empty, got %s", like.AuthorSignature) - } - - err := like.AppendSignature(TEST_PRIV_KEY, - like.SignatureOrder(), AuthorSignatureType) + err := like.AppendSignature(TEST_PRIV_KEY, like.SignatureOrder()) if err != nil { t.Errorf("Some error occured while parsing: %v", err) } @@ -56,13 +51,8 @@ func TestLikeAppendSignature(t *testing.T) { t.Errorf("Expected signature, was empty") } - err = like.AppendSignature(TEST_PRIV_KEY, - like.SignatureOrder(), ParentAuthorSignatureType) + err = like.AppendSignature(TEST_PRIV_KEY, like.SignatureOrder()) if err != nil { t.Errorf("Some error occured while parsing: %v", err) } - - if like.ParentAuthorSignature == "" { - t.Errorf("Expected signature, was empty") - } } diff --git a/entity_post.go b/entity_post.go index 6e83db2d24d61d44b9b5e86133e21eb89da733da..dc8ec03f6270fe600e1fb2777d0d9163f2b243b2 100644 --- a/entity_post.go +++ b/entity_post.go @@ -17,32 +17,36 @@ package federation // along with this program. If not, see . // -import ( - "time" - "encoding/xml" -) +import "encoding/xml" type EntityStatusMessage struct { XMLName xml.Name `xml:"status_message"` Author string `xml:"author"` Guid string `xml:"guid"` - CreatedAt time.Time `xml:"created_at"` + CreatedAt Time `xml:"created_at"` ProviderName string `xml:"provider_display_name"` Text string `xml:"text,omitempty"` Photo *EntityPhotos `xml:"photo,omitempty"` Location *EntityLocation `xml:"location,omitempty"` Poll *EntityPoll `xml:"poll,omitempty"` Public bool `xml:"public"` - // on reshare - RootHandle string `xml:"root_diaspora_id,omitempty"` - RootGuid string `xml:"root_guid,omitempty"` + Event *EntityEvent `xml:"event,omitempty"` +} + +type EntityReshare struct { + XMLName xml.Name `xml:"reshare"` + Author string `xml:"author"` + Guid string `xml:"guid"` + CreatedAt Time `xml:"created_at"` + RootAuthor string `xml:"root_author"` + RootGuid string `xml:"root_guid"` } type EntityPhoto struct { Guid string `xml:"guid"` Author string `xml:"author"` Public bool `xml:"public"` - CreatedAt time.Time `xml:"created_at"` + CreatedAt Time `xml:"created_at"` RemotePhotoPath string `xml:"remote_photo_path"` RemotePhotoName string `xml:"remote_photo_name"` Text string `xml:"text"` @@ -70,6 +74,14 @@ type EntityPollAnswer struct { Answer string `xml:"answer"` } -type PollParticipation struct { - PollAnswerGuid string `xml:"poll_answer_guid"` +type EntityEvent struct { + Author string `xml:"author"` + Guid string `xml:"guid"` + Summary string `xml:"summary"` + Start Time `xml:"start"` + End Time `xml:"end"` + AllDay bool `xml:"all_day"` + Timezone string `xml:"timezone"` + Description string `xml:"description"` + Location *EntityLocation `xml:"location,omitempty"` } diff --git a/http_client_test.go b/http_client_test.go index 8dca08510f4b0cf6993432fc5af6f3610a3a7a9b..eb3435a3aad29c19397513ecaec390ec320a67a1 100644 --- a/http_client_test.go +++ b/http_client_test.go @@ -27,9 +27,9 @@ import ( ) type Test struct { - XMLName xml.Name `xml:"AB";json:"-"` - A string `xml:"A";json:"A"` - B string `xml:"B";json:"B"` + XMLName xml.Name `xml:"AB" json:"-"` + A string `xml:"A" json:"A"` + B string `xml:"B" json:"B"` } func TestPushToPrivate(t *testing.T) {