From b9bf07389c071e4e9721305882561ea49843ded1 Mon Sep 17 00:00:00 2001 From: Martin/Geno Date: Sun, 2 Jun 2019 21:39:21 +0200 Subject: [PATCH] [TASK] add support to receive image from threema --- component/threema/account.go | 8 +++-- component/threema/file.go | 29 +++++++++++++++++ component/threema/file_test.go | 26 +++++++++++++++ component/threema/main.go | 28 +++++++++++++--- component/threema/main_test.go | 28 +++++++++++++++- component/threema/receive.go | 53 ++++++++++++++++++++++++------- component/threema/receive_test.go | 52 ++++++++++++++++++++---------- config_example.toml | 4 +++ vendor/gosrc.io/xmpp | 2 +- 9 files changed, 194 insertions(+), 36 deletions(-) create mode 100644 component/threema/file.go create mode 100644 component/threema/file_test.go diff --git a/component/threema/account.go b/component/threema/account.go index a3ecfdf..3afc95a 100644 --- a/component/threema/account.go +++ b/component/threema/account.go @@ -12,6 +12,7 @@ import ( type Account struct { models.AccountThreema + threema *Threema Session o3.SessionContext send chan<- o3.Message receive <-chan o3.ReceivedMsg @@ -43,8 +44,11 @@ func (t *Threema) getAccount(jid *models.JID) (*Account, error) { } tid.Nick = o3.NewPubNick("xmpp:" + jid.String()) - a := &Account{AccountThreema: account} - a.Session = o3.NewSessionContext(tid) + a := &Account{ + AccountThreema: account, + Session: o3.NewSessionContext(tid), + threema: t, + } a.send, a.receive, err = a.Session.Run() if err != nil { diff --git a/component/threema/file.go b/component/threema/file.go new file mode 100644 index 0000000..a2ff6a7 --- /dev/null +++ b/component/threema/file.go @@ -0,0 +1,29 @@ +package threema + +import ( + "fmt" + "io/ioutil" + "strconv" + + "gosrc.io/xmpp" +) + +func (a *Account) FileToXMPP(from string, msgID uint64, ext string, data []byte) (xmpp.Message, error) { + msgIDStr := strconv.FormatUint(msgID, 10) + msg := xmpp.Message{ + PacketAttrs: xmpp.PacketAttrs{ + Id: msgIDStr, + From: from, + To: a.XMPP.String(), + }, + } + url := fmt.Sprintf("%s/%d.%s", a.threema.httpUploadURL, msgID, ext) + path := fmt.Sprintf("%s/%d.%s", a.threema.httpUploadPath, msgID, ext) + if err := ioutil.WriteFile(path, data, 0644); err != nil { + msg.Body = "unable to save file on transport to forward" + return msg, err + } + msg.Body = url + msg.X = &xmpp.MsgXOOB{URL: url} + return msg, nil +} diff --git a/component/threema/file_test.go b/component/threema/file_test.go new file mode 100644 index 0000000..c8851b4 --- /dev/null +++ b/component/threema/file_test.go @@ -0,0 +1,26 @@ +package threema + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestFileToXMPP(t *testing.T) { + assert := assert.New(t) + + a := createDummyAccount() + a.threema = &Threema{ + httpUploadURL: "a", + httpUploadPath: "/tmp", + } + + msg, err := a.FileToXMPP("", 1, "jpg", []byte("hallo")) + assert.NoError(err) + assert.Equal("a/1.jpg", msg.X.URL) + + a.threema.httpUploadPath = "/gibt/es/nicht" + msg, err = a.FileToXMPP("", 1, "jpg", []byte("hallo")) + assert.Error(err) + assert.Equal("unable to save file on transport to forward", msg.Body) +} diff --git a/component/threema/main.go b/component/threema/main.go index 3d72762..b310365 100644 --- a/component/threema/main.go +++ b/component/threema/main.go @@ -1,6 +1,7 @@ package threema import ( + "errors" "strings" "github.com/bdlm/log" @@ -14,17 +15,34 @@ import ( type Threema struct { component.Component - out chan xmpp.Packet - accountJID map[string]*Account - bot map[string]*Bot + out chan xmpp.Packet + accountJID map[string]*Account + bot map[string]*Bot + httpUploadPath string + httpUploadURL string } func NewThreema(config map[string]interface{}) (component.Component, error) { - return &Threema{ + t := &Threema{ out: make(chan xmpp.Packet), accountJID: make(map[string]*Account), bot: make(map[string]*Bot), - }, nil + } + if pathI, ok := config["http_upload_path"]; ok { + if path, ok := pathI.(string); ok { + t.httpUploadPath = path + } else { + return nil, errors.New("wrong format of http_upload_path") + } + } + if urlI, ok := config["http_upload_url"]; ok { + if url, ok := urlI.(string); ok { + t.httpUploadURL = url + } else { + return nil, errors.New("wrong format of http_upload_url") + } + } + return t, nil } func (t *Threema) Connect() (chan xmpp.Packet, error) { diff --git a/component/threema/main_test.go b/component/threema/main_test.go index 0773f6e..8db9d6a 100644 --- a/component/threema/main_test.go +++ b/component/threema/main_test.go @@ -13,7 +13,25 @@ import ( func TestThreema(t *testing.T) { assert := assert.New(t) - c, err := NewThreema(map[string]interface{}{}) + // failed + c, err := NewThreema(map[string]interface{}{ + "http_upload_path": 3, + }) + assert.Error(err) + assert.Nil(c) + + // failed + c, err = NewThreema(map[string]interface{}{ + "http_upload_url": 3, + }) + assert.Error(err) + assert.Nil(c) + + // --- + c, err = NewThreema(map[string]interface{}{ + "http_upload_url": "", + "http_upload_path": "", + }) assert.NoError(err) assert.NotNil(c) @@ -75,6 +93,14 @@ func TestSend(t *testing.T) { p = tr.send(xmpp.IQ{}) assert.Nil(p) + // chat with bot without sender + p = tr.send(xmpp.Message{ + PacketAttrs: xmpp.PacketAttrs{ + To: "example.org", + }, + }) + assert.Nil(p) + // chat with bot p = tr.send(xmpp.Message{ PacketAttrs: xmpp.PacketAttrs{ diff --git a/component/threema/receive.go b/component/threema/receive.go index 59492be..950d5ae 100644 --- a/component/threema/receive.go +++ b/component/threema/receive.go @@ -1,6 +1,8 @@ package threema import ( + "errors" + "fmt" "strconv" "github.com/bdlm/log" @@ -10,27 +12,55 @@ import ( func (a *Account) receiver(out chan<- xmpp.Packet) { for receivedMessage := range a.receive { - if p := a.receiving(receivedMessage); p != nil { + sender := receivedMessage.Msg.Sender().String() + if string(a.TID) == sender { + continue + } + if p, err := a.receiving(receivedMessage); err != nil { + xMSG := xmpp.NewMessage("chat", sender, a.XMPP.String(), "", "en") + xMSG.Body = fmt.Sprintf("error on decoding message: %s\n%v", err, receivedMessage.Msg.Serialize()) + out <- xMSG + } else if p != nil { out <- p } } } -func (a *Account) receiving(receivedMessage o3.ReceivedMsg) xmpp.Packet { + +func requestExtensions(xMSG *xmpp.Message) { + xMSG.Extensions = append(xMSG.Extensions, xmpp.ReceiptRequest{}) + xMSG.Extensions = append(xMSG.Extensions, xmpp.ChatMarkerMarkable{}) +} + +func (a *Account) receiving(receivedMessage o3.ReceivedMsg) (xmpp.Packet, error) { if receivedMessage.Err != nil { log.Warnf("Error Receiving Message: %s\n", receivedMessage.Err) - return nil + return nil, receivedMessage.Err } switch msg := receivedMessage.Msg.(type) { case o3.TextMessage: sender := msg.Sender().String() - if string(a.TID) == sender { - return nil - } xMSG := xmpp.NewMessage("chat", sender, a.XMPP.String(), strconv.FormatUint(msg.ID(), 10), "en") xMSG.Body = msg.Text() - xMSG.Extensions = append(xMSG.Extensions, xmpp.ReceiptRequest{}) - xMSG.Extensions = append(xMSG.Extensions, xmpp.ChatMarkerMarkable{}) - return xMSG + requestExtensions(&xMSG) + return xMSG, nil + + case o3.ImageMessage: + if a.threema.httpUploadPath == "" { + return nil, errors.New("no place to store files at transport configurated") + } + data, err := msg.GetImageData(a.Session) + if err != nil { + log.Warnf("unable to read data from message: %s", err) + return nil, err + } + xMSG, err := a.FileToXMPP(msg.Sender().String(), msg.ID(), "jpg", data) + if err != nil { + log.Warnf("unable to create data from message: %s", err) + return nil, err + } + xMSG.Type = "chat" + requestExtensions(&xMSG) + return xMSG, nil case o3.DeliveryReceiptMessage: msgID := msg.MsgID() @@ -55,8 +85,9 @@ func (a *Account) receiving(receivedMessage o3.ReceivedMsg) xmpp.Packet { } if len(xMSG.Extensions) > 0 { - return xMSG + return xMSG, nil } + return nil, nil } - return nil + return nil, errors.New("not known data format") } diff --git a/component/threema/receive_test.go b/component/threema/receive_test.go index 743e5e2..d8e438c 100644 --- a/component/threema/receive_test.go +++ b/component/threema/receive_test.go @@ -34,14 +34,16 @@ func TestReceive(t *testing.T) { a := createDummyAccount() // receiving/skip error - p := a.receiving(o3.ReceivedMsg{ + p, err := a.receiving(o3.ReceivedMsg{ Err: errors.New("dummy"), }) assert.Nil(p) + assert.Error(err) // nothing to receiving - p = a.receiving(o3.ReceivedMsg{}) + p, err = a.receiving(o3.ReceivedMsg{}) assert.Nil(p) + assert.Error(err) } func TestReceiveText(t *testing.T) { @@ -58,26 +60,40 @@ func TestReceiveText(t *testing.T) { } txtMsg, err := o3.NewTextMessage(&session, threemaID, "Oojoh0Ah") assert.NoError(err) - p := a.receiving(o3.ReceivedMsg{ + p, err := a.receiving(o3.ReceivedMsg{ Msg: txtMsg, }) + assert.NoError(err) xMSG, ok := p.(xmpp.Message) assert.True(ok) assert.Equal("Oojoh0Ah", xMSG.Body) +} - // receiving/skip text to own id - session = o3.SessionContext{ +func TestReceiveImage(t *testing.T) { + assert := assert.New(t) + + a := createDummyAccount() + a.threema = &Threema{} + + /* receiving image + session := o3.SessionContext{ ID: o3.ThreemaID{ - ID: threemaIDByte, + ID: o3.NewIDString("12345678"), Nick: o3.NewPubNick("user"), }, - } - txtMsg, err = o3.NewTextMessage(&session, threemaID, "Aesh8shu") - assert.NoError(err) - p = a.receiving(o3.ReceivedMsg{ - Msg: txtMsg, + }*/ + imgMsg := o3.ImageMessage{} + _, err := a.receiving(o3.ReceivedMsg{ + Msg: imgMsg, }) - assert.Nil(p) + assert.Error(err) + + a.threema.httpUploadPath = "/tmp" + imgMsg = o3.ImageMessage{} + _, err = a.receiving(o3.ReceivedMsg{ + Msg: imgMsg, + }) + assert.Error(err) } func TestReceiveDeliveryReceipt(t *testing.T) { @@ -98,34 +114,38 @@ func TestReceiveDeliveryReceipt(t *testing.T) { drm, err := o3.NewDeliveryReceiptMessage(&session, threemaID, msgID, o3.MSGDELIVERED) assert.NoError(err) - p := a.receiving(o3.ReceivedMsg{ + p, err := a.receiving(o3.ReceivedMsg{ Msg: drm, }) + assert.NoError(err) xMSG, ok := p.(xmpp.Message) assert.True(ok) rr := xMSG.Extensions[0].(xmpp.ReceiptReceived) assert.Equal("im4aeseeh1IbaQui", rr.Id) // receiving delivered -> not in cache - p = a.receiving(o3.ReceivedMsg{ + p, err = a.receiving(o3.ReceivedMsg{ Msg: drm, }) + assert.NoError(err) assert.Nil(p) // receiving readed drm, err = o3.NewDeliveryReceiptMessage(&session, threemaID, msgID, o3.MSGREAD) assert.NoError(err) - p = a.receiving(o3.ReceivedMsg{ + p, err = a.receiving(o3.ReceivedMsg{ Msg: drm, }) + assert.NoError(err) xMSG, ok = p.(xmpp.Message) assert.True(ok) cmdd := xMSG.Extensions[0].(xmpp.ChatMarkerDisplayed) assert.Equal("im4aeseeh1IbaQui", cmdd.Id) // receiving delivered -> not in cache - p = a.receiving(o3.ReceivedMsg{ + p, err = a.receiving(o3.ReceivedMsg{ Msg: drm, }) + assert.NoError(err) assert.Nil(p) } diff --git a/config_example.toml b/config_example.toml index 47a9461..4111fdb 100644 --- a/config_example.toml +++ b/config_example.toml @@ -6,6 +6,10 @@ host = "threema.chat.sum7.eu" connection = "localhost:5347" secret = "change_me" +[component.special] +http_upload_url = "https://example.org/upload/threema" +http_upload_path = "/var/www/http/upload/threema" + [database] type = "sqlite3" logging = false diff --git a/vendor/gosrc.io/xmpp b/vendor/gosrc.io/xmpp index 3ddad3f..d0b7b1f 160000 --- a/vendor/gosrc.io/xmpp +++ b/vendor/gosrc.io/xmpp @@ -1 +1 @@ -Subproject commit 3ddad3f7a28604fa8c4fd5035d5a37aa1d676c95 +Subproject commit d0b7b1f864f0fd4651f8a92b92a341314ef7aa5b